@fayz-ai/plugin-conversations 0.23.14 → 0.23.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/dist/AtendeAIPage-PBBMWIDW.js +92 -0
- package/dist/AtendeAIPage-PBBMWIDW.js.map +1 -0
- package/dist/ConversationsPage-4V4VLM4D.js +47 -0
- package/dist/ConversationsPage-4V4VLM4D.js.map +1 -0
- package/dist/ConversationsSettings-7UQYM26R.js +158 -0
- package/dist/ConversationsSettings-7UQYM26R.js.map +1 -0
- package/dist/chunk-3FZX3ZQ3.js +318 -0
- package/dist/chunk-3FZX3ZQ3.js.map +1 -0
- package/dist/chunk-HMWUU5O7.js +75 -0
- package/dist/chunk-HMWUU5O7.js.map +1 -0
- package/dist/chunk-LJKLHGFX.js +1268 -0
- package/dist/chunk-LJKLHGFX.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +79 -4103
- package/dist/index.js.map +1 -1
- package/dist/migrations/main.browser.js +23 -0
- package/dist/migrations/main.browser.js.map +1 -0
- package/dist/migrations/main.js +2141 -0
- package/dist/migrations/main.js.map +1 -0
- package/dist/views/ConversationsPage.d.ts.map +1 -1
- package/package.json +10 -4
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Bot, Sparkles, Check, Settings2 } from 'lucide-react';
|
|
2
|
+
import { usePluginRuntimeOptional, tl, navigateApp, formatCurrency } from '@fayz-ai/core';
|
|
3
|
+
import { PRICE_BOOK } from '@fayz-ai/admin';
|
|
4
|
+
import { Card, Badge, Button, ComingSoon } from '@fayz-ai/ui';
|
|
5
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
// src/views/AtendeAIPage.tsx
|
|
8
|
+
var money = (n) => formatCurrency(n, "BRL");
|
|
9
|
+
var PROMISES = [
|
|
10
|
+
["Answers your clients on WhatsApp, day and night", "Responde seus clientes no WhatsApp, de dia e de noite"],
|
|
11
|
+
["Books, confirms and takes orders by itself", "Agenda, confirma e anota pedidos sozinho"],
|
|
12
|
+
["Hands over to a person when it should", "Passa para uma pessoa quando precisa"],
|
|
13
|
+
["Learns your prices, hours and services", "Aprende seus pre\xE7os, hor\xE1rios e servi\xE7os"]
|
|
14
|
+
];
|
|
15
|
+
function AtendeAIPage() {
|
|
16
|
+
const runtime = usePluginRuntimeOptional();
|
|
17
|
+
const plugin = runtime?.plugins.find((p) => p.id === "conversations");
|
|
18
|
+
const on = plugin?.config?.facets?.atendeai === true;
|
|
19
|
+
const price = PRICE_BOOK["conversations.atendeai"];
|
|
20
|
+
const go = (hash) => {
|
|
21
|
+
navigateApp(hash);
|
|
22
|
+
};
|
|
23
|
+
if (!on) {
|
|
24
|
+
return /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-3xl space-y-4", children: [
|
|
25
|
+
/* @__PURE__ */ jsx(Card, { className: "overflow-hidden border-violet-200 dark:border-violet-900", children: /* @__PURE__ */ jsxs("div", { className: "space-y-5 bg-gradient-to-br from-violet-50 to-transparent p-6 dark:from-violet-950/40", children: [
|
|
26
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
27
|
+
/* @__PURE__ */ jsx("span", { className: "flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-violet-100 text-violet-700 dark:bg-violet-900/60 dark:text-violet-200", children: /* @__PURE__ */ jsx(Bot, { className: "h-6 w-6", "aria-hidden": true }) }),
|
|
28
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1", children: [
|
|
29
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
30
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: "AtendeAI" }),
|
|
31
|
+
/* @__PURE__ */ jsxs(Badge, { className: "border-transparent bg-violet-100 text-violet-700 dark:bg-violet-900/60 dark:text-violet-200", children: [
|
|
32
|
+
/* @__PURE__ */ jsx(Sparkles, { className: "mr-1 h-3 w-3", "aria-hidden": true }),
|
|
33
|
+
tl("AI", "IA")
|
|
34
|
+
] })
|
|
35
|
+
] }),
|
|
36
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: tl(
|
|
37
|
+
"An attendant that never sleeps: it talks to your clients on WhatsApp and only calls your team when it matters.",
|
|
38
|
+
"Um atendente que n\xE3o dorme: conversa com seus clientes no WhatsApp e s\xF3 chama a sua equipe quando importa."
|
|
39
|
+
) })
|
|
40
|
+
] })
|
|
41
|
+
] }),
|
|
42
|
+
/* @__PURE__ */ jsx("ul", { className: "grid gap-2 sm:grid-cols-2", children: PROMISES.map(([en, pt]) => /* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2 text-sm", children: [
|
|
43
|
+
/* @__PURE__ */ jsx(Check, { className: "mt-0.5 h-4 w-4 shrink-0 text-violet-600 dark:text-violet-300", "aria-hidden": true }),
|
|
44
|
+
/* @__PURE__ */ jsx("span", { children: tl(en, pt) })
|
|
45
|
+
] }, pt)) }),
|
|
46
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [
|
|
47
|
+
price && /* @__PURE__ */ jsxs("p", { className: "text-sm", children: [
|
|
48
|
+
/* @__PURE__ */ jsx("span", { className: "text-2xl font-bold tabular-nums", children: money(price.price) }),
|
|
49
|
+
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
50
|
+
" ",
|
|
51
|
+
price.per === "unit" ? tl("per unit / month", "por unidade / m\xEAs") : tl("/ month", "/ m\xEAs")
|
|
52
|
+
] })
|
|
53
|
+
] }),
|
|
54
|
+
/* @__PURE__ */ jsxs(Button, { className: "h-11 bg-violet-600 text-white hover:bg-violet-700", onClick: () => go("/settings/modules"), children: [
|
|
55
|
+
/* @__PURE__ */ jsx(Sparkles, { className: "h-4 w-4", "aria-hidden": true }),
|
|
56
|
+
tl("Turn on AtendeAI", "Ligar o AtendeAI")
|
|
57
|
+
] })
|
|
58
|
+
] })
|
|
59
|
+
] }) }),
|
|
60
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: tl("Turned on in Settings \u203A Modules; its settings appear right after.", "Liga em Configura\xE7\xF5es \u203A M\xF3dulos; as configura\xE7\xF5es dele aparecem logo em seguida.") })
|
|
61
|
+
] });
|
|
62
|
+
}
|
|
63
|
+
return /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-3xl space-y-4", children: [
|
|
64
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap justify-end gap-2", children: [
|
|
65
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", className: "h-11", onClick: () => go("/settings/modules"), children: tl("Manage or turn off", "Gerenciar ou desligar") }),
|
|
66
|
+
/* @__PURE__ */ jsxs(Button, { variant: "outline", className: "h-11", onClick: () => go("/settings/conversations-atendeai"), children: [
|
|
67
|
+
/* @__PURE__ */ jsx(Settings2, { className: "h-4 w-4", "aria-hidden": true }),
|
|
68
|
+
tl("AtendeAI settings", "Configura\xE7\xF5es do AtendeAI")
|
|
69
|
+
] })
|
|
70
|
+
] }),
|
|
71
|
+
/* @__PURE__ */ jsx(
|
|
72
|
+
ComingSoon,
|
|
73
|
+
{
|
|
74
|
+
title: tl("AtendeAI operation", "Opera\xE7\xE3o do AtendeAI"),
|
|
75
|
+
summary: tl(
|
|
76
|
+
"Follow what the AI is answering, accept or correct its drafts and teach it new answers.",
|
|
77
|
+
"Acompanhe o que a IA est\xE1 respondendo, aceite ou corrija os rascunhos e ensine respostas novas."
|
|
78
|
+
),
|
|
79
|
+
v1Path: "AtendeAI \u2192 Opera\xE7\xE3o",
|
|
80
|
+
missing: [
|
|
81
|
+
tl("The WhatsApp number of each unit connected", "O n\xFAmero de WhatsApp de cada unidade conectado"),
|
|
82
|
+
tl("The AI answering engine (personality, Q&A, capabilities)", "O motor de respostas da IA (personalidade, perguntas e respostas, capacidades)"),
|
|
83
|
+
tl("The queue of drafts waiting for acceptance", "A fila de rascunhos esperando aceite")
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
)
|
|
87
|
+
] });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export { AtendeAIPage };
|
|
91
|
+
//# sourceMappingURL=AtendeAIPage-PBBMWIDW.js.map
|
|
92
|
+
//# sourceMappingURL=AtendeAIPage-PBBMWIDW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/views/AtendeAIPage.tsx"],"names":[],"mappings":"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","file":"AtendeAIPage-PBBMWIDW.js","sourcesContent":["import * as React from 'react'\nimport { Bot, Check, Settings2, Sparkles } from 'lucide-react'\nimport { tl, usePluginRuntimeOptional, formatCurrency, navigateApp } from '@fayz-ai/core'\nimport { PRICE_BOOK } from '@fayz-ai/admin'\nimport { Badge, Button, Card, ComingSoon } from '@fayz-ai/ui'\n\n// The platform's own prices are in BRL whatever the tenant's currency.\nconst money = (n: number) => formatCurrency(n, 'BRL')\n\nconst PROMISES: Array<[string, string]> = [\n ['Answers your clients on WhatsApp, day and night', 'Responde seus clientes no WhatsApp, de dia e de noite'],\n ['Books, confirms and takes orders by itself', 'Agenda, confirma e anota pedidos sozinho'],\n ['Hands over to a person when it should', 'Passa para uma pessoa quando precisa'],\n ['Learns your prices, hours and services', 'Aprende seus preços, horários e serviços'],\n]\n\n/**\n * AtendeAI's door in the rail, as in V1 (Bot icon, \"Operação\" + \"Configurações\").\n * Off: the upsell, priced per unit. On: what the operation screen will do, and\n * the way to its settings.\n */\nexport function AtendeAIPage() {\n const runtime = usePluginRuntimeOptional()\n const plugin = runtime?.plugins.find((p) => p.id === 'conversations')\n const on = (plugin?.config?.facets as Record<string, boolean> | undefined)?.atendeai === true\n const price = PRICE_BOOK['conversations.atendeai']\n const go = (hash: string) => { navigateApp(hash) }\n\n if (!on) {\n return (\n <div className=\"mx-auto max-w-3xl space-y-4\">\n <Card className=\"overflow-hidden border-violet-200 dark:border-violet-900\">\n <div className=\"space-y-5 bg-gradient-to-br from-violet-50 to-transparent p-6 dark:from-violet-950/40\">\n <div className=\"flex items-start gap-3\">\n <span className=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-violet-100 text-violet-700 dark:bg-violet-900/60 dark:text-violet-200\">\n <Bot className=\"h-6 w-6\" aria-hidden />\n </span>\n <div className=\"min-w-0 space-y-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h2 className=\"text-lg font-semibold\">AtendeAI</h2>\n <Badge className=\"border-transparent bg-violet-100 text-violet-700 dark:bg-violet-900/60 dark:text-violet-200\">\n <Sparkles className=\"mr-1 h-3 w-3\" aria-hidden />{tl('AI', 'IA')}\n </Badge>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {tl('An attendant that never sleeps: it talks to your clients on WhatsApp and only calls your team when it matters.',\n 'Um atendente que não dorme: conversa com seus clientes no WhatsApp e só chama a sua equipe quando importa.')}\n </p>\n </div>\n </div>\n <ul className=\"grid gap-2 sm:grid-cols-2\">\n {PROMISES.map(([en, pt]) => (\n <li key={pt} className=\"flex items-start gap-2 text-sm\">\n <Check className=\"mt-0.5 h-4 w-4 shrink-0 text-violet-600 dark:text-violet-300\" aria-hidden />\n <span>{tl(en, pt)}</span>\n </li>\n ))}\n </ul>\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n {price && (\n <p className=\"text-sm\">\n <span className=\"text-2xl font-bold tabular-nums\">{money(price.price)}</span>\n <span className=\"text-muted-foreground\"> {price.per === 'unit' ? tl('per unit / month', 'por unidade / mês') : tl('/ month', '/ mês')}</span>\n </p>\n )}\n <Button className=\"h-11 bg-violet-600 text-white hover:bg-violet-700\" onClick={() => go('/settings/modules')}>\n <Sparkles className=\"h-4 w-4\" aria-hidden />\n {tl('Turn on AtendeAI', 'Ligar o AtendeAI')}\n </Button>\n </div>\n </div>\n </Card>\n <p className=\"text-xs text-muted-foreground\">\n {tl('Turned on in Settings › Modules; its settings appear right after.', 'Liga em Configurações › Módulos; as configurações dele aparecem logo em seguida.')}\n </p>\n </div>\n )\n }\n\n return (\n <div className=\"mx-auto max-w-3xl space-y-4\">\n <div className=\"flex flex-wrap justify-end gap-2\">\n <Button variant=\"ghost\" className=\"h-11\" onClick={() => go('/settings/modules')}>\n {tl('Manage or turn off', 'Gerenciar ou desligar')}\n </Button>\n <Button variant=\"outline\" className=\"h-11\" onClick={() => go('/settings/conversations-atendeai')}>\n <Settings2 className=\"h-4 w-4\" aria-hidden />\n {tl('AtendeAI settings', 'Configurações do AtendeAI')}\n </Button>\n </div>\n <ComingSoon\n title={tl('AtendeAI operation', 'Operação do AtendeAI')}\n summary={tl('Follow what the AI is answering, accept or correct its drafts and teach it new answers.',\n 'Acompanhe o que a IA está respondendo, aceite ou corrija os rascunhos e ensine respostas novas.')}\n v1Path=\"AtendeAI → Operação\"\n missing={[\n tl('The WhatsApp number of each unit connected', 'O número de WhatsApp de cada unidade conectado'),\n tl('The AI answering engine (personality, Q&A, capabilities)', 'O motor de respostas da IA (personalidade, perguntas e respostas, capacidades)'),\n tl('The queue of drafts waiting for acceptance', 'A fila de rascunhos esperando aceite'),\n ]}\n />\n </div>\n )\n}\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ConversationsContextProvider, InboxView } from './chunk-LJKLHGFX.js';
|
|
2
|
+
import './chunk-HMWUU5O7.js';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { currentAppPath, appHref } from '@fayz-ai/core';
|
|
5
|
+
import { jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
var CHANNELS = ["sms", "whatsapp", "instagram", "email", "webchat"];
|
|
8
|
+
function requestedContact() {
|
|
9
|
+
try {
|
|
10
|
+
const hash = currentAppPath();
|
|
11
|
+
const qs = hash.includes("?") ? hash.slice(hash.indexOf("?") + 1) : "";
|
|
12
|
+
if (!qs) return null;
|
|
13
|
+
const p = new URLSearchParams(qs);
|
|
14
|
+
const handle = (p.get("handle") ?? "").trim();
|
|
15
|
+
const name = (p.get("name") ?? "").trim();
|
|
16
|
+
const personId = (p.get("personId") ?? "").trim() || void 0;
|
|
17
|
+
if (!handle) return null;
|
|
18
|
+
const raw = (p.get("channel") ?? "whatsapp").toLowerCase();
|
|
19
|
+
const channel = CHANNELS.includes(raw) ? raw : "whatsapp";
|
|
20
|
+
return { personId, name: name || handle, handle, channel };
|
|
21
|
+
} catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function ConversationsPage({ store, config }) {
|
|
26
|
+
React.useEffect(() => {
|
|
27
|
+
const wanted = requestedContact();
|
|
28
|
+
if (!wanted) {
|
|
29
|
+
void store.getState().load();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
void store.getState().openForPerson(wanted).catch(() => {
|
|
33
|
+
void store.getState().load();
|
|
34
|
+
});
|
|
35
|
+
try {
|
|
36
|
+
const hash = currentAppPath();
|
|
37
|
+
const clean = hash.includes("?") ? hash.slice(0, hash.indexOf("?")) : hash;
|
|
38
|
+
window.history.replaceState(null, "", appHref(clean || "/conversations"));
|
|
39
|
+
} catch {
|
|
40
|
+
}
|
|
41
|
+
}, []);
|
|
42
|
+
return /* @__PURE__ */ jsx(ConversationsContextProvider, { store, config, children: /* @__PURE__ */ jsx(InboxView, {}) });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { ConversationsPage };
|
|
46
|
+
//# sourceMappingURL=ConversationsPage-4V4VLM4D.js.map
|
|
47
|
+
//# sourceMappingURL=ConversationsPage-4V4VLM4D.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/views/ConversationsPage.tsx"],"names":[],"mappings":";;;;;;AAQA,IAAM,WAAsB,CAAC,KAAA,EAAO,UAAA,EAAY,WAAA,EAAa,SAAS,SAAS,CAAA;AAQ/E,SAAS,gBAAA,GAAiG;AACxG,EAAA,IAAI;AACF,IAAA,MAAM,OAAO,cAAA,EAAe;AAC5B,IAAA,MAAM,EAAA,GAAK,IAAA,CAAK,QAAA,CAAS,GAAG,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,OAAA,CAAQ,GAAG,CAAA,GAAI,CAAC,CAAA,GAAI,EAAA;AACpE,IAAA,IAAI,CAAC,IAAI,OAAO,IAAA;AAChB,IAAA,MAAM,CAAA,GAAI,IAAI,eAAA,CAAgB,EAAE,CAAA;AAChC,IAAA,MAAM,UAAU,CAAA,CAAE,GAAA,CAAI,QAAQ,CAAA,IAAK,IAAI,IAAA,EAAK;AAC5C,IAAA,MAAM,QAAQ,CAAA,CAAE,GAAA,CAAI,MAAM,CAAA,IAAK,IAAI,IAAA,EAAK;AACxC,IAAA,MAAM,YAAY,CAAA,CAAE,GAAA,CAAI,UAAU,CAAA,IAAK,EAAA,EAAI,MAAK,IAAK,KAAA,CAAA;AAGrD,IAAA,IAAI,CAAC,QAAQ,OAAO,IAAA;AACpB,IAAA,MAAM,OAAO,CAAA,CAAE,GAAA,CAAI,SAAS,CAAA,IAAK,YAAY,WAAA,EAAY;AACzD,IAAA,MAAM,OAAA,GAAW,QAAA,CAAsB,QAAA,CAAS,GAAG,IAAK,GAAA,GAAkB,UAAA;AAC1E,IAAA,OAAO,EAAE,QAAA,EAAU,IAAA,EAAM,IAAA,IAAQ,MAAA,EAAQ,QAAQ,OAAA,EAAQ;AAAA,EAC3D,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAEO,SAAS,iBAAA,CAAkB,EAAE,KAAA,EAAO,MAAA,EAAO,EAG/C;AACD,EAAA,KAAA,CAAM,UAAU,MAAM;AACpB,IAAA,MAAM,SAAS,gBAAA,EAAiB;AAChC,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,KAAK,KAAA,CAAM,QAAA,EAAS,CAAE,IAAA,EAAK;AAC3B,MAAA;AAAA,IACF;AAKA,IAAA,KAAK,MAAM,QAAA,EAAS,CAAE,cAAc,MAAM,CAAA,CAAE,MAAM,MAAM;AACtD,MAAA,KAAK,KAAA,CAAM,QAAA,EAAS,CAAE,IAAA,EAAK;AAAA,IAC7B,CAAC,CAAA;AAMD,IAAA,IAAI;AACF,MAAA,MAAM,OAAO,cAAA,EAAe;AAC5B,MAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,QAAA,CAAS,GAAG,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,IAAA,CAAK,OAAA,CAAQ,GAAG,CAAC,CAAA,GAAI,IAAA;AACtE,MAAA,MAAA,CAAO,QAAQ,YAAA,CAAa,IAAA,EAAM,IAAI,OAAA,CAAQ,KAAA,IAAS,gBAAgB,CAAC,CAAA;AAAA,IAC1E,CAAA,CAAA,MAAQ;AAAA,IAAoD;AAAA,EAE9D,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,2BACG,4BAAA,EAAA,EAA6B,KAAA,EAAc,MAAA,EAC1C,QAAA,kBAAA,GAAA,CAAC,aAAU,CAAA,EACb,CAAA;AAEJ","file":"ConversationsPage-4V4VLM4D.js","sourcesContent":["import React from 'react'\nimport type { StoreApi } from 'zustand/vanilla'\nimport { ConversationsContextProvider, type ResolvedConversationsConfig } from '../context'\nimport type { ConversationsUIState } from '../store'\nimport type { Channel } from '../types'\nimport { InboxView } from './InboxView'\nimport { currentAppPath, appHref } from '@fayz-ai/core'\n\nconst CHANNELS: Channel[] = ['sms', 'whatsapp', 'instagram', 'email', 'webchat']\n\n/** Quem o link de entrada pediu para abrir.\n *\n * A rota da shell é por hash, então os parâmetros vêm depois do `?` DENTRO do\n * hash — `#/conversations?personId=…`. `window.location.search` está vazio\n * aqui, e foi o primeiro lugar onde procurei.\n */\nfunction requestedContact(): { personId?: string; name: string; handle: string; channel: Channel } | null {\n try {\n const hash = currentAppPath()\n const qs = hash.includes('?') ? hash.slice(hash.indexOf('?') + 1) : ''\n if (!qs) return null\n const p = new URLSearchParams(qs)\n const handle = (p.get('handle') ?? '').trim()\n const name = (p.get('name') ?? '').trim()\n const personId = (p.get('personId') ?? '').trim() || undefined\n // Sem por onde falar não há conversa para abrir. Criar uma thread sem\n // handle produziria um registro que nunca entrega nada.\n if (!handle) return null\n const raw = (p.get('channel') ?? 'whatsapp').toLowerCase()\n const channel = (CHANNELS as string[]).includes(raw) ? (raw as Channel) : 'whatsapp'\n return { personId, name: name || handle, handle, channel }\n } catch {\n return null\n }\n}\n\nexport function ConversationsPage({ store, config }: {\n store: StoreApi<ConversationsUIState>\n config?: ResolvedConversationsConfig\n}) {\n React.useEffect(() => {\n const wanted = requestedContact()\n if (!wanted) {\n void store.getState().load()\n return\n }\n\n // `openForPerson` carrega a caixa sozinho quando precisa, então não há\n // `load()` aqui: os dois juntos disputariam a mesma lista e a thread recém\n // aberta poderia ser substituída pela resposta mais lenta.\n void store.getState().openForPerson(wanted).catch(() => {\n void store.getState().load()\n })\n\n // O parâmetro é uma ORDEM, não um estado: cumprida uma vez, sai da barra de\n // endereços. Sem isto, um F5 meia hora depois reabriria — ou pior,\n // recriaria — a conversa daquela pessoa, e o link ficaria colado no\n // histórico do navegador.\n try {\n const hash = currentAppPath()\n const clean = hash.includes('?') ? hash.slice(0, hash.indexOf('?')) : hash\n window.history.replaceState(null, '', appHref(clean || '/conversations'))\n } catch { /* a barra fica como está; a conversa já abriu */ }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n return (\n <ConversationsContextProvider store={store} config={config}>\n <InboxView />\n </ConversationsContextProvider>\n )\n}\n"]}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { COPY, WhatsAppProviders, listMessagingChannels, isPendingChannelStatus } from './chunk-3FZX3ZQ3.js';
|
|
2
|
+
import { providersApi } from './chunk-HMWUU5O7.js';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { useTranslation, tl } from '@fayz-ai/core';
|
|
5
|
+
import { PluginSettingsPanel, useConnectors, useConnectorsPanelAvailable, ConnectorMark, StateChip, openConnectorsPanel, ConnectorDetail, SettingsGroup } from '@fayz-ai/admin';
|
|
6
|
+
import { Badge, Button, Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetBody } from '@fayz-ai/ui';
|
|
7
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
8
|
+
|
|
9
|
+
var UPCOMING = ["Meta Cloud API", "Z-API", "Twilio"];
|
|
10
|
+
var PROVIDERS = ["tyxter", "evolution"];
|
|
11
|
+
function providerState(row) {
|
|
12
|
+
if (row?.isActive) return { label: tl("In use", "Valendo agora"), tone: "default" };
|
|
13
|
+
if (row?.hasCredential) return { label: tl("Connected", "Conectado"), tone: "secondary" };
|
|
14
|
+
return { label: tl("Not connected", "N\xE3o conectado"), tone: "outline" };
|
|
15
|
+
}
|
|
16
|
+
function WhatsAppConnectorList() {
|
|
17
|
+
const [rows, setRows] = React.useState(null);
|
|
18
|
+
const [open, setOpen] = React.useState(null);
|
|
19
|
+
const [hubSelected, setHubSelected] = React.useState(null);
|
|
20
|
+
const { connectors } = useConnectors({ hostPluginId: "conversations", includeGrants: false });
|
|
21
|
+
const panelAvailable = useConnectorsPanelAvailable();
|
|
22
|
+
const load = React.useCallback(() => {
|
|
23
|
+
providersApi.list().then(setRows).catch(() => setRows([]));
|
|
24
|
+
}, []);
|
|
25
|
+
React.useEffect(load, [load]);
|
|
26
|
+
const others = (connectors ?? []).filter((c) => !PROVIDERS.includes(c.slug));
|
|
27
|
+
const byId = Object.fromEntries((rows ?? []).map((r) => [r.provider, r]));
|
|
28
|
+
const tyxterHub = (connectors ?? []).find((c) => c.slug === "tyxter");
|
|
29
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
30
|
+
/* @__PURE__ */ jsx("h3", { className: "px-1 text-sm font-semibold", children: tl("WhatsApp providers", "Provedores de WhatsApp") }),
|
|
31
|
+
/* @__PURE__ */ jsxs("ul", { className: "divide-y rounded-card border bg-card shadow-sm", children: [
|
|
32
|
+
PROVIDERS.map((p) => {
|
|
33
|
+
const row = byId[p];
|
|
34
|
+
const state = providerState(row);
|
|
35
|
+
const copy = COPY[p];
|
|
36
|
+
return /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-3 p-4", children: [
|
|
37
|
+
p === "tyxter" && tyxterHub ? /* @__PURE__ */ jsx(ConnectorMark, { connector: tyxterHub }) : /* @__PURE__ */ jsx("span", { className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-muted text-xs font-semibold text-muted-foreground", children: copy.name.slice(0, 2) }),
|
|
38
|
+
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
39
|
+
/* @__PURE__ */ jsxs("span", { className: "flex flex-wrap items-center gap-2", children: [
|
|
40
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: copy.name }),
|
|
41
|
+
/* @__PURE__ */ jsx(Badge, { variant: state.tone, className: "font-normal", children: state.label })
|
|
42
|
+
] }),
|
|
43
|
+
/* @__PURE__ */ jsx("span", { className: "mt-0.5 block text-xs text-muted-foreground", children: copy.what })
|
|
44
|
+
] }),
|
|
45
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", className: "h-9 shrink-0", onClick: () => setOpen(p), children: row?.hasCredential ? tl("Configure", "Configurar") : tl("Connect", "Conectar") })
|
|
46
|
+
] }, p);
|
|
47
|
+
}),
|
|
48
|
+
others.map((c) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-3 p-4", children: [
|
|
49
|
+
/* @__PURE__ */ jsx(ConnectorMark, { connector: c }),
|
|
50
|
+
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
51
|
+
/* @__PURE__ */ jsxs("span", { className: "flex flex-wrap items-center gap-2", children: [
|
|
52
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: c.name }),
|
|
53
|
+
/* @__PURE__ */ jsx(StateChip, { state: c.state })
|
|
54
|
+
] }),
|
|
55
|
+
c.description && /* @__PURE__ */ jsx("span", { className: "mt-0.5 block text-xs text-muted-foreground", children: c.description })
|
|
56
|
+
] }),
|
|
57
|
+
/* @__PURE__ */ jsx(
|
|
58
|
+
Button,
|
|
59
|
+
{
|
|
60
|
+
variant: "outline",
|
|
61
|
+
className: "h-9 shrink-0",
|
|
62
|
+
onClick: () => panelAvailable ? openConnectorsPanel(c) : setHubSelected(c),
|
|
63
|
+
children: c.state === "connected" ? tl("Configure", "Configurar") : tl("Connect", "Conectar")
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
] }, c.id)),
|
|
67
|
+
UPCOMING.map((name) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-3 p-4 opacity-70", children: [
|
|
68
|
+
/* @__PURE__ */ jsx("span", { className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-muted text-xs font-semibold text-muted-foreground", children: name.slice(0, 2) }),
|
|
69
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 font-medium", children: name }),
|
|
70
|
+
/* @__PURE__ */ jsx(Badge, { variant: "secondary", children: tl("Coming soon", "Em breve") })
|
|
71
|
+
] }, name))
|
|
72
|
+
] }),
|
|
73
|
+
/* @__PURE__ */ jsx(Sheet, { open: !!open, onOpenChange: (o) => {
|
|
74
|
+
if (!o) setOpen(null);
|
|
75
|
+
}, children: /* @__PURE__ */ jsxs(SheetContent, { width: "max-w-xl", children: [
|
|
76
|
+
/* @__PURE__ */ jsxs(SheetHeader, { children: [
|
|
77
|
+
/* @__PURE__ */ jsx(SheetTitle, { children: open ? COPY[open].name : "" }),
|
|
78
|
+
/* @__PURE__ */ jsx(SheetDescription, { children: tl("One provider sends at a time; the other keeps its settings.", "Um provedor envia por vez; o outro guarda a configura\xE7\xE3o.") })
|
|
79
|
+
] }),
|
|
80
|
+
/* @__PURE__ */ jsx(SheetBody, { children: open && /* @__PURE__ */ jsx(WhatsAppProviders, { only: open, onChanged: load }) })
|
|
81
|
+
] }) }),
|
|
82
|
+
/* @__PURE__ */ jsx(ConnectorDetail, { connector: hubSelected, open: !!hubSelected, onOpenChange: (o) => {
|
|
83
|
+
if (!o) setHubSelected(null);
|
|
84
|
+
} })
|
|
85
|
+
] });
|
|
86
|
+
}
|
|
87
|
+
function channelLabel(account, t) {
|
|
88
|
+
const key = `conversations.filter.${account.channel}`;
|
|
89
|
+
const translated = t(key);
|
|
90
|
+
return translated === key ? account.channel : translated;
|
|
91
|
+
}
|
|
92
|
+
function ChannelRow({ account }) {
|
|
93
|
+
const t = useTranslation();
|
|
94
|
+
const pending = isPendingChannelStatus(account.status);
|
|
95
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 border-b py-2.5 last:border-b-0", children: [
|
|
96
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
97
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium", children: account.phoneE164 ?? account.displayName ?? channelLabel(account, t) }),
|
|
98
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
99
|
+
channelLabel(account, t),
|
|
100
|
+
" \xB7 ",
|
|
101
|
+
t(
|
|
102
|
+
account.kind === "dedicated" ? "conversations.settings.channelDedicated" : "conversations.settings.channelShared"
|
|
103
|
+
)
|
|
104
|
+
] })
|
|
105
|
+
] }),
|
|
106
|
+
/* @__PURE__ */ jsx(Badge, { variant: account.status === "active" ? "default" : pending ? "secondary" : "outline", children: t(`conversations.settings.status.${account.status}`) === `conversations.settings.status.${account.status}` ? account.status : t(`conversations.settings.status.${account.status}`) })
|
|
107
|
+
] });
|
|
108
|
+
}
|
|
109
|
+
function ConversationsGeneralSettings() {
|
|
110
|
+
const t = useTranslation();
|
|
111
|
+
const [channels, setChannels] = React.useState(null);
|
|
112
|
+
const [failed, setFailed] = React.useState(false);
|
|
113
|
+
React.useEffect(() => {
|
|
114
|
+
let cancelled = false;
|
|
115
|
+
listMessagingChannels().then((rows) => {
|
|
116
|
+
if (!cancelled) setChannels(rows);
|
|
117
|
+
}).catch(() => {
|
|
118
|
+
if (!cancelled) {
|
|
119
|
+
setFailed(true);
|
|
120
|
+
setChannels([]);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
return () => {
|
|
124
|
+
cancelled = true;
|
|
125
|
+
};
|
|
126
|
+
}, []);
|
|
127
|
+
return /* @__PURE__ */ jsx("div", { className: "space-y-6", children: /* @__PURE__ */ jsxs(
|
|
128
|
+
SettingsGroup,
|
|
129
|
+
{
|
|
130
|
+
title: t("conversations.settings.channels"),
|
|
131
|
+
description: t("conversations.settings.channelsHelp"),
|
|
132
|
+
children: [
|
|
133
|
+
channels === null && /* @__PURE__ */ jsx("p", { className: "py-2 text-sm text-muted-foreground", children: t("conversations.list.loading") }),
|
|
134
|
+
channels !== null && failed && /* @__PURE__ */ jsx("p", { className: "py-2 text-sm text-muted-foreground", children: t("conversations.settings.channelsUnavailable") }),
|
|
135
|
+
channels !== null && !failed && channels.length === 0 && /* @__PURE__ */ jsx("p", { className: "py-2 text-sm text-muted-foreground", children: t("conversations.settings.channelsEmpty") }),
|
|
136
|
+
channels?.map((account) => /* @__PURE__ */ jsx(ChannelRow, { account }, account.id))
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
) });
|
|
140
|
+
}
|
|
141
|
+
ConversationsGeneralSettings.displayName = "ConversationsGeneralSettings";
|
|
142
|
+
function ConversationsSettingsTab() {
|
|
143
|
+
const t = useTranslation();
|
|
144
|
+
return /* @__PURE__ */ jsx(
|
|
145
|
+
PluginSettingsPanel,
|
|
146
|
+
{
|
|
147
|
+
title: t("conversations.settings.title"),
|
|
148
|
+
generalSettings: /* @__PURE__ */ jsx(ConversationsGeneralSettings, {}),
|
|
149
|
+
customTabs: [{ id: "connectors", label: tl("Connectors", "Conectores"), icon: "Plug", content: /* @__PURE__ */ jsx(WhatsAppConnectorList, {}) }],
|
|
150
|
+
routeBase: "/settings/conversations"
|
|
151
|
+
}
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
ConversationsSettingsTab.displayName = "ConversationsSettingsTab";
|
|
155
|
+
|
|
156
|
+
export { ConversationsSettingsTab };
|
|
157
|
+
//# sourceMappingURL=ConversationsSettings-7UQYM26R.js.map
|
|
158
|
+
//# sourceMappingURL=ConversationsSettings-7UQYM26R.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/settings/WhatsAppConnectorList.tsx","../src/settings/ConversationsSettings.tsx"],"names":["jsxs","jsx","Badge","React2","tl"],"mappings":"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","file":"ConversationsSettings-7UQYM26R.js","sourcesContent":["import * as React from 'react'\nimport { tl } from '@fayz-ai/core'\nimport {\n ConnectorMark, StateChip, ConnectorDetail, useConnectors, openConnectorsPanel, useConnectorsPanelAvailable,\n type UnifiedConnector,\n} from '@fayz-ai/admin'\nimport { Badge, Button, Sheet, SheetBody, SheetContent, SheetDescription, SheetHeader, SheetTitle } from '@fayz-ai/ui'\nimport { providersApi, type ProviderId, type ProviderRow } from '../data/providers'\nimport { WhatsAppProviders, WHATSAPP_PROVIDER_COPY } from './WhatsAppProviders'\n\n// Providers with no connector here yet: listed so the house sees its provider\n// is known, not missing.\nconst UPCOMING = ['Meta Cloud API', 'Z-API', 'Twilio']\nconst PROVIDERS: ProviderId[] = ['tyxter', 'evolution']\n\nfunction providerState(row: ProviderRow | undefined): { label: string; tone: 'default' | 'secondary' | 'outline' } {\n if (row?.isActive) return { label: tl('In use', 'Valendo agora'), tone: 'default' }\n if (row?.hasCredential) return { label: tl('Connected', 'Conectado'), tone: 'secondary' }\n return { label: tl('Not connected', 'Não conectado'), tone: 'outline' }\n}\n\n/**\n * Where WhatsApp goes out, as one list (the Maquininhas pattern): mark, name,\n * state, one line, and Conectar/Configurar. Each provider's credentials,\n * test, numbers and pairing live in its own detail, not on this screen.\n */\nexport function WhatsAppConnectorList() {\n const [rows, setRows] = React.useState<ProviderRow[] | null>(null)\n const [open, setOpen] = React.useState<ProviderId | null>(null)\n const [hubSelected, setHubSelected] = React.useState<UnifiedConnector | null>(null)\n const { connectors } = useConnectors({ hostPluginId: 'conversations', includeGrants: false })\n const panelAvailable = useConnectorsPanelAvailable()\n\n const load = React.useCallback(() => {\n providersApi.list().then(setRows).catch(() => setRows([]))\n }, [])\n React.useEffect(load, [load])\n\n // Hub connectors of this inbox that are not one of the two providers above.\n const others = (connectors ?? []).filter((c) => !PROVIDERS.includes(c.slug as ProviderId))\n const byId = Object.fromEntries((rows ?? []).map((r) => [r.provider, r])) as Record<string, ProviderRow | undefined>\n const tyxterHub = (connectors ?? []).find((c) => c.slug === 'tyxter')\n\n return (\n <div className=\"space-y-2\">\n <h3 className=\"px-1 text-sm font-semibold\">{tl('WhatsApp providers', 'Provedores de WhatsApp')}</h3>\n <ul className=\"divide-y rounded-card border bg-card shadow-sm\">\n {PROVIDERS.map((p) => {\n const row = byId[p]\n const state = providerState(row)\n const copy = WHATSAPP_PROVIDER_COPY[p]\n return (\n <li key={p} className=\"flex items-center gap-3 p-4\">\n {p === 'tyxter' && tyxterHub ? <ConnectorMark connector={tyxterHub} /> : (\n <span className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-muted text-xs font-semibold text-muted-foreground\">\n {copy.name.slice(0, 2)}\n </span>\n )}\n <span className=\"min-w-0 flex-1\">\n <span className=\"flex flex-wrap items-center gap-2\">\n <span className=\"font-medium\">{copy.name}</span>\n <Badge variant={state.tone} className=\"font-normal\">{state.label}</Badge>\n </span>\n <span className=\"mt-0.5 block text-xs text-muted-foreground\">{copy.what}</span>\n </span>\n <Button variant=\"outline\" className=\"h-9 shrink-0\" onClick={() => setOpen(p)}>\n {row?.hasCredential ? tl('Configure', 'Configurar') : tl('Connect', 'Conectar')}\n </Button>\n </li>\n )\n })}\n {others.map((c) => (\n <li key={c.id} className=\"flex items-center gap-3 p-4\">\n <ConnectorMark connector={c} />\n <span className=\"min-w-0 flex-1\">\n <span className=\"flex flex-wrap items-center gap-2\">\n <span className=\"font-medium\">{c.name}</span>\n <StateChip state={c.state} />\n </span>\n {c.description && <span className=\"mt-0.5 block text-xs text-muted-foreground\">{c.description}</span>}\n </span>\n <Button variant=\"outline\" className=\"h-9 shrink-0\"\n onClick={() => (panelAvailable ? openConnectorsPanel(c) : setHubSelected(c))}>\n {c.state === 'connected' ? tl('Configure', 'Configurar') : tl('Connect', 'Conectar')}\n </Button>\n </li>\n ))}\n {UPCOMING.map((name) => (\n <li key={name} className=\"flex items-center gap-3 p-4 opacity-70\">\n <span className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-muted text-xs font-semibold text-muted-foreground\">\n {name.slice(0, 2)}\n </span>\n <span className=\"min-w-0 flex-1 font-medium\">{name}</span>\n <Badge variant=\"secondary\">{tl('Coming soon', 'Em breve')}</Badge>\n </li>\n ))}\n </ul>\n\n <Sheet open={!!open} onOpenChange={(o) => { if (!o) setOpen(null) }}>\n <SheetContent width=\"max-w-xl\">\n <SheetHeader>\n <SheetTitle>{open ? WHATSAPP_PROVIDER_COPY[open].name : ''}</SheetTitle>\n <SheetDescription>\n {tl('One provider sends at a time; the other keeps its settings.', 'Um provedor envia por vez; o outro guarda a configuração.')}\n </SheetDescription>\n </SheetHeader>\n <SheetBody>{open && <WhatsAppProviders only={open} onChanged={load} />}</SheetBody>\n </SheetContent>\n </Sheet>\n <ConnectorDetail connector={hubSelected} open={!!hubSelected} onOpenChange={(o) => { if (!o) setHubSelected(null) }} />\n </div>\n )\n}\n","import * as React from 'react'\nimport { WhatsAppConnectorList } from './WhatsAppConnectorList'\nimport { tl, useTranslation } from '@fayz-ai/core'\nimport { PluginSettingsPanel, SettingsGroup } from '@fayz-ai/admin'\nimport { Badge } from '@fayz-ai/ui'\nimport {\n listMessagingChannels,\n isPendingChannelStatus,\n type MessagingChannelAccount,\n} from '../data/channels'\n\n// ---------------------------------------------------------------------------\n// The inbox's settings screen.\n//\n// It exists for one reason: without it the WhatsApp connector is UNREACHABLE.\n// Connectors are surfaced by PluginSettingsPanel's Integrations tab, and that\n// tab only exists inside a plugin settings panel — so a plugin that declares a\n// connector and no settings route has declared a connector nobody can open.\n// Every app that mounted the inbox alone was in exactly that state.\n//\n// The General tab is deliberately not a copy of the connector's panel. That one\n// manages the Tyxter account (claim a number, register with Meta). This one\n// answers the question somebody asks BEFORE they know a connector exists:\n// \"why isn't my WhatsApp showing up in here?\" — so it states, plainly, which\n// numbers this inbox can send and receive by, and sends you one tab over when\n// the answer is \"none\".\n// ---------------------------------------------------------------------------\n\nfunction channelLabel(\n account: MessagingChannelAccount,\n t: (key: string) => string,\n): string {\n const key = `conversations.filter.${account.channel}`\n const translated = t(key)\n return translated === key ? account.channel : translated\n}\n\nfunction ChannelRow({ account }: { account: MessagingChannelAccount }) {\n const t = useTranslation()\n const pending = isPendingChannelStatus(account.status)\n return (\n <div className=\"flex items-center justify-between gap-3 border-b py-2.5 last:border-b-0\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium\">\n {account.phoneE164 ?? account.displayName ?? channelLabel(account, t)}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {channelLabel(account, t)}\n {' · '}\n {t(\n account.kind === 'dedicated'\n ? 'conversations.settings.channelDedicated'\n : 'conversations.settings.channelShared',\n )}\n </p>\n </div>\n {/* The provider's own word for the state, translated where we have a\n translation and passed through where we do not — a status invented\n here would drift from what the connector panel says one tab over. */}\n <Badge variant={account.status === 'active' ? 'default' : pending ? 'secondary' : 'outline'}>\n {t(`conversations.settings.status.${account.status}`) === `conversations.settings.status.${account.status}`\n ? account.status\n : t(`conversations.settings.status.${account.status}`)}\n </Badge>\n </div>\n )\n}\n\nfunction ConversationsGeneralSettings() {\n const t = useTranslation()\n const [channels, setChannels] = React.useState<MessagingChannelAccount[] | null>(null)\n // `null` = still loading, `[]` = loaded and empty, `'error'` = could not ask.\n // Three states and not two, because \"nenhum número conectado\" and \"não\n // consegui perguntar\" send somebody to opposite places.\n const [failed, setFailed] = React.useState(false)\n\n React.useEffect(() => {\n let cancelled = false\n listMessagingChannels()\n .then((rows) => { if (!cancelled) setChannels(rows) })\n .catch(() => { if (!cancelled) { setFailed(true); setChannels([]) } })\n return () => { cancelled = true }\n }, [])\n\n return (\n <div className=\"space-y-6\">\n <SettingsGroup\n title={t('conversations.settings.channels')}\n description={t('conversations.settings.channelsHelp')}\n >\n {channels === null && (\n <p className=\"py-2 text-sm text-muted-foreground\">{t('conversations.list.loading')}</p>\n )}\n {channels !== null && failed && (\n <p className=\"py-2 text-sm text-muted-foreground\">\n {t('conversations.settings.channelsUnavailable')}\n </p>\n )}\n {channels !== null && !failed && channels.length === 0 && (\n <p className=\"py-2 text-sm text-muted-foreground\">\n {t('conversations.settings.channelsEmpty')}\n </p>\n )}\n {channels?.map((account) => <ChannelRow key={account.id} account={account} />)}\n </SettingsGroup>\n </div>\n )\n}\nConversationsGeneralSettings.displayName = 'ConversationsGeneralSettings'\n\nexport function ConversationsSettingsTab() {\n const t = useTranslation()\n return (\n <PluginSettingsPanel\n title={t('conversations.settings.title')}\n generalSettings={<ConversationsGeneralSettings />}\n customTabs={[{ id: 'connectors', label: tl('Connectors', 'Conectores'), icon: 'Plug', content: <WhatsAppConnectorList /> }]}\n routeBase=\"/settings/conversations\"\n />\n )\n}\nConversationsSettingsTab.displayName = 'ConversationsSettingsTab'\n"]}
|