@synchat/webchat 0.0.13 → 0.0.15
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/README.md +127 -54
- package/dist/index.d.mts +39 -4
- package/dist/index.d.ts +39 -4
- package/dist/index.js +260 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +256 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -7
package/README.md
CHANGED
|
@@ -1,77 +1,150 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @synchat/webchat
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Componente de chat para sites, integrado à plataforma **Synchat**. Exibe um botão flutuante que abre um painel com formulário (nome, e-mail, telefone, mensagem) ou mensagem de fora do horário de atendimento, conforme a configuração do canal no portal.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
- **Site Synchat:** [synchat.com.br](https://synchat.com.br)
|
|
6
|
+
- **Portal (cadastro e gestão):** [portal.synchat.com.br](https://portal.synchat.com.br)
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Como se cadastrar na plataforma
|
|
11
|
+
|
|
12
|
+
1. Acesse o **portal**: [https://portal.synchat.com.br](https://portal.synchat.com.br).
|
|
13
|
+
2. Clique em **Cadastre-se** (ou acesse [https://portal.synchat.com.br/auth/register](https://portal.synchat.com.br/auth/register)).
|
|
14
|
+
3. Preencha os dados da empresa e do usuário e finalize o cadastro.
|
|
15
|
+
4. Após o login, você gerencia **canais**, **atendentes** e **conversas** pelo menu do portal.
|
|
16
|
+
|
|
17
|
+
Para mais informações sobre a Synchat, acesse [synchat.com.br](https://synchat.com.br).
|
|
18
|
+
|
|
19
|
+
---
|
|
11
20
|
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
npm install
|
|
15
|
-
```
|
|
21
|
+
## Criar um canal WebChat e obter o secret
|
|
16
22
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
1. No **portal**, vá em **Canais** (ou **Configurações** → **Canais**).
|
|
24
|
+
2. Clique em **Novo canal** e escolha **WebChat**.
|
|
25
|
+
3. Preencha:
|
|
26
|
+
- **Nome** do canal (ex.: "Chat do site")
|
|
27
|
+
- **Descrição** (opcional)
|
|
28
|
+
- **Restringir por dias e horário** (opcional): ative para exibir mensagem de “fora do horário” quando configurado
|
|
29
|
+
- Horários de atendimento (dias úteis e fim de semana), se usar restrição
|
|
30
|
+
- **Quando inativo, enviar para a fila** (opcional)
|
|
31
|
+
4. Clique em **Criar canal WebChat**.
|
|
32
|
+
5. Após a criação, o portal exibe o **secret** do canal. **Copie e guarde** esse valor — ele será usado no componente no seu site.
|
|
21
33
|
|
|
22
|
-
|
|
23
|
-
- Faça login (`npm login`).
|
|
24
|
-
- Se usar scope público: `npm publish --access public`
|
|
25
|
-
- Sem scope: `npm publish`
|
|
34
|
+
O **secret** identifica o canal na API e no widget; não o compartilhe publicamente.
|
|
26
35
|
|
|
27
|
-
|
|
28
|
-
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Instalação
|
|
29
39
|
|
|
30
|
-
|
|
40
|
+
```bash
|
|
41
|
+
npm install @synchat/webchat
|
|
31
42
|
```
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
├─ tsconfig.json
|
|
38
|
-
├─ tsup.config.ts
|
|
39
|
-
├─ .gitignore
|
|
40
|
-
└─ .npmignore
|
|
43
|
+
|
|
44
|
+
O pacote usa **React** e **Material UI** como dependências do seu projeto. Se ainda não tiver:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install react react-dom @mui/material @mui/icons-material @emotion/react @emotion/styled
|
|
41
48
|
```
|
|
42
49
|
|
|
43
|
-
|
|
50
|
+
---
|
|
44
51
|
|
|
45
|
-
##
|
|
52
|
+
## Uso básico
|
|
46
53
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
Importe o componente e informe o **secret** (obtido no portal) e a **URL base da API** da Synchat:
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
import { WebChat } from "@synchat/webchat";
|
|
58
|
+
|
|
59
|
+
function App() {
|
|
60
|
+
return (
|
|
61
|
+
<WebChat
|
|
62
|
+
secret="SEU_SECRET_DO_PORTAL"
|
|
63
|
+
apiBaseUrl="https://api.synchat.com.br"
|
|
64
|
+
/>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
52
67
|
```
|
|
53
68
|
|
|
69
|
+
- **secret:** valor exibido ao criar o canal WebChat no portal.
|
|
70
|
+
- **apiBaseUrl:** URL base da API (ex.: `https://api.synchat.com.br` em produção).
|
|
71
|
+
|
|
72
|
+
O widget exibe um **botão flutuante** no canto inferior direito. Ao clicar:
|
|
73
|
+
|
|
74
|
+
- Se o canal estiver **ativo** e **dentro do horário** (ou sem restrição): mostra o formulário (nome, e-mail, telefone, mensagem) e o botão **Iniciar chat**.
|
|
75
|
+
- Se estiver **fora do horário** (com restrição configurada): mostra a mensagem de horário de atendimento.
|
|
76
|
+
- Se o canal estiver inativo ou o secret for inválido: mostra mensagem de indisponibilidade.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Início da conversa (WebSocket)
|
|
81
|
+
|
|
82
|
+
O **início da conversa** não é feito por API; será feito via **WebSocket**. Por isso, o componente espera que você implemente o callback **onStartChat** e, nele, conecte ao WebSocket da Synchat e envie os dados do visitante.
|
|
83
|
+
|
|
84
|
+
Exemplo de uso com **onStartChat**:
|
|
85
|
+
|
|
54
86
|
```tsx
|
|
55
|
-
|
|
56
|
-
|
|
87
|
+
<WebChat
|
|
88
|
+
secret="SEU_SECRET_DO_PORTAL"
|
|
89
|
+
apiBaseUrl="https://api.synchat.com.br"
|
|
90
|
+
onStartChat={async (data) => {
|
|
91
|
+
// Conectar ao WebSocket usando o secret e enviar
|
|
92
|
+
// name, email, phone, message conforme documentação da API/WebSocket
|
|
93
|
+
await minhaConexaoWebSocket.iniciarChat(data);
|
|
94
|
+
}}
|
|
95
|
+
/>
|
|
96
|
+
```
|
|
57
97
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
98
|
+
Se **onStartChat** não for passado, o widget exibe uma mensagem orientando a configurá-lo (conversa via WebSocket).
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Props
|
|
103
|
+
|
|
104
|
+
| Prop | Tipo | Obrigatório | Descrição |
|
|
105
|
+
|-----------------|----------|-------------|-----------|
|
|
106
|
+
| **secret** | `string` | Sim | Secret do canal WebChat (portal → Canais → criar WebChat → copiar secret). |
|
|
107
|
+
| **apiBaseUrl** | `string` | Sim | URL base da API (ex.: `https://api.synchat.com.br`). |
|
|
108
|
+
| **onStartChat** | `(data) => void \| Promise<void>` | Não | Callback ao clicar em "Iniciar chat". Recebe `{ name, email, phone, message }`. Implemente aqui a conexão WebSocket. |
|
|
109
|
+
| **outOfHoursMessage** | `string` | Não | Mensagem customizada quando estiver fora do horário. Se não informado, usa o texto padrão com o horário configurado no canal. |
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Variáveis de ambiente (sugestão)
|
|
114
|
+
|
|
115
|
+
Em produção, evite deixar o secret no código. Use variáveis de ambiente:
|
|
116
|
+
|
|
117
|
+
```env
|
|
118
|
+
VITE_WEBCHAT_SECRET=seu_secret_aqui
|
|
119
|
+
VITE_SYNCHAT_API_URL=https://api.synchat.com.br
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
<WebChat
|
|
124
|
+
secret={import.meta.env.VITE_WEBCHAT_SECRET ?? ""}
|
|
125
|
+
apiBaseUrl={import.meta.env.VITE_SYNCHAT_API_URL ?? "https://api.synchat.com.br"}
|
|
126
|
+
onStartChat={handleStartChat}
|
|
127
|
+
/>
|
|
61
128
|
```
|
|
62
129
|
|
|
63
|
-
|
|
64
|
-
- Atualize `version` no `package.json` (ex.: `0.1.1`) antes de publicar novamente.
|
|
65
|
-
- Ou use `npm version patch|minor|major`.
|
|
130
|
+
---
|
|
66
131
|
|
|
67
|
-
##
|
|
68
|
-
- `npm pack` gera um `.tgz` local. No app consumidor: `npm install ../caminho/minha-lib-0.1.0.tgz`.
|
|
132
|
+
## Resumo do fluxo
|
|
69
133
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
- Se for SSR/Next.js, prefira `exports` bem definidos (já incluso).
|
|
134
|
+
1. **Cadastro:** [portal.synchat.com.br](https://portal.synchat.com.br) → Cadastre-se.
|
|
135
|
+
2. **Canal WebChat:** Portal → Canais → Novo canal → WebChat → preencher → Criar → **copiar o secret**.
|
|
136
|
+
3. **No site:** `npm install @synchat/webchat` e usar `<WebChat secret="..." apiBaseUrl="..." />`.
|
|
137
|
+
4. **Conversa:** Implementar **onStartChat** para conectar ao WebSocket e enviar a primeira mensagem quando o recurso estiver disponível.
|
|
75
138
|
|
|
76
139
|
---
|
|
77
|
-
|
|
140
|
+
|
|
141
|
+
## Links
|
|
142
|
+
|
|
143
|
+
- **Site:** [synchat.com.br](https://synchat.com.br)
|
|
144
|
+
- **Portal (login/cadastro):** [portal.synchat.com.br](https://portal.synchat.com.br)
|
|
145
|
+
- **Cadastro:** [portal.synchat.com.br/auth/register](https://portal.synchat.com.br/auth/register)
|
|
146
|
+
- **Pacote no npm:** [npmjs.com/package/@synchat/webchat](https://www.npmjs.com/package/@synchat/webchat)
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
Feito com ❤️ por [Synchat](https://synchat.com.br)
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { FC } from 'react';
|
|
3
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React__default, { FC } from 'react';
|
|
4
3
|
|
|
5
4
|
type ChatButtonProps = {
|
|
6
5
|
children?: React.ReactNode;
|
|
@@ -17,6 +16,42 @@ interface IComponent<T = {}> extends FC<IDefaultProps & T> {
|
|
|
17
16
|
|
|
18
17
|
declare const ChatButton: IComponent<ChatButtonProps>;
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
interface WebChatSchedule {
|
|
20
|
+
weekDays?: string[];
|
|
21
|
+
weekdaysStart?: string;
|
|
22
|
+
weekdaysEnd?: string;
|
|
23
|
+
weekendStart?: string;
|
|
24
|
+
weekendEnd?: string;
|
|
25
|
+
}
|
|
26
|
+
interface WebChatConfig {
|
|
27
|
+
name: string;
|
|
28
|
+
status: string;
|
|
29
|
+
restrictBySchedule?: boolean;
|
|
30
|
+
schedule?: WebChatSchedule;
|
|
31
|
+
sendToQueueWhenInactive?: boolean;
|
|
32
|
+
}
|
|
33
|
+
interface WebChatProps {
|
|
34
|
+
/** Channel secret (from portal when creating webchat channel). */
|
|
35
|
+
secret: string;
|
|
36
|
+
/** Base URL of the Synchat API (e.g. https://api.synchat.com.br). */
|
|
37
|
+
apiBaseUrl: string;
|
|
38
|
+
/** Called when user clicks "Iniciar chat". The app should connect via WebSocket and send the message (conversation start is not via API). */
|
|
39
|
+
onStartChat?: (data: {
|
|
40
|
+
name: string;
|
|
41
|
+
email: string;
|
|
42
|
+
phone: string;
|
|
43
|
+
message: string;
|
|
44
|
+
}) => void | Promise<void>;
|
|
45
|
+
/** Optional: custom out-of-hours message. If not set, uses schedule from config. */
|
|
46
|
+
outOfHoursMessage?: string;
|
|
47
|
+
}
|
|
48
|
+
interface WebChatFormData {
|
|
49
|
+
name: string;
|
|
50
|
+
email: string;
|
|
51
|
+
phone: string;
|
|
52
|
+
message: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
declare const WebChat: React__default.FC<WebChatProps>;
|
|
21
56
|
|
|
22
|
-
export { ChatButton, type ChatButtonProps, WebChat };
|
|
57
|
+
export { ChatButton, type ChatButtonProps, WebChat, type WebChatConfig, type WebChatFormData, type WebChatProps };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { FC } from 'react';
|
|
3
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React__default, { FC } from 'react';
|
|
4
3
|
|
|
5
4
|
type ChatButtonProps = {
|
|
6
5
|
children?: React.ReactNode;
|
|
@@ -17,6 +16,42 @@ interface IComponent<T = {}> extends FC<IDefaultProps & T> {
|
|
|
17
16
|
|
|
18
17
|
declare const ChatButton: IComponent<ChatButtonProps>;
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
interface WebChatSchedule {
|
|
20
|
+
weekDays?: string[];
|
|
21
|
+
weekdaysStart?: string;
|
|
22
|
+
weekdaysEnd?: string;
|
|
23
|
+
weekendStart?: string;
|
|
24
|
+
weekendEnd?: string;
|
|
25
|
+
}
|
|
26
|
+
interface WebChatConfig {
|
|
27
|
+
name: string;
|
|
28
|
+
status: string;
|
|
29
|
+
restrictBySchedule?: boolean;
|
|
30
|
+
schedule?: WebChatSchedule;
|
|
31
|
+
sendToQueueWhenInactive?: boolean;
|
|
32
|
+
}
|
|
33
|
+
interface WebChatProps {
|
|
34
|
+
/** Channel secret (from portal when creating webchat channel). */
|
|
35
|
+
secret: string;
|
|
36
|
+
/** Base URL of the Synchat API (e.g. https://api.synchat.com.br). */
|
|
37
|
+
apiBaseUrl: string;
|
|
38
|
+
/** Called when user clicks "Iniciar chat". The app should connect via WebSocket and send the message (conversation start is not via API). */
|
|
39
|
+
onStartChat?: (data: {
|
|
40
|
+
name: string;
|
|
41
|
+
email: string;
|
|
42
|
+
phone: string;
|
|
43
|
+
message: string;
|
|
44
|
+
}) => void | Promise<void>;
|
|
45
|
+
/** Optional: custom out-of-hours message. If not set, uses schedule from config. */
|
|
46
|
+
outOfHoursMessage?: string;
|
|
47
|
+
}
|
|
48
|
+
interface WebChatFormData {
|
|
49
|
+
name: string;
|
|
50
|
+
email: string;
|
|
51
|
+
phone: string;
|
|
52
|
+
message: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
declare const WebChat: React__default.FC<WebChatProps>;
|
|
21
56
|
|
|
22
|
-
export { ChatButton, type ChatButtonProps, WebChat };
|
|
57
|
+
export { ChatButton, type ChatButtonProps, WebChat, type WebChatConfig, type WebChatFormData, type WebChatProps };
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
var material = require('@mui/material');
|
|
4
4
|
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
var react = require('react');
|
|
6
|
+
var CloseIcon = require('@mui/icons-material/Close');
|
|
7
|
+
var ChatIcon = require('@mui/icons-material/Chat');
|
|
8
|
+
var ScheduleIcon = require('@mui/icons-material/Schedule');
|
|
9
|
+
|
|
10
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
|
+
|
|
12
|
+
var CloseIcon__default = /*#__PURE__*/_interopDefault(CloseIcon);
|
|
13
|
+
var ChatIcon__default = /*#__PURE__*/_interopDefault(ChatIcon);
|
|
14
|
+
var ScheduleIcon__default = /*#__PURE__*/_interopDefault(ScheduleIcon);
|
|
5
15
|
|
|
6
16
|
// src/lib/components/button/styles.tsx
|
|
7
17
|
var AtomButton = material.styled(material.Button, {
|
|
@@ -31,14 +41,256 @@ var ChatButton = ({ children = "Chat", onClick, className, disabled }) => {
|
|
|
31
41
|
}
|
|
32
42
|
);
|
|
33
43
|
};
|
|
34
|
-
var
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
var DAY_MAP = {
|
|
45
|
+
0: "SUNDAY",
|
|
46
|
+
1: "MONDAY",
|
|
47
|
+
2: "TUESDAY",
|
|
48
|
+
3: "WEDNESDAY",
|
|
49
|
+
4: "THURSDAY",
|
|
50
|
+
5: "FRIDAY",
|
|
51
|
+
6: "SATURDAY"
|
|
52
|
+
};
|
|
53
|
+
function isWithinSchedule(config) {
|
|
54
|
+
if (!config.restrictBySchedule || !config.schedule) return true;
|
|
55
|
+
const now = /* @__PURE__ */ new Date();
|
|
56
|
+
const day = DAY_MAP[now.getDay()];
|
|
57
|
+
const time = String(now.getHours()).padStart(2, "0") + ":" + String(now.getMinutes()).padStart(2, "0");
|
|
58
|
+
const weekDays = config.schedule.weekDays ?? [
|
|
59
|
+
"MONDAY",
|
|
60
|
+
"TUESDAY",
|
|
61
|
+
"WEDNESDAY",
|
|
62
|
+
"THURSDAY",
|
|
63
|
+
"FRIDAY"
|
|
64
|
+
];
|
|
65
|
+
const isWeekday = weekDays.includes(day);
|
|
66
|
+
if (isWeekday) {
|
|
67
|
+
const start2 = config.schedule.weekdaysStart ?? "07:00";
|
|
68
|
+
const end2 = config.schedule.weekdaysEnd ?? "18:00";
|
|
69
|
+
return time >= start2 && time <= end2;
|
|
70
|
+
}
|
|
71
|
+
const start = config.schedule.weekendStart ?? "07:00";
|
|
72
|
+
const end = config.schedule.weekendEnd ?? "13:00";
|
|
73
|
+
return time >= start && time <= end;
|
|
74
|
+
}
|
|
75
|
+
function formatScheduleMessage(config) {
|
|
76
|
+
if (!config.schedule) {
|
|
77
|
+
return "Nosso hor\xE1rio de atendimento \xE9 de segunda \xE0 sexta-feira das 07:00h \xE0s 18:00h. S\xE1bados, domingos e feriados das 07:00h \xE0s 13:00h.";
|
|
78
|
+
}
|
|
79
|
+
const wdStart = config.schedule.weekdaysStart ?? "07:00";
|
|
80
|
+
const wdEnd = config.schedule.weekdaysEnd ?? "18:00";
|
|
81
|
+
const weStart = config.schedule.weekendStart ?? "07:00";
|
|
82
|
+
const weEnd = config.schedule.weekendEnd ?? "13:00";
|
|
83
|
+
return `Nosso hor\xE1rio de atendimento \xE9 de segunda \xE0 sexta-feira das ${wdStart}h \xE0s ${wdEnd}h. S\xE1bados, domingos e feriados das ${weStart}h \xE0s ${weEnd}h.`;
|
|
84
|
+
}
|
|
85
|
+
var WebChat = ({
|
|
86
|
+
secret,
|
|
87
|
+
apiBaseUrl,
|
|
88
|
+
onStartChat,
|
|
89
|
+
outOfHoursMessage
|
|
90
|
+
}) => {
|
|
91
|
+
const [config, setConfig] = react.useState(null);
|
|
92
|
+
const [loading, setLoading] = react.useState(true);
|
|
93
|
+
const [error, setError] = react.useState(null);
|
|
94
|
+
const [open, setOpen] = react.useState(false);
|
|
95
|
+
const [submitting, setSubmitting] = react.useState(false);
|
|
96
|
+
const [submitted, setSubmitted] = react.useState(false);
|
|
97
|
+
const [form, setForm] = react.useState({
|
|
98
|
+
name: "",
|
|
99
|
+
email: "",
|
|
100
|
+
phone: "",
|
|
101
|
+
message: ""
|
|
102
|
+
});
|
|
103
|
+
const fetchConfig = react.useCallback(async () => {
|
|
104
|
+
if (!secret || !apiBaseUrl) return;
|
|
105
|
+
setLoading(true);
|
|
106
|
+
setError(null);
|
|
107
|
+
try {
|
|
108
|
+
const base = apiBaseUrl.replace(/\/$/, "");
|
|
109
|
+
const res = await fetch(`${base}/channel/webchat/${encodeURIComponent(secret)}`);
|
|
110
|
+
if (!res.ok) {
|
|
111
|
+
setError("Canal n\xE3o encontrado.");
|
|
112
|
+
setConfig(null);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const data = await res.json();
|
|
116
|
+
setConfig(data);
|
|
117
|
+
} catch {
|
|
118
|
+
setError("N\xE3o foi poss\xEDvel carregar o canal.");
|
|
119
|
+
setConfig(null);
|
|
120
|
+
} finally {
|
|
121
|
+
setLoading(false);
|
|
122
|
+
}
|
|
123
|
+
}, [secret, apiBaseUrl]);
|
|
124
|
+
react.useEffect(() => {
|
|
125
|
+
fetchConfig();
|
|
126
|
+
}, [fetchConfig]);
|
|
127
|
+
const isActive = config && (config.status === "live" || config.status === "enable");
|
|
128
|
+
const outsideHours = config && config.restrictBySchedule && !isWithinSchedule(config);
|
|
129
|
+
const showOutOfHours = isActive && outsideHours;
|
|
130
|
+
const showForm = isActive && !outsideHours;
|
|
131
|
+
const handleSubmit = async (e) => {
|
|
132
|
+
e.preventDefault();
|
|
133
|
+
if (submitting || !form.name.trim()) return;
|
|
134
|
+
if (!onStartChat) {
|
|
135
|
+
setError("In\xEDcio de conversa ser\xE1 via WebSocket. Configure onStartChat no componente.");
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
setSubmitting(true);
|
|
139
|
+
try {
|
|
140
|
+
await onStartChat({
|
|
141
|
+
name: form.name.trim(),
|
|
142
|
+
email: form.email.trim(),
|
|
143
|
+
phone: form.phone.trim(),
|
|
144
|
+
message: form.message.trim()
|
|
145
|
+
});
|
|
146
|
+
setSubmitted(true);
|
|
147
|
+
} catch (err) {
|
|
148
|
+
setError(err instanceof Error ? err.message : "Erro ao enviar.");
|
|
149
|
+
} finally {
|
|
150
|
+
setSubmitting(false);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
if (!secret) return null;
|
|
154
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
155
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
156
|
+
material.IconButton,
|
|
157
|
+
{
|
|
158
|
+
onClick: () => setOpen((o) => !o),
|
|
159
|
+
sx: {
|
|
160
|
+
position: "fixed",
|
|
161
|
+
bottom: 20,
|
|
162
|
+
right: 20,
|
|
163
|
+
width: 56,
|
|
164
|
+
height: 56,
|
|
165
|
+
bgcolor: "#007bff",
|
|
166
|
+
color: "white",
|
|
167
|
+
"&:hover": { bgcolor: "#0056b3" },
|
|
168
|
+
boxShadow: 2
|
|
169
|
+
},
|
|
170
|
+
size: "large",
|
|
171
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ChatIcon__default.default, {})
|
|
172
|
+
}
|
|
173
|
+
),
|
|
174
|
+
open && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
175
|
+
material.Box,
|
|
176
|
+
{
|
|
177
|
+
sx: {
|
|
178
|
+
position: "fixed",
|
|
179
|
+
bottom: 90,
|
|
180
|
+
right: 20,
|
|
181
|
+
width: 380,
|
|
182
|
+
maxWidth: "calc(100vw - 40px)",
|
|
183
|
+
maxHeight: "calc(100vh - 120px)",
|
|
184
|
+
overflow: "auto",
|
|
185
|
+
bgcolor: "background.paper",
|
|
186
|
+
borderRadius: 2,
|
|
187
|
+
boxShadow: 6,
|
|
188
|
+
zIndex: 1300,
|
|
189
|
+
display: "flex",
|
|
190
|
+
flexDirection: "column"
|
|
191
|
+
},
|
|
192
|
+
children: [
|
|
193
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
194
|
+
material.Box,
|
|
195
|
+
{
|
|
196
|
+
sx: {
|
|
197
|
+
display: "flex",
|
|
198
|
+
alignItems: "center",
|
|
199
|
+
justifyContent: "space-between",
|
|
200
|
+
p: 1.5,
|
|
201
|
+
borderBottom: 1,
|
|
202
|
+
borderColor: "divider"
|
|
203
|
+
},
|
|
204
|
+
children: [
|
|
205
|
+
/* @__PURE__ */ jsxRuntime.jsx(material.Typography, { variant: "subtitle1", fontWeight: 600, children: config?.name ?? "Chat" }),
|
|
206
|
+
/* @__PURE__ */ jsxRuntime.jsx(material.IconButton, { size: "small", onClick: () => setOpen(false), children: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon__default.default, {}) })
|
|
207
|
+
]
|
|
208
|
+
}
|
|
209
|
+
),
|
|
210
|
+
/* @__PURE__ */ jsxRuntime.jsxs(material.Box, { sx: { p: 2, flex: 1 }, children: [
|
|
211
|
+
loading && /* @__PURE__ */ jsxRuntime.jsx(material.Box, { display: "flex", justifyContent: "center", py: 3, children: /* @__PURE__ */ jsxRuntime.jsx(material.CircularProgress, { size: 32 }) }),
|
|
212
|
+
error && !loading && /* @__PURE__ */ jsxRuntime.jsx(material.Typography, { color: "error", variant: "body2", children: error }),
|
|
213
|
+
!loading && config && !isActive && /* @__PURE__ */ jsxRuntime.jsx(material.Typography, { color: "text.secondary", variant: "body2", children: "Este canal n\xE3o est\xE1 dispon\xEDvel no momento." }),
|
|
214
|
+
showOutOfHours && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
215
|
+
material.Box,
|
|
216
|
+
{
|
|
217
|
+
sx: {
|
|
218
|
+
textAlign: "center",
|
|
219
|
+
py: 2,
|
|
220
|
+
px: 1,
|
|
221
|
+
bgcolor: "#1976d2",
|
|
222
|
+
borderRadius: 1,
|
|
223
|
+
color: "white"
|
|
224
|
+
},
|
|
225
|
+
children: [
|
|
226
|
+
/* @__PURE__ */ jsxRuntime.jsx(ScheduleIcon__default.default, { sx: { fontSize: 48, opacity: 0.9, mb: 1 } }),
|
|
227
|
+
/* @__PURE__ */ jsxRuntime.jsx(material.Typography, { variant: "body1", sx: { fontWeight: 500, mb: 1 }, children: "Fora do hor\xE1rio de atendimento" }),
|
|
228
|
+
/* @__PURE__ */ jsxRuntime.jsx(material.Typography, { variant: "body2", sx: { opacity: 0.95 }, children: outOfHoursMessage ?? formatScheduleMessage(config) })
|
|
229
|
+
]
|
|
230
|
+
}
|
|
231
|
+
),
|
|
232
|
+
showForm && !submitted && /* @__PURE__ */ jsxRuntime.jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxRuntime.jsxs(material.Box, { display: "flex", flexDirection: "column", gap: 2, children: [
|
|
233
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
234
|
+
material.TextField,
|
|
235
|
+
{
|
|
236
|
+
label: "Nome",
|
|
237
|
+
value: form.name,
|
|
238
|
+
onChange: (e) => setForm((f) => ({ ...f, name: e.target.value })),
|
|
239
|
+
required: true,
|
|
240
|
+
fullWidth: true,
|
|
241
|
+
size: "small"
|
|
242
|
+
}
|
|
243
|
+
),
|
|
244
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
245
|
+
material.TextField,
|
|
246
|
+
{
|
|
247
|
+
label: "E-mail",
|
|
248
|
+
type: "email",
|
|
249
|
+
value: form.email,
|
|
250
|
+
onChange: (e) => setForm((f) => ({ ...f, email: e.target.value })),
|
|
251
|
+
fullWidth: true,
|
|
252
|
+
size: "small"
|
|
253
|
+
}
|
|
254
|
+
),
|
|
255
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
256
|
+
material.TextField,
|
|
257
|
+
{
|
|
258
|
+
label: "Telefone",
|
|
259
|
+
value: form.phone,
|
|
260
|
+
onChange: (e) => setForm((f) => ({ ...f, phone: e.target.value })),
|
|
261
|
+
fullWidth: true,
|
|
262
|
+
size: "small"
|
|
263
|
+
}
|
|
264
|
+
),
|
|
265
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
266
|
+
material.TextField,
|
|
267
|
+
{
|
|
268
|
+
label: "Mensagem",
|
|
269
|
+
value: form.message,
|
|
270
|
+
onChange: (e) => setForm((f) => ({ ...f, message: e.target.value })),
|
|
271
|
+
multiline: true,
|
|
272
|
+
rows: 3,
|
|
273
|
+
fullWidth: true,
|
|
274
|
+
size: "small"
|
|
275
|
+
}
|
|
276
|
+
),
|
|
277
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
278
|
+
material.Button,
|
|
279
|
+
{
|
|
280
|
+
type: "submit",
|
|
281
|
+
variant: "contained",
|
|
282
|
+
disabled: submitting || !form.name.trim(),
|
|
283
|
+
fullWidth: true,
|
|
284
|
+
children: submitting ? "Enviando\u2026" : "Iniciar chat"
|
|
285
|
+
}
|
|
286
|
+
)
|
|
287
|
+
] }) }),
|
|
288
|
+
showForm && submitted && /* @__PURE__ */ jsxRuntime.jsx(material.Typography, { color: "text.secondary", variant: "body2", sx: { py: 2 }, children: "Sua mensagem foi enviada. Em breve um atendente responder\xE1." })
|
|
289
|
+
] })
|
|
290
|
+
]
|
|
291
|
+
}
|
|
292
|
+
)
|
|
293
|
+
] });
|
|
42
294
|
};
|
|
43
295
|
|
|
44
296
|
exports.ChatButton = ChatButton;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/components/button/styles.tsx","../src/lib/components/button/index.tsx","../src/lib/components/webchat/index.tsx"],"names":["styled","Button","jsx"],"mappings":";;;;;;AA8EO,IAAM,UAAA,GAAaA,gBAAOC,eAAA,EAAQ;AAAA,EACrC,iBAAA,EAAmB,CAAC,IAAA,KAAS,IAAA,KAAS;AAC1C,CAAC,CAAA,CAAmB,CAAC,EAAE,UAAA,GAAa,WAAU,KAAM;AAEhD,EAAA,OAAO,EACP;AAEJ,CAAC,CAAA;AChFM,IAAM,UAAA,GAA0C,CAAC,EAAE,QAAA,GAAW,QAAQ,OAAA,EAAS,SAAA,EAAW,UAAS,KAAM;AAC9G,EAAA,uBACEC,cAAA;AAAA,IAAC,UAAA;AAAA,IAAA;AAAA,MACC,OAAA;AAAA,MACA,QAAA;AAAA,MACA,SAAA;AAAA,MACA,KAAA,EAAO;AAAA,QACH,QAAA,EAAU,OAAA;AAAA,QACV,MAAA,EAAQ,MAAA;AAAA,QACR,KAAA,EAAO,MAAA;AAAA,QACP,eAAA,EAAiB,SAAA;AAAA,QACjB,KAAA,EAAO,MAAA;AAAA,QACP,OAAA,EAAS,MAAA;AAAA,QACT,YAAA,EAAc,KAAA;AAAA,QACd,KAAA,EAAO,MAAA;AAAA,QACP,MAAA,EAAQ;AAAA,OACZ;AAAA,MAEC;AAAA;AAAA,GACH;AAEJ;AC1BO,IAAM,UAAU,MAAM;AACzB,EAAA,uBACIA,cAAAA,CAAC,QAAA,EAAA,EAAO,KAAA,EAAO;AAAA,IACX,QAAA,EAAU,OAAA;AAAA,IACV,MAAA,EAAQ,MAAA;AAAA,IACR,KAAA,EAAO,MAAA;AAAA,IACP,eAAA,EAAiB,SAAA;AAAA,IACjB,OAAA,EAAS;AAAA,KACV,QAAA,EAAA,MAAA,EAEH,CAAA;AAER","file":"index.js","sourcesContent":["import { Button, ButtonProps, styled } from \"@mui/material\";\nimport { ButtonHTMLAttributes } from \"react\";\n\nconst disabledStyles = {\n backgroundColor: \"#E0E0E0\",\n color: \"#9E9E9E\",\n borderColor: \"#BDBDBD\",\n};\n\nconst buttonVariants = {\n primary: {\n background: \"#00BB7E\",\n color: \"#FFF\",\n border: \"none\",\n },\n secondary: {\n background: \"transparent\",\n color: \"#00BB7E\",\n border: \"#00BB7E\",\n },\n error: {\n background: \"transparent\",\n color: \"#D32F2F\",\n border: \"#D32F2F\",\n },\n danger: {\n background: \"#E61E2E\",\n color: \"#fff\",\n border: \"none\",\n },\n outlinedBlue: {\n background: \"transparent\",\n color: \"#2196F3\",\n border: \"#2196F3\",\n },\n outlinedGreen: {\n background: \"transparent\",\n color: \"#00BB7E\",\n border: \"#00BB7E\",\n },\n outlinedBlack: {\n background: \"transparent\",\n color: \"#000000DE\",\n border: \"#000000DE\",\n },\n outlinedGrey: {\n background: \"transparent\",\n color: \"#59676D\",\n border: \"#0B0D0E52\",\n },\n textPrimary: {\n background: \"transparent\",\n color: \"#00BB7E\",\n border: \"transparent\",\n },\n textSecondary: {\n background: \"transparent\",\n color: \"#000000DE\",\n border: \"transparent\",\n },\n textBlue: {\n background: \"transparent\",\n color: \"#2196F3\",\n border: \"transparent\",\n },\n default: {\n background: \"#00BB7E\",\n color: \"#FFF\",\n border: \"#00BB7E\",\n },\n};\n\ninterface AtomButtonProps\n extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, \"color\">,\n ButtonProps {\n buttonType?: keyof typeof buttonVariants;\n}\n\nexport const AtomButton = styled(Button, {\n shouldForwardProp: (prop) => prop !== \"buttonType\",\n})<AtomButtonProps>(({ buttonType = \"default\" }) => {\n\n return {\n }\n\n});","import * as React from 'react'\nimport { ChatButtonProps } from \"./types\";\nimport IComponent from \"../../../types/component\";\nimport { AtomButton } from \"./styles\";\n\nexport const ChatButton: IComponent<ChatButtonProps> = ({ children = 'Chat', onClick, className, disabled }) => {\n return (\n <AtomButton\n onClick={onClick}\n disabled={disabled}\n className={className}\n style={{\n position: \"fixed\",\n bottom: \"20px\",\n right: \"20px\",\n backgroundColor: \"#007bff\",\n color: \"#fff\",\n display: \"flex\",\n borderRadius: \"50%\",\n width: \"60px\",\n height: \"60px\"\n }}\n >\n {children}\n </AtomButton>\n )\n}\n","export const WebChat = () => {\n return (\n <button style={{\n position: \"fixed\",\n bottom: \"20px\",\n right: \"20px\",\n backgroundColor: \"#007bff\",\n display: \"flex\",\n }}>\n CHAT\n </button>\n )\n}"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/components/button/styles.tsx","../src/lib/components/button/index.tsx","../src/lib/components/webchat/index.tsx"],"names":["styled","Button","jsx","start","end","useState","useCallback","useEffect","jsxs","Fragment","IconButton","ChatIcon","Box","Typography","CloseIcon","CircularProgress","ScheduleIcon","TextField"],"mappings":"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","file":"index.js","sourcesContent":["import { Button, ButtonProps, styled } from \"@mui/material\";\nimport { ButtonHTMLAttributes } from \"react\";\n\nconst disabledStyles = {\n backgroundColor: \"#E0E0E0\",\n color: \"#9E9E9E\",\n borderColor: \"#BDBDBD\",\n};\n\nconst buttonVariants = {\n primary: {\n background: \"#00BB7E\",\n color: \"#FFF\",\n border: \"none\",\n },\n secondary: {\n background: \"transparent\",\n color: \"#00BB7E\",\n border: \"#00BB7E\",\n },\n error: {\n background: \"transparent\",\n color: \"#D32F2F\",\n border: \"#D32F2F\",\n },\n danger: {\n background: \"#E61E2E\",\n color: \"#fff\",\n border: \"none\",\n },\n outlinedBlue: {\n background: \"transparent\",\n color: \"#2196F3\",\n border: \"#2196F3\",\n },\n outlinedGreen: {\n background: \"transparent\",\n color: \"#00BB7E\",\n border: \"#00BB7E\",\n },\n outlinedBlack: {\n background: \"transparent\",\n color: \"#000000DE\",\n border: \"#000000DE\",\n },\n outlinedGrey: {\n background: \"transparent\",\n color: \"#59676D\",\n border: \"#0B0D0E52\",\n },\n textPrimary: {\n background: \"transparent\",\n color: \"#00BB7E\",\n border: \"transparent\",\n },\n textSecondary: {\n background: \"transparent\",\n color: \"#000000DE\",\n border: \"transparent\",\n },\n textBlue: {\n background: \"transparent\",\n color: \"#2196F3\",\n border: \"transparent\",\n },\n default: {\n background: \"#00BB7E\",\n color: \"#FFF\",\n border: \"#00BB7E\",\n },\n};\n\ninterface AtomButtonProps\n extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, \"color\">,\n ButtonProps {\n buttonType?: keyof typeof buttonVariants;\n}\n\nexport const AtomButton = styled(Button, {\n shouldForwardProp: (prop) => prop !== \"buttonType\",\n})<AtomButtonProps>(({ buttonType = \"default\" }) => {\n\n return {\n }\n\n});","import * as React from 'react'\nimport { ChatButtonProps } from \"./types\";\nimport IComponent from \"../../../types/component\";\nimport { AtomButton } from \"./styles\";\n\nexport const ChatButton: IComponent<ChatButtonProps> = ({ children = 'Chat', onClick, className, disabled }) => {\n return (\n <AtomButton\n onClick={onClick}\n disabled={disabled}\n className={className}\n style={{\n position: \"fixed\",\n bottom: \"20px\",\n right: \"20px\",\n backgroundColor: \"#007bff\",\n color: \"#fff\",\n display: \"flex\",\n borderRadius: \"50%\",\n width: \"60px\",\n height: \"60px\"\n }}\n >\n {children}\n </AtomButton>\n )\n}\n","import React, { useCallback, useEffect, useState } from \"react\";\nimport {\n Box,\n Button,\n CircularProgress,\n IconButton,\n TextField,\n Typography,\n} from \"@mui/material\";\nimport CloseIcon from \"@mui/icons-material/Close\";\nimport ChatIcon from \"@mui/icons-material/Chat\";\nimport ScheduleIcon from \"@mui/icons-material/Schedule\";\nimport { WebChatConfig, WebChatProps } from \"./types\";\n\nconst DAY_MAP: Record<number, string> = {\n 0: \"SUNDAY\",\n 1: \"MONDAY\",\n 2: \"TUESDAY\",\n 3: \"WEDNESDAY\",\n 4: \"THURSDAY\",\n 5: \"FRIDAY\",\n 6: \"SATURDAY\",\n};\n\nfunction isWithinSchedule(config: WebChatConfig): boolean {\n if (!config.restrictBySchedule || !config.schedule) return true;\n\n const now = new Date();\n const day = DAY_MAP[now.getDay()];\n const time =\n String(now.getHours()).padStart(2, \"0\") +\n \":\" +\n String(now.getMinutes()).padStart(2, \"0\");\n const weekDays = config.schedule.weekDays ?? [\n \"MONDAY\",\n \"TUESDAY\",\n \"WEDNESDAY\",\n \"THURSDAY\",\n \"FRIDAY\",\n ];\n const isWeekday = weekDays.includes(day);\n\n if (isWeekday) {\n const start = config.schedule.weekdaysStart ?? \"07:00\";\n const end = config.schedule.weekdaysEnd ?? \"18:00\";\n return time >= start && time <= end;\n }\n\n const start = config.schedule.weekendStart ?? \"07:00\";\n const end = config.schedule.weekendEnd ?? \"13:00\";\n return time >= start && time <= end;\n}\n\nfunction formatScheduleMessage(config: WebChatConfig): string {\n if (!config.schedule) {\n return \"Nosso horário de atendimento é de segunda à sexta-feira das 07:00h às 18:00h. Sábados, domingos e feriados das 07:00h às 13:00h.\";\n }\n const wdStart = config.schedule.weekdaysStart ?? \"07:00\";\n const wdEnd = config.schedule.weekdaysEnd ?? \"18:00\";\n const weStart = config.schedule.weekendStart ?? \"07:00\";\n const weEnd = config.schedule.weekendEnd ?? \"13:00\";\n return `Nosso horário de atendimento é de segunda à sexta-feira das ${wdStart}h às ${wdEnd}h. Sábados, domingos e feriados das ${weStart}h às ${weEnd}h.`;\n}\n\nexport const WebChat: React.FC<WebChatProps> = ({\n secret,\n apiBaseUrl,\n onStartChat,\n outOfHoursMessage,\n}) => {\n const [config, setConfig] = useState<WebChatConfig | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [open, setOpen] = useState(false);\n const [submitting, setSubmitting] = useState(false);\n const [submitted, setSubmitted] = useState(false);\n\n const [form, setForm] = useState({\n name: \"\",\n email: \"\",\n phone: \"\",\n message: \"\",\n });\n\n const fetchConfig = useCallback(async () => {\n if (!secret || !apiBaseUrl) return;\n setLoading(true);\n setError(null);\n try {\n const base = apiBaseUrl.replace(/\\/$/, \"\");\n const res = await fetch(`${base}/channel/webchat/${encodeURIComponent(secret)}`);\n if (!res.ok) {\n setError(\"Canal não encontrado.\");\n setConfig(null);\n return;\n }\n const data: WebChatConfig = await res.json();\n setConfig(data);\n } catch {\n setError(\"Não foi possível carregar o canal.\");\n setConfig(null);\n } finally {\n setLoading(false);\n }\n }, [secret, apiBaseUrl]);\n\n useEffect(() => {\n fetchConfig();\n }, [fetchConfig]);\n\n const isActive =\n config &&\n (config.status === \"live\" || config.status === \"enable\");\n const outsideHours =\n config && config.restrictBySchedule && !isWithinSchedule(config);\n const showOutOfHours = isActive && outsideHours;\n const showForm = isActive && !outsideHours;\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (submitting || !form.name.trim()) return;\n if (!onStartChat) {\n setError(\"Início de conversa será via WebSocket. Configure onStartChat no componente.\");\n return;\n }\n\n setSubmitting(true);\n try {\n await onStartChat({\n name: form.name.trim(),\n email: form.email.trim(),\n phone: form.phone.trim(),\n message: form.message.trim(),\n });\n setSubmitted(true);\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Erro ao enviar.\");\n } finally {\n setSubmitting(false);\n }\n };\n\n if (!secret) return null;\n\n return (\n <>\n <IconButton\n onClick={() => setOpen((o) => !o)}\n sx={{\n position: \"fixed\",\n bottom: 20,\n right: 20,\n width: 56,\n height: 56,\n bgcolor: \"#007bff\",\n color: \"white\",\n \"&:hover\": { bgcolor: \"#0056b3\" },\n boxShadow: 2,\n }}\n size=\"large\"\n >\n <ChatIcon />\n </IconButton>\n\n {open && (\n <Box\n sx={{\n position: \"fixed\",\n bottom: 90,\n right: 20,\n width: 380,\n maxWidth: \"calc(100vw - 40px)\",\n maxHeight: \"calc(100vh - 120px)\",\n overflow: \"auto\",\n bgcolor: \"background.paper\",\n borderRadius: 2,\n boxShadow: 6,\n zIndex: 1300,\n display: \"flex\",\n flexDirection: \"column\",\n }}\n >\n <Box\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n p: 1.5,\n borderBottom: 1,\n borderColor: \"divider\",\n }}\n >\n <Typography variant=\"subtitle1\" fontWeight={600}>\n {config?.name ?? \"Chat\"}\n </Typography>\n <IconButton size=\"small\" onClick={() => setOpen(false)}>\n <CloseIcon />\n </IconButton>\n </Box>\n\n <Box sx={{ p: 2, flex: 1 }}>\n {loading && (\n <Box display=\"flex\" justifyContent=\"center\" py={3}>\n <CircularProgress size={32} />\n </Box>\n )}\n\n {error && !loading && (\n <Typography color=\"error\" variant=\"body2\">\n {error}\n </Typography>\n )}\n\n {!loading && config && !isActive && (\n <Typography color=\"text.secondary\" variant=\"body2\">\n Este canal não está disponível no momento.\n </Typography>\n )}\n\n {showOutOfHours && (\n <Box\n sx={{\n textAlign: \"center\",\n py: 2,\n px: 1,\n bgcolor: \"#1976d2\",\n borderRadius: 1,\n color: \"white\",\n }}\n >\n <ScheduleIcon sx={{ fontSize: 48, opacity: 0.9, mb: 1 }} />\n <Typography variant=\"body1\" sx={{ fontWeight: 500, mb: 1 }}>\n Fora do horário de atendimento\n </Typography>\n <Typography variant=\"body2\" sx={{ opacity: 0.95 }}>\n {outOfHoursMessage ?? formatScheduleMessage(config)}\n </Typography>\n </Box>\n )}\n\n {showForm && !submitted && (\n <form onSubmit={handleSubmit}>\n <Box display=\"flex\" flexDirection=\"column\" gap={2}>\n <TextField\n label=\"Nome\"\n value={form.name}\n onChange={(e) => setForm((f) => ({ ...f, name: e.target.value }))}\n required\n fullWidth\n size=\"small\"\n />\n <TextField\n label=\"E-mail\"\n type=\"email\"\n value={form.email}\n onChange={(e) => setForm((f) => ({ ...f, email: e.target.value }))}\n fullWidth\n size=\"small\"\n />\n <TextField\n label=\"Telefone\"\n value={form.phone}\n onChange={(e) => setForm((f) => ({ ...f, phone: e.target.value }))}\n fullWidth\n size=\"small\"\n />\n <TextField\n label=\"Mensagem\"\n value={form.message}\n onChange={(e) => setForm((f) => ({ ...f, message: e.target.value }))}\n multiline\n rows={3}\n fullWidth\n size=\"small\"\n />\n <Button\n type=\"submit\"\n variant=\"contained\"\n disabled={submitting || !form.name.trim()}\n fullWidth\n >\n {submitting ? \"Enviando…\" : \"Iniciar chat\"}\n </Button>\n </Box>\n </form>\n )}\n\n {showForm && submitted && (\n <Typography color=\"text.secondary\" variant=\"body2\" sx={{ py: 2 }}>\n Sua mensagem foi enviada. Em breve um atendente responderá.\n </Typography>\n )}\n </Box>\n </Box>\n )}\n </>\n );\n};\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import { styled, Button } from '@mui/material';
|
|
2
|
-
import { jsx } from 'react/jsx-runtime';
|
|
1
|
+
import { styled, Button, IconButton, Box, Typography, CircularProgress, TextField } from '@mui/material';
|
|
2
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
|
+
import { useState, useCallback, useEffect } from 'react';
|
|
4
|
+
import CloseIcon from '@mui/icons-material/Close';
|
|
5
|
+
import ChatIcon from '@mui/icons-material/Chat';
|
|
6
|
+
import ScheduleIcon from '@mui/icons-material/Schedule';
|
|
3
7
|
|
|
4
8
|
// src/lib/components/button/styles.tsx
|
|
5
9
|
var AtomButton = styled(Button, {
|
|
@@ -29,14 +33,256 @@ var ChatButton = ({ children = "Chat", onClick, className, disabled }) => {
|
|
|
29
33
|
}
|
|
30
34
|
);
|
|
31
35
|
};
|
|
32
|
-
var
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
var DAY_MAP = {
|
|
37
|
+
0: "SUNDAY",
|
|
38
|
+
1: "MONDAY",
|
|
39
|
+
2: "TUESDAY",
|
|
40
|
+
3: "WEDNESDAY",
|
|
41
|
+
4: "THURSDAY",
|
|
42
|
+
5: "FRIDAY",
|
|
43
|
+
6: "SATURDAY"
|
|
44
|
+
};
|
|
45
|
+
function isWithinSchedule(config) {
|
|
46
|
+
if (!config.restrictBySchedule || !config.schedule) return true;
|
|
47
|
+
const now = /* @__PURE__ */ new Date();
|
|
48
|
+
const day = DAY_MAP[now.getDay()];
|
|
49
|
+
const time = String(now.getHours()).padStart(2, "0") + ":" + String(now.getMinutes()).padStart(2, "0");
|
|
50
|
+
const weekDays = config.schedule.weekDays ?? [
|
|
51
|
+
"MONDAY",
|
|
52
|
+
"TUESDAY",
|
|
53
|
+
"WEDNESDAY",
|
|
54
|
+
"THURSDAY",
|
|
55
|
+
"FRIDAY"
|
|
56
|
+
];
|
|
57
|
+
const isWeekday = weekDays.includes(day);
|
|
58
|
+
if (isWeekday) {
|
|
59
|
+
const start2 = config.schedule.weekdaysStart ?? "07:00";
|
|
60
|
+
const end2 = config.schedule.weekdaysEnd ?? "18:00";
|
|
61
|
+
return time >= start2 && time <= end2;
|
|
62
|
+
}
|
|
63
|
+
const start = config.schedule.weekendStart ?? "07:00";
|
|
64
|
+
const end = config.schedule.weekendEnd ?? "13:00";
|
|
65
|
+
return time >= start && time <= end;
|
|
66
|
+
}
|
|
67
|
+
function formatScheduleMessage(config) {
|
|
68
|
+
if (!config.schedule) {
|
|
69
|
+
return "Nosso hor\xE1rio de atendimento \xE9 de segunda \xE0 sexta-feira das 07:00h \xE0s 18:00h. S\xE1bados, domingos e feriados das 07:00h \xE0s 13:00h.";
|
|
70
|
+
}
|
|
71
|
+
const wdStart = config.schedule.weekdaysStart ?? "07:00";
|
|
72
|
+
const wdEnd = config.schedule.weekdaysEnd ?? "18:00";
|
|
73
|
+
const weStart = config.schedule.weekendStart ?? "07:00";
|
|
74
|
+
const weEnd = config.schedule.weekendEnd ?? "13:00";
|
|
75
|
+
return `Nosso hor\xE1rio de atendimento \xE9 de segunda \xE0 sexta-feira das ${wdStart}h \xE0s ${wdEnd}h. S\xE1bados, domingos e feriados das ${weStart}h \xE0s ${weEnd}h.`;
|
|
76
|
+
}
|
|
77
|
+
var WebChat = ({
|
|
78
|
+
secret,
|
|
79
|
+
apiBaseUrl,
|
|
80
|
+
onStartChat,
|
|
81
|
+
outOfHoursMessage
|
|
82
|
+
}) => {
|
|
83
|
+
const [config, setConfig] = useState(null);
|
|
84
|
+
const [loading, setLoading] = useState(true);
|
|
85
|
+
const [error, setError] = useState(null);
|
|
86
|
+
const [open, setOpen] = useState(false);
|
|
87
|
+
const [submitting, setSubmitting] = useState(false);
|
|
88
|
+
const [submitted, setSubmitted] = useState(false);
|
|
89
|
+
const [form, setForm] = useState({
|
|
90
|
+
name: "",
|
|
91
|
+
email: "",
|
|
92
|
+
phone: "",
|
|
93
|
+
message: ""
|
|
94
|
+
});
|
|
95
|
+
const fetchConfig = useCallback(async () => {
|
|
96
|
+
if (!secret || !apiBaseUrl) return;
|
|
97
|
+
setLoading(true);
|
|
98
|
+
setError(null);
|
|
99
|
+
try {
|
|
100
|
+
const base = apiBaseUrl.replace(/\/$/, "");
|
|
101
|
+
const res = await fetch(`${base}/channel/webchat/${encodeURIComponent(secret)}`);
|
|
102
|
+
if (!res.ok) {
|
|
103
|
+
setError("Canal n\xE3o encontrado.");
|
|
104
|
+
setConfig(null);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const data = await res.json();
|
|
108
|
+
setConfig(data);
|
|
109
|
+
} catch {
|
|
110
|
+
setError("N\xE3o foi poss\xEDvel carregar o canal.");
|
|
111
|
+
setConfig(null);
|
|
112
|
+
} finally {
|
|
113
|
+
setLoading(false);
|
|
114
|
+
}
|
|
115
|
+
}, [secret, apiBaseUrl]);
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
fetchConfig();
|
|
118
|
+
}, [fetchConfig]);
|
|
119
|
+
const isActive = config && (config.status === "live" || config.status === "enable");
|
|
120
|
+
const outsideHours = config && config.restrictBySchedule && !isWithinSchedule(config);
|
|
121
|
+
const showOutOfHours = isActive && outsideHours;
|
|
122
|
+
const showForm = isActive && !outsideHours;
|
|
123
|
+
const handleSubmit = async (e) => {
|
|
124
|
+
e.preventDefault();
|
|
125
|
+
if (submitting || !form.name.trim()) return;
|
|
126
|
+
if (!onStartChat) {
|
|
127
|
+
setError("In\xEDcio de conversa ser\xE1 via WebSocket. Configure onStartChat no componente.");
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
setSubmitting(true);
|
|
131
|
+
try {
|
|
132
|
+
await onStartChat({
|
|
133
|
+
name: form.name.trim(),
|
|
134
|
+
email: form.email.trim(),
|
|
135
|
+
phone: form.phone.trim(),
|
|
136
|
+
message: form.message.trim()
|
|
137
|
+
});
|
|
138
|
+
setSubmitted(true);
|
|
139
|
+
} catch (err) {
|
|
140
|
+
setError(err instanceof Error ? err.message : "Erro ao enviar.");
|
|
141
|
+
} finally {
|
|
142
|
+
setSubmitting(false);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
if (!secret) return null;
|
|
146
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
147
|
+
/* @__PURE__ */ jsx(
|
|
148
|
+
IconButton,
|
|
149
|
+
{
|
|
150
|
+
onClick: () => setOpen((o) => !o),
|
|
151
|
+
sx: {
|
|
152
|
+
position: "fixed",
|
|
153
|
+
bottom: 20,
|
|
154
|
+
right: 20,
|
|
155
|
+
width: 56,
|
|
156
|
+
height: 56,
|
|
157
|
+
bgcolor: "#007bff",
|
|
158
|
+
color: "white",
|
|
159
|
+
"&:hover": { bgcolor: "#0056b3" },
|
|
160
|
+
boxShadow: 2
|
|
161
|
+
},
|
|
162
|
+
size: "large",
|
|
163
|
+
children: /* @__PURE__ */ jsx(ChatIcon, {})
|
|
164
|
+
}
|
|
165
|
+
),
|
|
166
|
+
open && /* @__PURE__ */ jsxs(
|
|
167
|
+
Box,
|
|
168
|
+
{
|
|
169
|
+
sx: {
|
|
170
|
+
position: "fixed",
|
|
171
|
+
bottom: 90,
|
|
172
|
+
right: 20,
|
|
173
|
+
width: 380,
|
|
174
|
+
maxWidth: "calc(100vw - 40px)",
|
|
175
|
+
maxHeight: "calc(100vh - 120px)",
|
|
176
|
+
overflow: "auto",
|
|
177
|
+
bgcolor: "background.paper",
|
|
178
|
+
borderRadius: 2,
|
|
179
|
+
boxShadow: 6,
|
|
180
|
+
zIndex: 1300,
|
|
181
|
+
display: "flex",
|
|
182
|
+
flexDirection: "column"
|
|
183
|
+
},
|
|
184
|
+
children: [
|
|
185
|
+
/* @__PURE__ */ jsxs(
|
|
186
|
+
Box,
|
|
187
|
+
{
|
|
188
|
+
sx: {
|
|
189
|
+
display: "flex",
|
|
190
|
+
alignItems: "center",
|
|
191
|
+
justifyContent: "space-between",
|
|
192
|
+
p: 1.5,
|
|
193
|
+
borderBottom: 1,
|
|
194
|
+
borderColor: "divider"
|
|
195
|
+
},
|
|
196
|
+
children: [
|
|
197
|
+
/* @__PURE__ */ jsx(Typography, { variant: "subtitle1", fontWeight: 600, children: config?.name ?? "Chat" }),
|
|
198
|
+
/* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => setOpen(false), children: /* @__PURE__ */ jsx(CloseIcon, {}) })
|
|
199
|
+
]
|
|
200
|
+
}
|
|
201
|
+
),
|
|
202
|
+
/* @__PURE__ */ jsxs(Box, { sx: { p: 2, flex: 1 }, children: [
|
|
203
|
+
loading && /* @__PURE__ */ jsx(Box, { display: "flex", justifyContent: "center", py: 3, children: /* @__PURE__ */ jsx(CircularProgress, { size: 32 }) }),
|
|
204
|
+
error && !loading && /* @__PURE__ */ jsx(Typography, { color: "error", variant: "body2", children: error }),
|
|
205
|
+
!loading && config && !isActive && /* @__PURE__ */ jsx(Typography, { color: "text.secondary", variant: "body2", children: "Este canal n\xE3o est\xE1 dispon\xEDvel no momento." }),
|
|
206
|
+
showOutOfHours && /* @__PURE__ */ jsxs(
|
|
207
|
+
Box,
|
|
208
|
+
{
|
|
209
|
+
sx: {
|
|
210
|
+
textAlign: "center",
|
|
211
|
+
py: 2,
|
|
212
|
+
px: 1,
|
|
213
|
+
bgcolor: "#1976d2",
|
|
214
|
+
borderRadius: 1,
|
|
215
|
+
color: "white"
|
|
216
|
+
},
|
|
217
|
+
children: [
|
|
218
|
+
/* @__PURE__ */ jsx(ScheduleIcon, { sx: { fontSize: 48, opacity: 0.9, mb: 1 } }),
|
|
219
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body1", sx: { fontWeight: 500, mb: 1 }, children: "Fora do hor\xE1rio de atendimento" }),
|
|
220
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { opacity: 0.95 }, children: outOfHoursMessage ?? formatScheduleMessage(config) })
|
|
221
|
+
]
|
|
222
|
+
}
|
|
223
|
+
),
|
|
224
|
+
showForm && !submitted && /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs(Box, { display: "flex", flexDirection: "column", gap: 2, children: [
|
|
225
|
+
/* @__PURE__ */ jsx(
|
|
226
|
+
TextField,
|
|
227
|
+
{
|
|
228
|
+
label: "Nome",
|
|
229
|
+
value: form.name,
|
|
230
|
+
onChange: (e) => setForm((f) => ({ ...f, name: e.target.value })),
|
|
231
|
+
required: true,
|
|
232
|
+
fullWidth: true,
|
|
233
|
+
size: "small"
|
|
234
|
+
}
|
|
235
|
+
),
|
|
236
|
+
/* @__PURE__ */ jsx(
|
|
237
|
+
TextField,
|
|
238
|
+
{
|
|
239
|
+
label: "E-mail",
|
|
240
|
+
type: "email",
|
|
241
|
+
value: form.email,
|
|
242
|
+
onChange: (e) => setForm((f) => ({ ...f, email: e.target.value })),
|
|
243
|
+
fullWidth: true,
|
|
244
|
+
size: "small"
|
|
245
|
+
}
|
|
246
|
+
),
|
|
247
|
+
/* @__PURE__ */ jsx(
|
|
248
|
+
TextField,
|
|
249
|
+
{
|
|
250
|
+
label: "Telefone",
|
|
251
|
+
value: form.phone,
|
|
252
|
+
onChange: (e) => setForm((f) => ({ ...f, phone: e.target.value })),
|
|
253
|
+
fullWidth: true,
|
|
254
|
+
size: "small"
|
|
255
|
+
}
|
|
256
|
+
),
|
|
257
|
+
/* @__PURE__ */ jsx(
|
|
258
|
+
TextField,
|
|
259
|
+
{
|
|
260
|
+
label: "Mensagem",
|
|
261
|
+
value: form.message,
|
|
262
|
+
onChange: (e) => setForm((f) => ({ ...f, message: e.target.value })),
|
|
263
|
+
multiline: true,
|
|
264
|
+
rows: 3,
|
|
265
|
+
fullWidth: true,
|
|
266
|
+
size: "small"
|
|
267
|
+
}
|
|
268
|
+
),
|
|
269
|
+
/* @__PURE__ */ jsx(
|
|
270
|
+
Button,
|
|
271
|
+
{
|
|
272
|
+
type: "submit",
|
|
273
|
+
variant: "contained",
|
|
274
|
+
disabled: submitting || !form.name.trim(),
|
|
275
|
+
fullWidth: true,
|
|
276
|
+
children: submitting ? "Enviando\u2026" : "Iniciar chat"
|
|
277
|
+
}
|
|
278
|
+
)
|
|
279
|
+
] }) }),
|
|
280
|
+
showForm && submitted && /* @__PURE__ */ jsx(Typography, { color: "text.secondary", variant: "body2", sx: { py: 2 }, children: "Sua mensagem foi enviada. Em breve um atendente responder\xE1." })
|
|
281
|
+
] })
|
|
282
|
+
]
|
|
283
|
+
}
|
|
284
|
+
)
|
|
285
|
+
] });
|
|
40
286
|
};
|
|
41
287
|
|
|
42
288
|
export { ChatButton, WebChat };
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/components/button/styles.tsx","../src/lib/components/button/index.tsx","../src/lib/components/webchat/index.tsx"],"names":["jsx"],"mappings":";;;;AA8EO,IAAM,UAAA,GAAa,OAAO,MAAA,EAAQ;AAAA,EACrC,iBAAA,EAAmB,CAAC,IAAA,KAAS,IAAA,KAAS;AAC1C,CAAC,CAAA,CAAmB,CAAC,EAAE,UAAA,GAAa,WAAU,KAAM;AAEhD,EAAA,OAAO,EACP;AAEJ,CAAC,CAAA;AChFM,IAAM,UAAA,GAA0C,CAAC,EAAE,QAAA,GAAW,QAAQ,OAAA,EAAS,SAAA,EAAW,UAAS,KAAM;AAC9G,EAAA,uBACE,GAAA;AAAA,IAAC,UAAA;AAAA,IAAA;AAAA,MACC,OAAA;AAAA,MACA,QAAA;AAAA,MACA,SAAA;AAAA,MACA,KAAA,EAAO;AAAA,QACH,QAAA,EAAU,OAAA;AAAA,QACV,MAAA,EAAQ,MAAA;AAAA,QACR,KAAA,EAAO,MAAA;AAAA,QACP,eAAA,EAAiB,SAAA;AAAA,QACjB,KAAA,EAAO,MAAA;AAAA,QACP,OAAA,EAAS,MAAA;AAAA,QACT,YAAA,EAAc,KAAA;AAAA,QACd,KAAA,EAAO,MAAA;AAAA,QACP,MAAA,EAAQ;AAAA,OACZ;AAAA,MAEC;AAAA;AAAA,GACH;AAEJ;AC1BO,IAAM,UAAU,MAAM;AACzB,EAAA,uBACIA,GAAAA,CAAC,QAAA,EAAA,EAAO,KAAA,EAAO;AAAA,IACX,QAAA,EAAU,OAAA;AAAA,IACV,MAAA,EAAQ,MAAA;AAAA,IACR,KAAA,EAAO,MAAA;AAAA,IACP,eAAA,EAAiB,SAAA;AAAA,IACjB,OAAA,EAAS;AAAA,KACV,QAAA,EAAA,MAAA,EAEH,CAAA;AAER","file":"index.mjs","sourcesContent":["import { Button, ButtonProps, styled } from \"@mui/material\";\nimport { ButtonHTMLAttributes } from \"react\";\n\nconst disabledStyles = {\n backgroundColor: \"#E0E0E0\",\n color: \"#9E9E9E\",\n borderColor: \"#BDBDBD\",\n};\n\nconst buttonVariants = {\n primary: {\n background: \"#00BB7E\",\n color: \"#FFF\",\n border: \"none\",\n },\n secondary: {\n background: \"transparent\",\n color: \"#00BB7E\",\n border: \"#00BB7E\",\n },\n error: {\n background: \"transparent\",\n color: \"#D32F2F\",\n border: \"#D32F2F\",\n },\n danger: {\n background: \"#E61E2E\",\n color: \"#fff\",\n border: \"none\",\n },\n outlinedBlue: {\n background: \"transparent\",\n color: \"#2196F3\",\n border: \"#2196F3\",\n },\n outlinedGreen: {\n background: \"transparent\",\n color: \"#00BB7E\",\n border: \"#00BB7E\",\n },\n outlinedBlack: {\n background: \"transparent\",\n color: \"#000000DE\",\n border: \"#000000DE\",\n },\n outlinedGrey: {\n background: \"transparent\",\n color: \"#59676D\",\n border: \"#0B0D0E52\",\n },\n textPrimary: {\n background: \"transparent\",\n color: \"#00BB7E\",\n border: \"transparent\",\n },\n textSecondary: {\n background: \"transparent\",\n color: \"#000000DE\",\n border: \"transparent\",\n },\n textBlue: {\n background: \"transparent\",\n color: \"#2196F3\",\n border: \"transparent\",\n },\n default: {\n background: \"#00BB7E\",\n color: \"#FFF\",\n border: \"#00BB7E\",\n },\n};\n\ninterface AtomButtonProps\n extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, \"color\">,\n ButtonProps {\n buttonType?: keyof typeof buttonVariants;\n}\n\nexport const AtomButton = styled(Button, {\n shouldForwardProp: (prop) => prop !== \"buttonType\",\n})<AtomButtonProps>(({ buttonType = \"default\" }) => {\n\n return {\n }\n\n});","import * as React from 'react'\nimport { ChatButtonProps } from \"./types\";\nimport IComponent from \"../../../types/component\";\nimport { AtomButton } from \"./styles\";\n\nexport const ChatButton: IComponent<ChatButtonProps> = ({ children = 'Chat', onClick, className, disabled }) => {\n return (\n <AtomButton\n onClick={onClick}\n disabled={disabled}\n className={className}\n style={{\n position: \"fixed\",\n bottom: \"20px\",\n right: \"20px\",\n backgroundColor: \"#007bff\",\n color: \"#fff\",\n display: \"flex\",\n borderRadius: \"50%\",\n width: \"60px\",\n height: \"60px\"\n }}\n >\n {children}\n </AtomButton>\n )\n}\n","export const WebChat = () => {\n return (\n <button style={{\n position: \"fixed\",\n bottom: \"20px\",\n right: \"20px\",\n backgroundColor: \"#007bff\",\n display: \"flex\",\n }}>\n CHAT\n </button>\n )\n}"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/components/button/styles.tsx","../src/lib/components/button/index.tsx","../src/lib/components/webchat/index.tsx"],"names":["start","end","jsx","Button"],"mappings":"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","file":"index.mjs","sourcesContent":["import { Button, ButtonProps, styled } from \"@mui/material\";\nimport { ButtonHTMLAttributes } from \"react\";\n\nconst disabledStyles = {\n backgroundColor: \"#E0E0E0\",\n color: \"#9E9E9E\",\n borderColor: \"#BDBDBD\",\n};\n\nconst buttonVariants = {\n primary: {\n background: \"#00BB7E\",\n color: \"#FFF\",\n border: \"none\",\n },\n secondary: {\n background: \"transparent\",\n color: \"#00BB7E\",\n border: \"#00BB7E\",\n },\n error: {\n background: \"transparent\",\n color: \"#D32F2F\",\n border: \"#D32F2F\",\n },\n danger: {\n background: \"#E61E2E\",\n color: \"#fff\",\n border: \"none\",\n },\n outlinedBlue: {\n background: \"transparent\",\n color: \"#2196F3\",\n border: \"#2196F3\",\n },\n outlinedGreen: {\n background: \"transparent\",\n color: \"#00BB7E\",\n border: \"#00BB7E\",\n },\n outlinedBlack: {\n background: \"transparent\",\n color: \"#000000DE\",\n border: \"#000000DE\",\n },\n outlinedGrey: {\n background: \"transparent\",\n color: \"#59676D\",\n border: \"#0B0D0E52\",\n },\n textPrimary: {\n background: \"transparent\",\n color: \"#00BB7E\",\n border: \"transparent\",\n },\n textSecondary: {\n background: \"transparent\",\n color: \"#000000DE\",\n border: \"transparent\",\n },\n textBlue: {\n background: \"transparent\",\n color: \"#2196F3\",\n border: \"transparent\",\n },\n default: {\n background: \"#00BB7E\",\n color: \"#FFF\",\n border: \"#00BB7E\",\n },\n};\n\ninterface AtomButtonProps\n extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, \"color\">,\n ButtonProps {\n buttonType?: keyof typeof buttonVariants;\n}\n\nexport const AtomButton = styled(Button, {\n shouldForwardProp: (prop) => prop !== \"buttonType\",\n})<AtomButtonProps>(({ buttonType = \"default\" }) => {\n\n return {\n }\n\n});","import * as React from 'react'\nimport { ChatButtonProps } from \"./types\";\nimport IComponent from \"../../../types/component\";\nimport { AtomButton } from \"./styles\";\n\nexport const ChatButton: IComponent<ChatButtonProps> = ({ children = 'Chat', onClick, className, disabled }) => {\n return (\n <AtomButton\n onClick={onClick}\n disabled={disabled}\n className={className}\n style={{\n position: \"fixed\",\n bottom: \"20px\",\n right: \"20px\",\n backgroundColor: \"#007bff\",\n color: \"#fff\",\n display: \"flex\",\n borderRadius: \"50%\",\n width: \"60px\",\n height: \"60px\"\n }}\n >\n {children}\n </AtomButton>\n )\n}\n","import React, { useCallback, useEffect, useState } from \"react\";\nimport {\n Box,\n Button,\n CircularProgress,\n IconButton,\n TextField,\n Typography,\n} from \"@mui/material\";\nimport CloseIcon from \"@mui/icons-material/Close\";\nimport ChatIcon from \"@mui/icons-material/Chat\";\nimport ScheduleIcon from \"@mui/icons-material/Schedule\";\nimport { WebChatConfig, WebChatProps } from \"./types\";\n\nconst DAY_MAP: Record<number, string> = {\n 0: \"SUNDAY\",\n 1: \"MONDAY\",\n 2: \"TUESDAY\",\n 3: \"WEDNESDAY\",\n 4: \"THURSDAY\",\n 5: \"FRIDAY\",\n 6: \"SATURDAY\",\n};\n\nfunction isWithinSchedule(config: WebChatConfig): boolean {\n if (!config.restrictBySchedule || !config.schedule) return true;\n\n const now = new Date();\n const day = DAY_MAP[now.getDay()];\n const time =\n String(now.getHours()).padStart(2, \"0\") +\n \":\" +\n String(now.getMinutes()).padStart(2, \"0\");\n const weekDays = config.schedule.weekDays ?? [\n \"MONDAY\",\n \"TUESDAY\",\n \"WEDNESDAY\",\n \"THURSDAY\",\n \"FRIDAY\",\n ];\n const isWeekday = weekDays.includes(day);\n\n if (isWeekday) {\n const start = config.schedule.weekdaysStart ?? \"07:00\";\n const end = config.schedule.weekdaysEnd ?? \"18:00\";\n return time >= start && time <= end;\n }\n\n const start = config.schedule.weekendStart ?? \"07:00\";\n const end = config.schedule.weekendEnd ?? \"13:00\";\n return time >= start && time <= end;\n}\n\nfunction formatScheduleMessage(config: WebChatConfig): string {\n if (!config.schedule) {\n return \"Nosso horário de atendimento é de segunda à sexta-feira das 07:00h às 18:00h. Sábados, domingos e feriados das 07:00h às 13:00h.\";\n }\n const wdStart = config.schedule.weekdaysStart ?? \"07:00\";\n const wdEnd = config.schedule.weekdaysEnd ?? \"18:00\";\n const weStart = config.schedule.weekendStart ?? \"07:00\";\n const weEnd = config.schedule.weekendEnd ?? \"13:00\";\n return `Nosso horário de atendimento é de segunda à sexta-feira das ${wdStart}h às ${wdEnd}h. Sábados, domingos e feriados das ${weStart}h às ${weEnd}h.`;\n}\n\nexport const WebChat: React.FC<WebChatProps> = ({\n secret,\n apiBaseUrl,\n onStartChat,\n outOfHoursMessage,\n}) => {\n const [config, setConfig] = useState<WebChatConfig | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [open, setOpen] = useState(false);\n const [submitting, setSubmitting] = useState(false);\n const [submitted, setSubmitted] = useState(false);\n\n const [form, setForm] = useState({\n name: \"\",\n email: \"\",\n phone: \"\",\n message: \"\",\n });\n\n const fetchConfig = useCallback(async () => {\n if (!secret || !apiBaseUrl) return;\n setLoading(true);\n setError(null);\n try {\n const base = apiBaseUrl.replace(/\\/$/, \"\");\n const res = await fetch(`${base}/channel/webchat/${encodeURIComponent(secret)}`);\n if (!res.ok) {\n setError(\"Canal não encontrado.\");\n setConfig(null);\n return;\n }\n const data: WebChatConfig = await res.json();\n setConfig(data);\n } catch {\n setError(\"Não foi possível carregar o canal.\");\n setConfig(null);\n } finally {\n setLoading(false);\n }\n }, [secret, apiBaseUrl]);\n\n useEffect(() => {\n fetchConfig();\n }, [fetchConfig]);\n\n const isActive =\n config &&\n (config.status === \"live\" || config.status === \"enable\");\n const outsideHours =\n config && config.restrictBySchedule && !isWithinSchedule(config);\n const showOutOfHours = isActive && outsideHours;\n const showForm = isActive && !outsideHours;\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (submitting || !form.name.trim()) return;\n if (!onStartChat) {\n setError(\"Início de conversa será via WebSocket. Configure onStartChat no componente.\");\n return;\n }\n\n setSubmitting(true);\n try {\n await onStartChat({\n name: form.name.trim(),\n email: form.email.trim(),\n phone: form.phone.trim(),\n message: form.message.trim(),\n });\n setSubmitted(true);\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Erro ao enviar.\");\n } finally {\n setSubmitting(false);\n }\n };\n\n if (!secret) return null;\n\n return (\n <>\n <IconButton\n onClick={() => setOpen((o) => !o)}\n sx={{\n position: \"fixed\",\n bottom: 20,\n right: 20,\n width: 56,\n height: 56,\n bgcolor: \"#007bff\",\n color: \"white\",\n \"&:hover\": { bgcolor: \"#0056b3\" },\n boxShadow: 2,\n }}\n size=\"large\"\n >\n <ChatIcon />\n </IconButton>\n\n {open && (\n <Box\n sx={{\n position: \"fixed\",\n bottom: 90,\n right: 20,\n width: 380,\n maxWidth: \"calc(100vw - 40px)\",\n maxHeight: \"calc(100vh - 120px)\",\n overflow: \"auto\",\n bgcolor: \"background.paper\",\n borderRadius: 2,\n boxShadow: 6,\n zIndex: 1300,\n display: \"flex\",\n flexDirection: \"column\",\n }}\n >\n <Box\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n p: 1.5,\n borderBottom: 1,\n borderColor: \"divider\",\n }}\n >\n <Typography variant=\"subtitle1\" fontWeight={600}>\n {config?.name ?? \"Chat\"}\n </Typography>\n <IconButton size=\"small\" onClick={() => setOpen(false)}>\n <CloseIcon />\n </IconButton>\n </Box>\n\n <Box sx={{ p: 2, flex: 1 }}>\n {loading && (\n <Box display=\"flex\" justifyContent=\"center\" py={3}>\n <CircularProgress size={32} />\n </Box>\n )}\n\n {error && !loading && (\n <Typography color=\"error\" variant=\"body2\">\n {error}\n </Typography>\n )}\n\n {!loading && config && !isActive && (\n <Typography color=\"text.secondary\" variant=\"body2\">\n Este canal não está disponível no momento.\n </Typography>\n )}\n\n {showOutOfHours && (\n <Box\n sx={{\n textAlign: \"center\",\n py: 2,\n px: 1,\n bgcolor: \"#1976d2\",\n borderRadius: 1,\n color: \"white\",\n }}\n >\n <ScheduleIcon sx={{ fontSize: 48, opacity: 0.9, mb: 1 }} />\n <Typography variant=\"body1\" sx={{ fontWeight: 500, mb: 1 }}>\n Fora do horário de atendimento\n </Typography>\n <Typography variant=\"body2\" sx={{ opacity: 0.95 }}>\n {outOfHoursMessage ?? formatScheduleMessage(config)}\n </Typography>\n </Box>\n )}\n\n {showForm && !submitted && (\n <form onSubmit={handleSubmit}>\n <Box display=\"flex\" flexDirection=\"column\" gap={2}>\n <TextField\n label=\"Nome\"\n value={form.name}\n onChange={(e) => setForm((f) => ({ ...f, name: e.target.value }))}\n required\n fullWidth\n size=\"small\"\n />\n <TextField\n label=\"E-mail\"\n type=\"email\"\n value={form.email}\n onChange={(e) => setForm((f) => ({ ...f, email: e.target.value }))}\n fullWidth\n size=\"small\"\n />\n <TextField\n label=\"Telefone\"\n value={form.phone}\n onChange={(e) => setForm((f) => ({ ...f, phone: e.target.value }))}\n fullWidth\n size=\"small\"\n />\n <TextField\n label=\"Mensagem\"\n value={form.message}\n onChange={(e) => setForm((f) => ({ ...f, message: e.target.value }))}\n multiline\n rows={3}\n fullWidth\n size=\"small\"\n />\n <Button\n type=\"submit\"\n variant=\"contained\"\n disabled={submitting || !form.name.trim()}\n fullWidth\n >\n {submitting ? \"Enviando…\" : \"Iniciar chat\"}\n </Button>\n </Box>\n </form>\n )}\n\n {showForm && submitted && (\n <Typography color=\"text.secondary\" variant=\"body2\" sx={{ py: 2 }}>\n Sua mensagem foi enviada. Em breve um atendente responderá.\n </Typography>\n )}\n </Box>\n </Box>\n )}\n </>\n );\n};\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synchat/webchat",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"description": "Starter de biblioteca de componentes React + TypeScript com tsup.",
|
|
5
5
|
"author": "synchat <contato@synchat.com.br>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,19 +34,25 @@
|
|
|
34
34
|
"component-library",
|
|
35
35
|
"tsup"
|
|
36
36
|
],
|
|
37
|
-
"
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"@emotion/react": ">=11",
|
|
39
|
+
"@emotion/styled": ">=11",
|
|
40
|
+
"@mui/icons-material": ">=5",
|
|
41
|
+
"@mui/material": ">=5",
|
|
42
|
+
"react": ">=18",
|
|
43
|
+
"react-dom": ">=18"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
38
46
|
"@emotion/react": "^11.14.0",
|
|
39
47
|
"@emotion/styled": "^11.14.1",
|
|
40
48
|
"@mui/icons-material": "^7.3.5",
|
|
41
49
|
"@mui/material": "^7.3.5",
|
|
42
|
-
"react": "19.2.0",
|
|
43
|
-
"react-dom": "19.2.0"
|
|
44
|
-
},
|
|
45
|
-
"devDependencies": {
|
|
46
50
|
"@types/react": "^18.3.0",
|
|
47
51
|
"@types/react-dom": "^18.3.0",
|
|
52
|
+
"react": "19.2.0",
|
|
53
|
+
"react-dom": "19.2.0",
|
|
48
54
|
"rimraf": "^6.0.0",
|
|
49
55
|
"tsup": "^8.0.1",
|
|
50
56
|
"typescript": "^5.6.0"
|
|
51
57
|
}
|
|
52
|
-
}
|
|
58
|
+
}
|