@orbe-agro/client-core 5.6.243 → 5.6.245
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/@ecme/components/template/Footer.js +1 -1
- package/dist/@ecme/services/AiService.js +2 -1
- package/dist/@ecme/services/AiService.js.map +1 -1
- package/dist/@ecme/views/concepts/ai/Chat/components/ChatView.js +14 -2
- package/dist/@ecme/views/concepts/ai/Chat/components/ChatView.js.map +1 -1
- package/dist/@ecme/views/concepts/ai/Chat/components/DataRequestCard.js +99 -0
- package/dist/@ecme/views/concepts/ai/Chat/components/DataRequestCard.js.map +1 -0
- package/dist/@ecme/views/concepts/ai/Chat/components/toolPresentation.js +15 -1
- package/dist/@ecme/views/concepts/ai/Chat/components/toolPresentation.js.map +1 -1
- package/dist/@ecme/views/concepts/ai/Chat/hooks/useChatSend.js +21 -1
- package/dist/@ecme/views/concepts/ai/Chat/hooks/useChatSend.js.map +1 -1
- package/dist/@types/@ecme/services/AiService.d.ts +13 -4
- package/dist/@types/@ecme/services/AiService.d.ts.map +1 -1
- package/dist/@types/@ecme/views/concepts/ai/Chat/components/ChatView.d.ts.map +1 -1
- package/dist/@types/@ecme/views/concepts/ai/Chat/components/DataRequestCard.d.ts +20 -0
- package/dist/@types/@ecme/views/concepts/ai/Chat/components/DataRequestCard.d.ts.map +1 -0
- package/dist/@types/@ecme/views/concepts/ai/Chat/components/toolPresentation.d.ts +12 -0
- package/dist/@types/@ecme/views/concepts/ai/Chat/components/toolPresentation.d.ts.map +1 -1
- package/dist/@types/@ecme/views/concepts/ai/Chat/hooks/useChatSend.d.ts +1 -0
- package/dist/@types/@ecme/views/concepts/ai/Chat/hooks/useChatSend.d.ts.map +1 -1
- package/dist/@types/@ecme/views/concepts/ai/Chat/types.d.ts +24 -0
- package/dist/@types/@ecme/views/concepts/ai/Chat/types.d.ts.map +1 -1
- package/dist/@types/base/@types/models/racao/carga.d.ts +1 -0
- package/dist/@types/base/@types/models/racao/carga.d.ts.map +1 -1
- package/dist/@types/base/configs/endpoints.config/agendamento/agendamento.d.ts +0 -1
- package/dist/@types/base/configs/endpoints.config/agendamento/agendamento.d.ts.map +1 -1
- package/dist/@types/base/configs/endpoints.config/agendamento/config/contratoVenda.d.ts +0 -1
- package/dist/@types/base/configs/endpoints.config/agendamento/config/contratoVenda.d.ts.map +1 -1
- package/dist/@types/base/configs/endpoints.config/dm/config/municipio.d.ts +4 -0
- package/dist/@types/base/configs/endpoints.config/dm/config/municipio.d.ts.map +1 -1
- package/dist/@types/base/configs/endpoints.config/dm/dm.d.ts +4 -0
- package/dist/@types/base/configs/endpoints.config/dm/dm.d.ts.map +1 -1
- package/dist/@types/base/configs/endpoints.config/endpoints.navigation.d.ts +4 -1
- package/dist/@types/base/configs/endpoints.config/endpoints.navigation.d.ts.map +1 -1
- package/dist/@types/base/services/modules/dm/municipio/MunicipioService.d.ts +1 -0
- package/dist/@types/base/services/modules/dm/municipio/MunicipioService.d.ts.map +1 -1
- package/dist/base/@types/models/racao/carga.js.map +1 -1
- package/dist/base/configs/endpoints.config/agendamento/config/contratoVenda.js +0 -1
- package/dist/base/configs/endpoints.config/agendamento/config/contratoVenda.js.map +1 -1
- package/dist/base/configs/endpoints.config/dm/config/municipio.js +2 -1
- package/dist/base/configs/endpoints.config/dm/config/municipio.js.map +1 -1
- package/dist/base/index.js +2 -1
- package/dist/base/services/index.js +2 -1
- package/dist/base/services/modules/agendamento/ContratoVendaService.js +2 -2
- package/dist/base/services/modules/agendamento/ContratoVendaService.js.map +1 -1
- package/dist/base/services/modules/dm/index.js +2 -1
- package/dist/base/services/modules/dm/municipio/MunicipioService.js +7 -0
- package/dist/base/services/modules/dm/municipio/MunicipioService.js.map +1 -1
- package/dist/base/services/modules/index.js +2 -1
- package/lib/@ecme/services/AiService.ts +14 -4
- package/lib/@ecme/views/concepts/ai/Chat/components/ChatView.tsx +17 -3
- package/lib/@ecme/views/concepts/ai/Chat/components/DataRequestCard.tsx +144 -0
- package/lib/@ecme/views/concepts/ai/Chat/components/toolPresentation.ts +29 -0
- package/lib/@ecme/views/concepts/ai/Chat/hooks/useChatSend.ts +43 -0
- package/lib/@ecme/views/concepts/ai/Chat/types.ts +23 -0
- package/lib/base/@types/models/racao/carga.ts +1 -0
- package/lib/base/configs/endpoints.config/agendamento/config/contratoVenda.ts +0 -1
- package/lib/base/configs/endpoints.config/dm/config/municipio.ts +2 -1
- package/lib/base/services/modules/agendamento/ContratoVendaService.ts +2 -2
- package/lib/base/services/modules/dm/municipio/MunicipioService.tsx +7 -0
- package/package.json +1 -1
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import { TbForms } from 'react-icons/tb'
|
|
3
|
+
import Button from '@/components/ui/Button'
|
|
4
|
+
import Input from '@/components/ui/Input'
|
|
5
|
+
import Select from '@/components/ui/Select'
|
|
6
|
+
import { opcoesDoCampo, rotularCampo, tipoDoCampo } from './toolPresentation'
|
|
7
|
+
import type { PendingInput } from '../types'
|
|
8
|
+
|
|
9
|
+
type DataRequestCardProps = {
|
|
10
|
+
input: PendingInput
|
|
11
|
+
/** `null` = o usuario fechou sem preencher. */
|
|
12
|
+
onSubmit: (dados: Record<string, unknown> | null) => void
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Formulario dos campos que faltam para criar um registro no ERP.
|
|
17
|
+
*
|
|
18
|
+
* O agente fica PARADO enquanto este card estiver na tela — ele so volta a
|
|
19
|
+
* responder depois do envio. Por isso divide o visual do ApprovalCard: sao as
|
|
20
|
+
* duas paradas do fluxo, e o usuario deve reconhece-las como a mesma coisa.
|
|
21
|
+
*
|
|
22
|
+
* Os valores vao para o modelo, que monta a chamada final. Por isso o envio nao
|
|
23
|
+
* valida tipo: o `type=number` e o select sao conveniencia de digitacao, nao
|
|
24
|
+
* regra de negocio — quem valida de verdade e o backend do ERP.
|
|
25
|
+
*/
|
|
26
|
+
const DataRequestCard = ({ input, onSubmit }: DataRequestCardProps) => {
|
|
27
|
+
const [valores, setValores] = useState<Record<string, string>>({})
|
|
28
|
+
const [enviando, setEnviando] = useState<'enviar' | 'cancelar' | null>(null)
|
|
29
|
+
|
|
30
|
+
const preencher = (campo: string, valor: string) =>
|
|
31
|
+
setValores((atual) => ({ ...atual, [campo]: valor }))
|
|
32
|
+
|
|
33
|
+
// Exigir todos evita uma ida e volta: faltando um, o agente pediria de novo.
|
|
34
|
+
const completo = input.campos.every((campo) => valores[campo]?.trim())
|
|
35
|
+
|
|
36
|
+
const enviar = () => {
|
|
37
|
+
setEnviando('enviar')
|
|
38
|
+
// Numeros vao como number; o resto como texto. O modelo aceita os dois,
|
|
39
|
+
// mas o tipo certo evita que ele precise adivinhar.
|
|
40
|
+
const dados = Object.fromEntries(
|
|
41
|
+
input.campos.map((campo) => {
|
|
42
|
+
const bruto = valores[campo].trim()
|
|
43
|
+
const numero =
|
|
44
|
+
tipoDoCampo(campo) === 'numero' ? Number(bruto) : NaN
|
|
45
|
+
return [campo, Number.isNaN(numero) ? bruto : numero]
|
|
46
|
+
}),
|
|
47
|
+
)
|
|
48
|
+
onSubmit(dados)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const cancelar = () => {
|
|
52
|
+
setEnviando('cancelar')
|
|
53
|
+
onSubmit(null)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<div className="not-prose mt-1 overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-800">
|
|
58
|
+
<div className="flex items-center gap-2 border-b border-gray-200 px-4 py-2.5 dark:border-gray-700">
|
|
59
|
+
<TbForms size={16} className="text-gray-400 dark:text-gray-500" />
|
|
60
|
+
<span className="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">
|
|
61
|
+
Dados necessários
|
|
62
|
+
</span>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<div className="px-4 py-3.5">
|
|
66
|
+
<div className="mb-3 text-sm text-gray-500 dark:text-gray-400">
|
|
67
|
+
Preencha os campos abaixo para eu continuar.
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<div className="flex flex-col gap-3">
|
|
71
|
+
{input.campos.map((campo) => {
|
|
72
|
+
const rotulo = rotularCampo(campo)
|
|
73
|
+
const opcoes = opcoesDoCampo(campo)
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<label key={campo} className="flex flex-col gap-1">
|
|
77
|
+
<span className="text-sm text-gray-600 dark:text-gray-300">
|
|
78
|
+
{rotulo}
|
|
79
|
+
</span>
|
|
80
|
+
{opcoes.length ? (
|
|
81
|
+
<Select
|
|
82
|
+
options={opcoes}
|
|
83
|
+
value={opcoes.filter(
|
|
84
|
+
(opcao) =>
|
|
85
|
+
opcao.value === valores[campo],
|
|
86
|
+
)}
|
|
87
|
+
placeholder={`Selecione ${rotulo.toLowerCase()}`}
|
|
88
|
+
isDisabled={enviando !== null}
|
|
89
|
+
onChange={(opcao) =>
|
|
90
|
+
preencher(
|
|
91
|
+
campo,
|
|
92
|
+
(opcao as { value: string } | null)
|
|
93
|
+
?.value ?? '',
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
/>
|
|
97
|
+
) : (
|
|
98
|
+
<Input
|
|
99
|
+
type={
|
|
100
|
+
tipoDoCampo(campo) === 'numero'
|
|
101
|
+
? 'number'
|
|
102
|
+
: 'text'
|
|
103
|
+
}
|
|
104
|
+
value={valores[campo] ?? ''}
|
|
105
|
+
disabled={enviando !== null}
|
|
106
|
+
placeholder={`Informe ${rotulo.toLowerCase()}`}
|
|
107
|
+
onChange={(evento) =>
|
|
108
|
+
preencher(campo, evento.target.value)
|
|
109
|
+
}
|
|
110
|
+
/>
|
|
111
|
+
)}
|
|
112
|
+
</label>
|
|
113
|
+
)
|
|
114
|
+
})}
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
<div className="mt-4 flex items-center gap-2">
|
|
118
|
+
<Button
|
|
119
|
+
variant="solid"
|
|
120
|
+
size="sm"
|
|
121
|
+
loading={enviando === 'enviar'}
|
|
122
|
+
disabled={!completo || enviando !== null}
|
|
123
|
+
onClick={enviar}
|
|
124
|
+
>
|
|
125
|
+
Enviar
|
|
126
|
+
</Button>
|
|
127
|
+
<Button
|
|
128
|
+
size="sm"
|
|
129
|
+
loading={enviando === 'cancelar'}
|
|
130
|
+
disabled={enviando !== null}
|
|
131
|
+
onClick={cancelar}
|
|
132
|
+
>
|
|
133
|
+
Cancelar
|
|
134
|
+
</Button>
|
|
135
|
+
<span className="ml-auto text-xs text-gray-400 dark:text-gray-500">
|
|
136
|
+
Você ainda confirma antes de gravar
|
|
137
|
+
</span>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export default DataRequestCard
|
|
@@ -84,6 +84,35 @@ const REFERENCIAS = new Set([
|
|
|
84
84
|
|
|
85
85
|
const ORDENACAO: Record<string, string> = { ASC: 'Crescente', DESC: 'Decrescente' }
|
|
86
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Opcoes dos campos que sao enum no backend.
|
|
89
|
+
*
|
|
90
|
+
* O rotulo sai do mesmo `humanizar` usado na exibicao, entao o formulario e o
|
|
91
|
+
* card de confirmacao mostram exatamente o mesmo texto.
|
|
92
|
+
*/
|
|
93
|
+
const OPCOES: Record<string, string[]> = {
|
|
94
|
+
tipo_contrato: ['MERCADO_INTERNO', 'MERCADO_EXTERNO'],
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export const opcoesDoCampo = (chave: string) =>
|
|
98
|
+
(OPCOES[chave] ?? []).map((valor) => ({
|
|
99
|
+
value: valor,
|
|
100
|
+
label: humanizar(valor),
|
|
101
|
+
}))
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Que tipo de controle o campo pede no formulario de coleta.
|
|
105
|
+
*
|
|
106
|
+
* E so UX (teclado numerico, lista em vez de texto livre): os valores voltam
|
|
107
|
+
* para o modelo, e e ele quem monta a chamada final — um tipo errado nao
|
|
108
|
+
* quebra, so piora o preenchimento.
|
|
109
|
+
*/
|
|
110
|
+
export const tipoDoCampo = (chave: string): 'numero' | 'enum' | 'texto' => {
|
|
111
|
+
if (OPCOES[chave]) return 'enum'
|
|
112
|
+
if (REFERENCIAS.has(chave) || chave === 'saldo_negociado') return 'numero'
|
|
113
|
+
return 'texto'
|
|
114
|
+
}
|
|
115
|
+
|
|
87
116
|
/** Converte o valor tecnico no que o usuario espera ler. */
|
|
88
117
|
export const formatarValor = (chave: string, valor: unknown): string => {
|
|
89
118
|
if (valor === null || valor === undefined || valor === '') {
|
|
@@ -137,6 +137,15 @@ const useChatSend = () => {
|
|
|
137
137
|
break
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
case 'input_required':
|
|
141
|
+
// Diferente da aprovação, aqui nenhuma ferramenta foi
|
|
142
|
+
// pedida ao ERP: o agente só percebeu que faltam campos.
|
|
143
|
+
// Por isso não há chip de ferramenta para ajustar.
|
|
144
|
+
updateConversation(chatId, messageId, {
|
|
145
|
+
pendingInput: evento.data,
|
|
146
|
+
})
|
|
147
|
+
break
|
|
148
|
+
|
|
140
149
|
case 'done':
|
|
141
150
|
// O done traz a resposta final completa e é a fonte
|
|
142
151
|
// autoritativa — se algum token se perdeu, ele corrige.
|
|
@@ -235,9 +244,43 @@ const useChatSend = () => {
|
|
|
235
244
|
)
|
|
236
245
|
}
|
|
237
246
|
|
|
247
|
+
/**
|
|
248
|
+
* Envia os dados que faltavam (ou o cancelamento) e retoma a conversa.
|
|
249
|
+
*
|
|
250
|
+
* Mesma mecânica do `handleApproval`: continua na MESMA mensagem, então o
|
|
251
|
+
* acumulador parte do que já está lá.
|
|
252
|
+
*/
|
|
253
|
+
const handleDataSubmit = async (
|
|
254
|
+
chatId: string,
|
|
255
|
+
messageId: string,
|
|
256
|
+
dados: Record<string, unknown> | null,
|
|
257
|
+
) => {
|
|
258
|
+
const mensagem = usGenerativeChatStore
|
|
259
|
+
.getState()
|
|
260
|
+
.chatHistory.find((chat) => chat.id === chatId)
|
|
261
|
+
?.conversation?.find((item) => item.id === messageId)
|
|
262
|
+
|
|
263
|
+
if (!mensagem?.pendingInput) {
|
|
264
|
+
return
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
setIsTyping(true)
|
|
268
|
+
// Some com o formulário antes da chamada: dois envios mandariam duas
|
|
269
|
+
// retomadas, e a segunda falha (o interrupt já foi consumido).
|
|
270
|
+
updateConversation(chatId, messageId, { pendingInput: undefined })
|
|
271
|
+
|
|
272
|
+
await consumirStream(
|
|
273
|
+
chatId,
|
|
274
|
+
messageId,
|
|
275
|
+
(onEvent) => resumeChat({ dados, conversationId: chatId, onEvent }),
|
|
276
|
+
{ texto: mensagem.content ?? '', tools: mensagem.tools ?? [] },
|
|
277
|
+
)
|
|
278
|
+
}
|
|
279
|
+
|
|
238
280
|
return {
|
|
239
281
|
handleSend,
|
|
240
282
|
handleApproval,
|
|
283
|
+
handleDataSubmit,
|
|
241
284
|
}
|
|
242
285
|
}
|
|
243
286
|
|
|
@@ -24,6 +24,17 @@ export type PendingApproval = {
|
|
|
24
24
|
calls: { id: string; name: string; args: Record<string, unknown> }[]
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Criacao parada por falta de dados, esperando o usuario preencher.
|
|
29
|
+
*
|
|
30
|
+
* `campos` traz os nomes TECNICOS dos parametros que faltam (ex.:
|
|
31
|
+
* `produto_id`); quem exibe traduz com `rotularCampo`.
|
|
32
|
+
*/
|
|
33
|
+
export type PendingInput = {
|
|
34
|
+
id: string
|
|
35
|
+
campos: string[]
|
|
36
|
+
}
|
|
37
|
+
|
|
27
38
|
export type Conversation = {
|
|
28
39
|
id: string
|
|
29
40
|
sender: {
|
|
@@ -53,6 +64,8 @@ export type Conversation = {
|
|
|
53
64
|
tools?: ToolCall[]
|
|
54
65
|
/** Preenchido quando o agente pausou esperando aprovacao humana. */
|
|
55
66
|
pendingApproval?: PendingApproval
|
|
67
|
+
/** Preenchido quando o agente pausou pedindo os dados que faltam. */
|
|
68
|
+
pendingInput?: PendingInput
|
|
56
69
|
}
|
|
57
70
|
|
|
58
71
|
/**
|
|
@@ -97,6 +110,16 @@ export type ChatEvent =
|
|
|
97
110
|
}[]
|
|
98
111
|
}
|
|
99
112
|
}
|
|
113
|
+
| {
|
|
114
|
+
/**
|
|
115
|
+
* Faltam campos para criar: o agente parou esperando o formulario.
|
|
116
|
+
*
|
|
117
|
+
* Como no `approval_required`, o `done` NAO vem — o turno esta
|
|
118
|
+
* pausado. Retomar e chamar `resumeChat({ dados })`.
|
|
119
|
+
*/
|
|
120
|
+
type: 'input_required'
|
|
121
|
+
data: { id: string; campos: string[] }
|
|
122
|
+
}
|
|
100
123
|
| { type: 'done'; data: { content: string } }
|
|
101
124
|
| { type: 'error'; data: { message: string } }
|
|
102
125
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const contratoVenda = {
|
|
2
2
|
findAll: '/agendamento/api/contrato-venda',
|
|
3
3
|
findOne: '/agendamento/api/contrato-venda',
|
|
4
|
-
findContratos: '/agendamento/api/vendas/contrato/find',
|
|
5
4
|
comSaldoByCliente: '/agendamento/api/contrato-venda/com-saldo/cliente',
|
|
6
5
|
relatorioCotas: '/agendamento/api/contrato-venda/find-relatorio-cotas',
|
|
7
6
|
periodo: '/agendamento/api/contrato-venda/periodo',
|
|
@@ -5,7 +5,8 @@ const municipio = {
|
|
|
5
5
|
findMunicipioByCodigo: { endpoint: `${MUNICIPIO_BASE_URL}/find/`, method: 'post' },
|
|
6
6
|
findMonitor: { endpoint: `${MUNICIPIO_BASE_URL}/find-monitor`, httpMethod: 'post' },
|
|
7
7
|
findAllUfs: { endpoint: `${MUNICIPIO_BASE_URL}/find-all-ufs`, httpMethod: 'get' },
|
|
8
|
-
findMunicipioByUf: { endpoint: `${MUNICIPIO_BASE_URL}/find-municipio-by-uf/`, httpMethod: 'get' }
|
|
8
|
+
findMunicipioByUf: { endpoint: `${MUNICIPIO_BASE_URL}/find-municipio-by-uf/`, httpMethod: 'get' },
|
|
9
|
+
findById : { endpoint: `${MUNICIPIO_BASE_URL}`, httpMethod: 'get' }
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
export default municipio
|
|
@@ -36,7 +36,7 @@ export async function apiFindContratosComSaldoPorCliente(clienteId: string | num
|
|
|
36
36
|
|
|
37
37
|
export async function apiFindAllContratos(params: Record<string, unknown>) {
|
|
38
38
|
return ApiService.fetchDataWithAxios<TQueryResponse<TContratoVenda>>({
|
|
39
|
-
url: endpointsConfig.agendamento.contratoVenda.
|
|
39
|
+
url: endpointsConfig.agendamento.contratoVenda.findAll,
|
|
40
40
|
method: 'post',
|
|
41
41
|
data: params,
|
|
42
42
|
})
|
|
@@ -44,7 +44,7 @@ export async function apiFindAllContratos(params: Record<string, unknown>) {
|
|
|
44
44
|
|
|
45
45
|
export async function apiFindACDVendasContrato(contratoNumero: string) {
|
|
46
46
|
return ApiService.fetchDataWithAxios<TQueryResponse<TContratoVenda>>({
|
|
47
|
-
url: endpointsConfig.agendamento.contratoVenda.
|
|
47
|
+
url: endpointsConfig.agendamento.contratoVenda.findAll,
|
|
48
48
|
method: 'post',
|
|
49
49
|
data: {
|
|
50
50
|
pageNumber: 1,
|
|
@@ -41,3 +41,10 @@ export async function apiFindMunicipioByUf(uf: string) {
|
|
|
41
41
|
method: MUNICIPIO_ENDPOINT.findMunicipioByUf.httpMethod,
|
|
42
42
|
})
|
|
43
43
|
}
|
|
44
|
+
|
|
45
|
+
export async function apiFindMunicipioById(id: string) {
|
|
46
|
+
return ApiService.fetchDataWithAxios<TMunicipio>({
|
|
47
|
+
url: MUNICIPIO_ENDPOINT.findById.endpoint + `/${id}`,
|
|
48
|
+
method: MUNICIPIO_ENDPOINT.findById.httpMethod,
|
|
49
|
+
})
|
|
50
|
+
}
|
package/package.json
CHANGED