@greatapps/greatchat-ui 0.1.4 → 0.1.6
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/index.d.ts +122 -1
- package/dist/index.js +1692 -0
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
- package/src/components/channel-card.tsx +100 -0
- package/src/components/channel-create-dialog.tsx +126 -0
- package/src/components/channel-edit-dialog.tsx +132 -0
- package/src/components/channels-page.tsx +242 -0
- package/src/components/chat-dashboard.tsx +433 -0
- package/src/components/contact-form-dialog.tsx +139 -0
- package/src/components/contacts-page.tsx +41 -0
- package/src/components/contacts-table.tsx +218 -0
- package/src/components/data-table.tsx +185 -0
- package/src/components/inbox-page.tsx +174 -0
- package/src/components/index.ts +6 -0
- package/src/components/ui/card.tsx +94 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/progress.tsx +31 -0
- package/src/components/ui/table.tsx +101 -0
- package/src/components/whatsapp-icon.tsx +21 -0
- package/src/components/whatsapp-qr-dialog.tsx +147 -0
- package/src/components/whatsapp-status-badge.tsx +72 -0
- package/src/index.ts +40 -1
package/dist/index.js
CHANGED
|
@@ -2474,19 +2474,1711 @@ function NewConversationDialog({
|
|
|
2474
2474
|
] })
|
|
2475
2475
|
] }) });
|
|
2476
2476
|
}
|
|
2477
|
+
|
|
2478
|
+
// src/components/whatsapp-status-badge.tsx
|
|
2479
|
+
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
2480
|
+
function WhatsappStatusBadge({
|
|
2481
|
+
status,
|
|
2482
|
+
hasSession
|
|
2483
|
+
}) {
|
|
2484
|
+
if (!hasSession) {
|
|
2485
|
+
return /* @__PURE__ */ jsx24(Badge, { variant: "outline", className: "text-xs text-zinc-500 border-zinc-300", children: "Sem sess\xE3o" });
|
|
2486
|
+
}
|
|
2487
|
+
if (!status) {
|
|
2488
|
+
return /* @__PURE__ */ jsx24(Badge, { variant: "outline", className: "text-xs text-zinc-500 border-zinc-300", children: "Verificando..." });
|
|
2489
|
+
}
|
|
2490
|
+
if (status.connected && status.logged_in) {
|
|
2491
|
+
return /* @__PURE__ */ jsx24(
|
|
2492
|
+
Badge,
|
|
2493
|
+
{
|
|
2494
|
+
variant: "outline",
|
|
2495
|
+
className: "text-xs bg-green-500/10 text-green-600 border-green-200",
|
|
2496
|
+
children: "Conectado"
|
|
2497
|
+
}
|
|
2498
|
+
);
|
|
2499
|
+
}
|
|
2500
|
+
if (status.connected && !status.logged_in) {
|
|
2501
|
+
return /* @__PURE__ */ jsx24(
|
|
2502
|
+
Badge,
|
|
2503
|
+
{
|
|
2504
|
+
variant: "outline",
|
|
2505
|
+
className: "text-xs bg-yellow-500/10 text-yellow-600 border-yellow-200",
|
|
2506
|
+
children: "Reconectando..."
|
|
2507
|
+
}
|
|
2508
|
+
);
|
|
2509
|
+
}
|
|
2510
|
+
if (status.logged_in && !status.connected) {
|
|
2511
|
+
return /* @__PURE__ */ jsx24(
|
|
2512
|
+
Badge,
|
|
2513
|
+
{
|
|
2514
|
+
variant: "outline",
|
|
2515
|
+
className: "text-xs bg-yellow-500/10 text-yellow-600 border-yellow-200",
|
|
2516
|
+
children: "Desconectado"
|
|
2517
|
+
}
|
|
2518
|
+
);
|
|
2519
|
+
}
|
|
2520
|
+
return /* @__PURE__ */ jsx24(
|
|
2521
|
+
Badge,
|
|
2522
|
+
{
|
|
2523
|
+
variant: "outline",
|
|
2524
|
+
className: "text-xs bg-red-500/10 text-red-600 border-red-200",
|
|
2525
|
+
children: "Offline"
|
|
2526
|
+
}
|
|
2527
|
+
);
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2530
|
+
// src/components/channel-card.tsx
|
|
2531
|
+
import { jsx as jsx25, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2532
|
+
function ChannelCard({
|
|
2533
|
+
channel,
|
|
2534
|
+
config,
|
|
2535
|
+
onEdit,
|
|
2536
|
+
onDelete,
|
|
2537
|
+
linkedAgentName,
|
|
2538
|
+
linkedAgentActive = true,
|
|
2539
|
+
actions
|
|
2540
|
+
}) {
|
|
2541
|
+
const { data: status } = useChannelWhatsappStatus(config, channel.id);
|
|
2542
|
+
const hasSession = !!channel.external_id;
|
|
2543
|
+
return /* @__PURE__ */ jsxs14("div", { className: "rounded-lg border bg-card text-card-foreground shadow-sm", children: [
|
|
2544
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex flex-row items-start justify-between p-4 pb-2", children: [
|
|
2545
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2", children: [
|
|
2546
|
+
/* @__PURE__ */ jsx25("div", { className: "flex h-9 w-9 items-center justify-center rounded-md bg-green-500/10", children: /* @__PURE__ */ jsx25("svg", { className: "h-5 w-5 text-green-600", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx25("path", { d: "M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z" }) }) }),
|
|
2547
|
+
/* @__PURE__ */ jsxs14("div", { children: [
|
|
2548
|
+
/* @__PURE__ */ jsx25("h3", { className: "text-sm font-medium", children: channel.name }),
|
|
2549
|
+
/* @__PURE__ */ jsx25("p", { className: "text-xs text-muted-foreground", children: channel.identifier || "Sem n\xFAmero" })
|
|
2550
|
+
] })
|
|
2551
|
+
] }),
|
|
2552
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-1", children: [
|
|
2553
|
+
onEdit && /* @__PURE__ */ jsx25(
|
|
2554
|
+
Button,
|
|
2555
|
+
{
|
|
2556
|
+
variant: "ghost",
|
|
2557
|
+
size: "icon",
|
|
2558
|
+
className: "h-7 w-7",
|
|
2559
|
+
onClick: onEdit,
|
|
2560
|
+
children: /* @__PURE__ */ jsxs14("svg", { className: "h-3.5 w-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
2561
|
+
/* @__PURE__ */ jsx25("path", { d: "M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" }),
|
|
2562
|
+
/* @__PURE__ */ jsx25("path", { d: "m15 5 4 4" })
|
|
2563
|
+
] })
|
|
2564
|
+
}
|
|
2565
|
+
),
|
|
2566
|
+
onDelete && /* @__PURE__ */ jsx25(
|
|
2567
|
+
Button,
|
|
2568
|
+
{
|
|
2569
|
+
variant: "ghost",
|
|
2570
|
+
size: "icon",
|
|
2571
|
+
className: "h-7 w-7 text-destructive hover:text-destructive",
|
|
2572
|
+
onClick: onDelete,
|
|
2573
|
+
children: /* @__PURE__ */ jsxs14("svg", { className: "h-3.5 w-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
2574
|
+
/* @__PURE__ */ jsx25("path", { d: "M3 6h18" }),
|
|
2575
|
+
/* @__PURE__ */ jsx25("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }),
|
|
2576
|
+
/* @__PURE__ */ jsx25("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" })
|
|
2577
|
+
] })
|
|
2578
|
+
}
|
|
2579
|
+
),
|
|
2580
|
+
/* @__PURE__ */ jsx25(WhatsappStatusBadge, { status, hasSession })
|
|
2581
|
+
] })
|
|
2582
|
+
] }),
|
|
2583
|
+
/* @__PURE__ */ jsxs14("div", { className: "p-4 pt-0 space-y-3", children: [
|
|
2584
|
+
linkedAgentName && /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
|
|
2585
|
+
/* @__PURE__ */ jsxs14("svg", { className: "h-3.5 w-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
2586
|
+
/* @__PURE__ */ jsx25("path", { d: "M12 8V4H8" }),
|
|
2587
|
+
/* @__PURE__ */ jsx25("rect", { width: "16", height: "12", x: "4", y: "8", rx: "2" }),
|
|
2588
|
+
/* @__PURE__ */ jsx25("path", { d: "M2 14h2" }),
|
|
2589
|
+
/* @__PURE__ */ jsx25("path", { d: "M20 14h2" }),
|
|
2590
|
+
/* @__PURE__ */ jsx25("path", { d: "M15 13v2" }),
|
|
2591
|
+
/* @__PURE__ */ jsx25("path", { d: "M9 13v2" })
|
|
2592
|
+
] }),
|
|
2593
|
+
/* @__PURE__ */ jsxs14("span", { children: [
|
|
2594
|
+
"Agent: ",
|
|
2595
|
+
/* @__PURE__ */ jsx25("strong", { children: linkedAgentName })
|
|
2596
|
+
] }),
|
|
2597
|
+
!linkedAgentActive && /* @__PURE__ */ jsx25("span", { className: "text-[10px] px-1 py-0 border rounded", children: "inativo" })
|
|
2598
|
+
] }),
|
|
2599
|
+
actions
|
|
2600
|
+
] })
|
|
2601
|
+
] });
|
|
2602
|
+
}
|
|
2603
|
+
|
|
2604
|
+
// src/components/whatsapp-icon.tsx
|
|
2605
|
+
import { jsx as jsx26, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2606
|
+
function WhatsappIcon(props) {
|
|
2607
|
+
return /* @__PURE__ */ jsxs15(
|
|
2608
|
+
"svg",
|
|
2609
|
+
{
|
|
2610
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2611
|
+
width: "24",
|
|
2612
|
+
height: "24",
|
|
2613
|
+
viewBox: "0 0 24 24",
|
|
2614
|
+
fill: "none",
|
|
2615
|
+
stroke: "currentColor",
|
|
2616
|
+
strokeWidth: 2,
|
|
2617
|
+
strokeLinecap: "round",
|
|
2618
|
+
strokeLinejoin: "round",
|
|
2619
|
+
...props,
|
|
2620
|
+
children: [
|
|
2621
|
+
/* @__PURE__ */ jsx26("path", { d: "M3 21l1.65 -3.8a9 9 0 1 1 3.4 2.9l-5.05 .9" }),
|
|
2622
|
+
/* @__PURE__ */ jsx26("path", { d: "M9 10a.5 .5 0 0 0 1 0v-1a.5 .5 0 0 0 -1 0v1a5 5 0 0 0 5 5h1a.5 .5 0 0 0 0 -1h-1a.5 .5 0 0 0 0 1" })
|
|
2623
|
+
]
|
|
2624
|
+
}
|
|
2625
|
+
);
|
|
2626
|
+
}
|
|
2627
|
+
|
|
2628
|
+
// src/components/whatsapp-qr-dialog.tsx
|
|
2629
|
+
import { useEffect as useEffect3, useState as useState6, useCallback as useCallback2 } from "react";
|
|
2630
|
+
import { Loader2 as Loader22, Check as Check4, AlertCircle as AlertCircle2, RefreshCw } from "lucide-react";
|
|
2631
|
+
import { toast } from "sonner";
|
|
2632
|
+
import { jsx as jsx27, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2633
|
+
function WhatsappQrDialog({
|
|
2634
|
+
open,
|
|
2635
|
+
onOpenChange,
|
|
2636
|
+
channelId,
|
|
2637
|
+
config
|
|
2638
|
+
}) {
|
|
2639
|
+
const connectChannel = useConnectChannel(config);
|
|
2640
|
+
const [state, setState] = useState6("connecting");
|
|
2641
|
+
const [errorMsg, setErrorMsg] = useState6("");
|
|
2642
|
+
const { data: qrUrl } = useChannelQR(
|
|
2643
|
+
config,
|
|
2644
|
+
channelId,
|
|
2645
|
+
open && (state === "waiting_qr" || state === "showing_qr")
|
|
2646
|
+
);
|
|
2647
|
+
const { data: status } = useChannelWhatsappStatus(
|
|
2648
|
+
config,
|
|
2649
|
+
channelId,
|
|
2650
|
+
open && state !== "connected"
|
|
2651
|
+
);
|
|
2652
|
+
const startConnect = useCallback2(() => {
|
|
2653
|
+
setState("connecting");
|
|
2654
|
+
setErrorMsg("");
|
|
2655
|
+
connectChannel.mutate(channelId, {
|
|
2656
|
+
onSuccess: () => setState("waiting_qr"),
|
|
2657
|
+
onError: (err) => {
|
|
2658
|
+
setState("error");
|
|
2659
|
+
setErrorMsg(err.message || "Erro ao conectar");
|
|
2660
|
+
}
|
|
2661
|
+
});
|
|
2662
|
+
}, [channelId, connectChannel]);
|
|
2663
|
+
useEffect3(() => {
|
|
2664
|
+
if (!open) {
|
|
2665
|
+
setState("connecting");
|
|
2666
|
+
setErrorMsg("");
|
|
2667
|
+
return;
|
|
2668
|
+
}
|
|
2669
|
+
startConnect();
|
|
2670
|
+
}, [open, channelId]);
|
|
2671
|
+
useEffect3(() => {
|
|
2672
|
+
if (qrUrl && (state === "waiting_qr" || state === "showing_qr")) {
|
|
2673
|
+
setState("showing_qr");
|
|
2674
|
+
}
|
|
2675
|
+
}, [qrUrl, state]);
|
|
2676
|
+
useEffect3(() => {
|
|
2677
|
+
if (status?.connected && status?.logged_in) {
|
|
2678
|
+
setState("connected");
|
|
2679
|
+
toast.success("WhatsApp conectado com sucesso!");
|
|
2680
|
+
setTimeout(() => onOpenChange(false), 1500);
|
|
2681
|
+
}
|
|
2682
|
+
}, [status, onOpenChange]);
|
|
2683
|
+
return /* @__PURE__ */ jsx27(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs16(DialogContent, { className: "sm:max-w-md", children: [
|
|
2684
|
+
/* @__PURE__ */ jsxs16(DialogHeader, { children: [
|
|
2685
|
+
/* @__PURE__ */ jsx27(DialogTitle, { children: "Conectar WhatsApp" }),
|
|
2686
|
+
/* @__PURE__ */ jsx27(DialogDescription, { children: "Escaneie o QR code com seu WhatsApp para conectar" })
|
|
2687
|
+
] }),
|
|
2688
|
+
/* @__PURE__ */ jsxs16("div", { className: "flex min-h-[300px] items-center justify-center", children: [
|
|
2689
|
+
state === "connecting" && /* @__PURE__ */ jsxs16("div", { className: "flex flex-col items-center gap-3 text-muted-foreground", children: [
|
|
2690
|
+
/* @__PURE__ */ jsx27(Loader22, { className: "h-8 w-8 animate-spin" }),
|
|
2691
|
+
/* @__PURE__ */ jsx27("p", { className: "text-sm", children: "Iniciando conex\xE3o..." })
|
|
2692
|
+
] }),
|
|
2693
|
+
state === "waiting_qr" && /* @__PURE__ */ jsxs16("div", { className: "flex flex-col items-center gap-3 text-muted-foreground", children: [
|
|
2694
|
+
/* @__PURE__ */ jsx27(Loader22, { className: "h-8 w-8 animate-spin" }),
|
|
2695
|
+
/* @__PURE__ */ jsx27("p", { className: "text-sm", children: "Gerando QR code..." })
|
|
2696
|
+
] }),
|
|
2697
|
+
state === "showing_qr" && qrUrl && /* @__PURE__ */ jsxs16("div", { className: "flex flex-col items-center gap-3", children: [
|
|
2698
|
+
/* @__PURE__ */ jsx27(
|
|
2699
|
+
"img",
|
|
2700
|
+
{
|
|
2701
|
+
src: qrUrl,
|
|
2702
|
+
alt: "QR Code WhatsApp",
|
|
2703
|
+
className: "h-64 w-64 rounded-lg border"
|
|
2704
|
+
}
|
|
2705
|
+
),
|
|
2706
|
+
/* @__PURE__ */ jsx27("p", { className: "text-sm text-muted-foreground", children: "Abra o WhatsApp > Aparelhos conectados > Conectar" })
|
|
2707
|
+
] }),
|
|
2708
|
+
state === "connected" && /* @__PURE__ */ jsxs16("div", { className: "flex flex-col items-center gap-3 text-green-600", children: [
|
|
2709
|
+
/* @__PURE__ */ jsx27(Check4, { className: "h-12 w-12" }),
|
|
2710
|
+
/* @__PURE__ */ jsx27("p", { className: "text-lg font-medium", children: "Conectado!" })
|
|
2711
|
+
] }),
|
|
2712
|
+
state === "error" && /* @__PURE__ */ jsxs16("div", { className: "flex flex-col items-center gap-3 text-destructive", children: [
|
|
2713
|
+
/* @__PURE__ */ jsx27(AlertCircle2, { className: "h-8 w-8" }),
|
|
2714
|
+
/* @__PURE__ */ jsx27("p", { className: "text-sm", children: errorMsg || "Erro ao gerar QR code" }),
|
|
2715
|
+
/* @__PURE__ */ jsxs16(
|
|
2716
|
+
Button,
|
|
2717
|
+
{
|
|
2718
|
+
variant: "outline",
|
|
2719
|
+
size: "sm",
|
|
2720
|
+
onClick: startConnect,
|
|
2721
|
+
children: [
|
|
2722
|
+
/* @__PURE__ */ jsx27(RefreshCw, { className: "mr-2 h-4 w-4" }),
|
|
2723
|
+
"Tentar novamente"
|
|
2724
|
+
]
|
|
2725
|
+
}
|
|
2726
|
+
)
|
|
2727
|
+
] })
|
|
2728
|
+
] })
|
|
2729
|
+
] }) });
|
|
2730
|
+
}
|
|
2731
|
+
|
|
2732
|
+
// src/components/channel-create-dialog.tsx
|
|
2733
|
+
import { useState as useState7 } from "react";
|
|
2734
|
+
|
|
2735
|
+
// src/components/ui/label.tsx
|
|
2736
|
+
import { Label as LabelPrimitive } from "radix-ui";
|
|
2737
|
+
import { jsx as jsx28 } from "react/jsx-runtime";
|
|
2738
|
+
function Label({
|
|
2739
|
+
className,
|
|
2740
|
+
...props
|
|
2741
|
+
}) {
|
|
2742
|
+
return /* @__PURE__ */ jsx28(
|
|
2743
|
+
LabelPrimitive.Root,
|
|
2744
|
+
{
|
|
2745
|
+
"data-slot": "label",
|
|
2746
|
+
className: cn(
|
|
2747
|
+
"gap-2 text-sm leading-none font-medium group-data-[disabled=true]:opacity-50 peer-disabled:opacity-50 flex items-center select-none group-data-[disabled=true]:pointer-events-none peer-disabled:cursor-not-allowed",
|
|
2748
|
+
className
|
|
2749
|
+
),
|
|
2750
|
+
...props
|
|
2751
|
+
}
|
|
2752
|
+
);
|
|
2753
|
+
}
|
|
2754
|
+
|
|
2755
|
+
// src/components/channel-create-dialog.tsx
|
|
2756
|
+
import { Loader2 as Loader23 } from "lucide-react";
|
|
2757
|
+
import { toast as toast2 } from "sonner";
|
|
2758
|
+
import { jsx as jsx29, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2759
|
+
function ChannelCreateDialog({
|
|
2760
|
+
open,
|
|
2761
|
+
onOpenChange,
|
|
2762
|
+
config,
|
|
2763
|
+
renderAgentSelect
|
|
2764
|
+
}) {
|
|
2765
|
+
const createChannel = useCreateChannel(config);
|
|
2766
|
+
const [name, setName] = useState7("");
|
|
2767
|
+
const [identifier, setIdentifier] = useState7("");
|
|
2768
|
+
const [idAgent, setIdAgent] = useState7(null);
|
|
2769
|
+
async function handleSubmit(e) {
|
|
2770
|
+
e.preventDefault();
|
|
2771
|
+
if (!name.trim()) return;
|
|
2772
|
+
try {
|
|
2773
|
+
await createChannel.mutateAsync({
|
|
2774
|
+
name: name.trim(),
|
|
2775
|
+
type: "whatsapp_unofficial",
|
|
2776
|
+
provider: "gwhatsmeow",
|
|
2777
|
+
identifier: identifier.trim() || void 0,
|
|
2778
|
+
id_agent: idAgent
|
|
2779
|
+
});
|
|
2780
|
+
toast2.success("Canal criado! Agora conecte via QR code.");
|
|
2781
|
+
setName("");
|
|
2782
|
+
setIdentifier("");
|
|
2783
|
+
setIdAgent(null);
|
|
2784
|
+
onOpenChange(false);
|
|
2785
|
+
} catch {
|
|
2786
|
+
toast2.error("Erro ao criar canal");
|
|
2787
|
+
}
|
|
2788
|
+
}
|
|
2789
|
+
return /* @__PURE__ */ jsx29(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs17(DialogContent, { children: [
|
|
2790
|
+
/* @__PURE__ */ jsx29(DialogHeader, { children: /* @__PURE__ */ jsxs17(DialogTitle, { className: "flex items-center gap-2", children: [
|
|
2791
|
+
/* @__PURE__ */ jsx29(WhatsappIcon, { className: "h-5 w-5 text-green-600" }),
|
|
2792
|
+
"Novo Canal WhatsApp"
|
|
2793
|
+
] }) }),
|
|
2794
|
+
/* @__PURE__ */ jsxs17("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
2795
|
+
/* @__PURE__ */ jsxs17("div", { className: "space-y-2", children: [
|
|
2796
|
+
/* @__PURE__ */ jsx29(Label, { htmlFor: "channel-name", children: "Nome do canal *" }),
|
|
2797
|
+
/* @__PURE__ */ jsx29(
|
|
2798
|
+
Input,
|
|
2799
|
+
{
|
|
2800
|
+
id: "channel-name",
|
|
2801
|
+
value: name,
|
|
2802
|
+
onChange: (e) => setName(e.target.value),
|
|
2803
|
+
placeholder: "Ex: WhatsApp Principal",
|
|
2804
|
+
required: true,
|
|
2805
|
+
disabled: createChannel.isPending
|
|
2806
|
+
}
|
|
2807
|
+
)
|
|
2808
|
+
] }),
|
|
2809
|
+
/* @__PURE__ */ jsxs17("div", { className: "space-y-2", children: [
|
|
2810
|
+
/* @__PURE__ */ jsx29(Label, { htmlFor: "channel-identifier", children: "N\xFAmero do WhatsApp (opcional)" }),
|
|
2811
|
+
/* @__PURE__ */ jsx29(
|
|
2812
|
+
Input,
|
|
2813
|
+
{
|
|
2814
|
+
id: "channel-identifier",
|
|
2815
|
+
value: identifier,
|
|
2816
|
+
onChange: (e) => setIdentifier(e.target.value),
|
|
2817
|
+
placeholder: "5511999999999",
|
|
2818
|
+
disabled: createChannel.isPending
|
|
2819
|
+
}
|
|
2820
|
+
),
|
|
2821
|
+
/* @__PURE__ */ jsx29("p", { className: "text-xs text-muted-foreground", children: "Ser\xE1 detectado automaticamente ao escanear o QR code" })
|
|
2822
|
+
] }),
|
|
2823
|
+
renderAgentSelect?.({
|
|
2824
|
+
value: idAgent,
|
|
2825
|
+
onChange: setIdAgent,
|
|
2826
|
+
disabled: createChannel.isPending
|
|
2827
|
+
}),
|
|
2828
|
+
/* @__PURE__ */ jsxs17(DialogFooter, { children: [
|
|
2829
|
+
/* @__PURE__ */ jsx29(
|
|
2830
|
+
Button,
|
|
2831
|
+
{
|
|
2832
|
+
type: "button",
|
|
2833
|
+
variant: "outline",
|
|
2834
|
+
onClick: () => onOpenChange(false),
|
|
2835
|
+
disabled: createChannel.isPending,
|
|
2836
|
+
children: "Cancelar"
|
|
2837
|
+
}
|
|
2838
|
+
),
|
|
2839
|
+
/* @__PURE__ */ jsxs17(Button, { type: "submit", disabled: createChannel.isPending || !name.trim(), children: [
|
|
2840
|
+
createChannel.isPending && /* @__PURE__ */ jsx29(Loader23, { className: "mr-2 h-4 w-4 animate-spin" }),
|
|
2841
|
+
"Criar Canal"
|
|
2842
|
+
] })
|
|
2843
|
+
] })
|
|
2844
|
+
] })
|
|
2845
|
+
] }) });
|
|
2846
|
+
}
|
|
2847
|
+
|
|
2848
|
+
// src/components/channel-edit-dialog.tsx
|
|
2849
|
+
import { useState as useState8, useEffect as useEffect4 } from "react";
|
|
2850
|
+
import { Loader2 as Loader24 } from "lucide-react";
|
|
2851
|
+
import { toast as toast3 } from "sonner";
|
|
2852
|
+
import { jsx as jsx30, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2853
|
+
function ChannelEditDialog({
|
|
2854
|
+
open,
|
|
2855
|
+
onOpenChange,
|
|
2856
|
+
channel,
|
|
2857
|
+
config,
|
|
2858
|
+
renderAgentSelect
|
|
2859
|
+
}) {
|
|
2860
|
+
const updateChannel = useUpdateChannel(config);
|
|
2861
|
+
const [name, setName] = useState8(channel.name);
|
|
2862
|
+
const [identifier, setIdentifier] = useState8(channel.identifier || "");
|
|
2863
|
+
const [idAgent, setIdAgent] = useState8(channel.id_agent);
|
|
2864
|
+
useEffect4(() => {
|
|
2865
|
+
if (open) {
|
|
2866
|
+
setName(channel.name);
|
|
2867
|
+
setIdentifier(channel.identifier || "");
|
|
2868
|
+
setIdAgent(channel.id_agent);
|
|
2869
|
+
}
|
|
2870
|
+
}, [open, channel]);
|
|
2871
|
+
async function handleSubmit(e) {
|
|
2872
|
+
e.preventDefault();
|
|
2873
|
+
if (!name.trim()) return;
|
|
2874
|
+
try {
|
|
2875
|
+
await updateChannel.mutateAsync({
|
|
2876
|
+
id: channel.id,
|
|
2877
|
+
body: {
|
|
2878
|
+
name: name.trim(),
|
|
2879
|
+
identifier: identifier.trim() || void 0,
|
|
2880
|
+
id_agent: idAgent
|
|
2881
|
+
}
|
|
2882
|
+
});
|
|
2883
|
+
toast3.success("Canal atualizado");
|
|
2884
|
+
onOpenChange(false);
|
|
2885
|
+
} catch {
|
|
2886
|
+
toast3.error("Erro ao atualizar canal");
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
return /* @__PURE__ */ jsx30(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs18(DialogContent, { children: [
|
|
2890
|
+
/* @__PURE__ */ jsx30(DialogHeader, { children: /* @__PURE__ */ jsxs18(DialogTitle, { className: "flex items-center gap-2", children: [
|
|
2891
|
+
/* @__PURE__ */ jsx30(WhatsappIcon, { className: "h-5 w-5 text-green-600" }),
|
|
2892
|
+
"Editar Canal"
|
|
2893
|
+
] }) }),
|
|
2894
|
+
/* @__PURE__ */ jsxs18("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
2895
|
+
/* @__PURE__ */ jsxs18("div", { className: "space-y-2", children: [
|
|
2896
|
+
/* @__PURE__ */ jsx30(Label, { htmlFor: "edit-channel-name", children: "Nome do canal *" }),
|
|
2897
|
+
/* @__PURE__ */ jsx30(
|
|
2898
|
+
Input,
|
|
2899
|
+
{
|
|
2900
|
+
id: "edit-channel-name",
|
|
2901
|
+
value: name,
|
|
2902
|
+
onChange: (e) => setName(e.target.value),
|
|
2903
|
+
placeholder: "Ex: WhatsApp Principal",
|
|
2904
|
+
required: true,
|
|
2905
|
+
disabled: updateChannel.isPending
|
|
2906
|
+
}
|
|
2907
|
+
)
|
|
2908
|
+
] }),
|
|
2909
|
+
/* @__PURE__ */ jsxs18("div", { className: "space-y-2", children: [
|
|
2910
|
+
/* @__PURE__ */ jsx30(Label, { htmlFor: "edit-channel-identifier", children: "N\xFAmero do WhatsApp (opcional)" }),
|
|
2911
|
+
/* @__PURE__ */ jsx30(
|
|
2912
|
+
Input,
|
|
2913
|
+
{
|
|
2914
|
+
id: "edit-channel-identifier",
|
|
2915
|
+
value: identifier,
|
|
2916
|
+
onChange: (e) => setIdentifier(e.target.value),
|
|
2917
|
+
placeholder: "5511999999999",
|
|
2918
|
+
disabled: updateChannel.isPending
|
|
2919
|
+
}
|
|
2920
|
+
)
|
|
2921
|
+
] }),
|
|
2922
|
+
renderAgentSelect?.({
|
|
2923
|
+
value: idAgent,
|
|
2924
|
+
onChange: setIdAgent,
|
|
2925
|
+
disabled: updateChannel.isPending
|
|
2926
|
+
}),
|
|
2927
|
+
/* @__PURE__ */ jsxs18(DialogFooter, { children: [
|
|
2928
|
+
/* @__PURE__ */ jsx30(
|
|
2929
|
+
Button,
|
|
2930
|
+
{
|
|
2931
|
+
type: "button",
|
|
2932
|
+
variant: "outline",
|
|
2933
|
+
onClick: () => onOpenChange(false),
|
|
2934
|
+
disabled: updateChannel.isPending,
|
|
2935
|
+
children: "Cancelar"
|
|
2936
|
+
}
|
|
2937
|
+
),
|
|
2938
|
+
/* @__PURE__ */ jsxs18(Button, { type: "submit", disabled: updateChannel.isPending || !name.trim(), children: [
|
|
2939
|
+
updateChannel.isPending && /* @__PURE__ */ jsx30(Loader24, { className: "mr-2 h-4 w-4 animate-spin" }),
|
|
2940
|
+
"Salvar"
|
|
2941
|
+
] })
|
|
2942
|
+
] })
|
|
2943
|
+
] })
|
|
2944
|
+
] }) });
|
|
2945
|
+
}
|
|
2946
|
+
|
|
2947
|
+
// src/components/contact-form-dialog.tsx
|
|
2948
|
+
import { useEffect as useEffect5, useState as useState9 } from "react";
|
|
2949
|
+
import { Loader2 as Loader25 } from "lucide-react";
|
|
2950
|
+
import { toast as toast4 } from "sonner";
|
|
2951
|
+
import { jsx as jsx31, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2952
|
+
function ContactFormDialog({
|
|
2953
|
+
open,
|
|
2954
|
+
onOpenChange,
|
|
2955
|
+
contact,
|
|
2956
|
+
config
|
|
2957
|
+
}) {
|
|
2958
|
+
const isEditing = !!contact;
|
|
2959
|
+
const createContact = useCreateContact(config);
|
|
2960
|
+
const updateContact = useUpdateContact(config);
|
|
2961
|
+
const [name, setName] = useState9("");
|
|
2962
|
+
const [phoneNumber, setPhoneNumber] = useState9("");
|
|
2963
|
+
const [identifier, setIdentifier] = useState9("");
|
|
2964
|
+
useEffect5(() => {
|
|
2965
|
+
if (contact) {
|
|
2966
|
+
setName(contact.name);
|
|
2967
|
+
setPhoneNumber(contact.phone_number || "");
|
|
2968
|
+
setIdentifier(contact.identifier || "");
|
|
2969
|
+
} else {
|
|
2970
|
+
setName("");
|
|
2971
|
+
setPhoneNumber("");
|
|
2972
|
+
setIdentifier("");
|
|
2973
|
+
}
|
|
2974
|
+
}, [contact, open]);
|
|
2975
|
+
const isPending = createContact.isPending || updateContact.isPending;
|
|
2976
|
+
async function handleSubmit(e) {
|
|
2977
|
+
e.preventDefault();
|
|
2978
|
+
if (!name.trim()) return;
|
|
2979
|
+
const body = {
|
|
2980
|
+
name: name.trim(),
|
|
2981
|
+
phone_number: phoneNumber.trim() || void 0,
|
|
2982
|
+
identifier: identifier.trim() || void 0
|
|
2983
|
+
};
|
|
2984
|
+
try {
|
|
2985
|
+
if (isEditing) {
|
|
2986
|
+
await updateContact.mutateAsync({ id: contact.id, body });
|
|
2987
|
+
toast4.success("Contato atualizado");
|
|
2988
|
+
} else {
|
|
2989
|
+
await createContact.mutateAsync(body);
|
|
2990
|
+
toast4.success("Contato criado");
|
|
2991
|
+
}
|
|
2992
|
+
onOpenChange(false);
|
|
2993
|
+
} catch {
|
|
2994
|
+
toast4.error(isEditing ? "Erro ao atualizar" : "Erro ao criar contato");
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
return /* @__PURE__ */ jsx31(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs19(DialogContent, { children: [
|
|
2998
|
+
/* @__PURE__ */ jsx31(DialogHeader, { children: /* @__PURE__ */ jsx31(DialogTitle, { children: isEditing ? "Editar Contato" : "Novo Contato" }) }),
|
|
2999
|
+
/* @__PURE__ */ jsxs19("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
3000
|
+
/* @__PURE__ */ jsxs19("div", { className: "space-y-2", children: [
|
|
3001
|
+
/* @__PURE__ */ jsx31(Label, { htmlFor: "contact-name", children: "Nome *" }),
|
|
3002
|
+
/* @__PURE__ */ jsx31(
|
|
3003
|
+
Input,
|
|
3004
|
+
{
|
|
3005
|
+
id: "contact-name",
|
|
3006
|
+
value: name,
|
|
3007
|
+
onChange: (e) => setName(e.target.value),
|
|
3008
|
+
placeholder: "Nome do contato",
|
|
3009
|
+
required: true,
|
|
3010
|
+
disabled: isPending
|
|
3011
|
+
}
|
|
3012
|
+
)
|
|
3013
|
+
] }),
|
|
3014
|
+
/* @__PURE__ */ jsxs19("div", { className: "space-y-2", children: [
|
|
3015
|
+
/* @__PURE__ */ jsx31(Label, { htmlFor: "contact-phone", children: "Telefone" }),
|
|
3016
|
+
/* @__PURE__ */ jsx31(
|
|
3017
|
+
Input,
|
|
3018
|
+
{
|
|
3019
|
+
id: "contact-phone",
|
|
3020
|
+
value: phoneNumber,
|
|
3021
|
+
onChange: (e) => setPhoneNumber(e.target.value),
|
|
3022
|
+
placeholder: "5511999999999",
|
|
3023
|
+
disabled: isPending
|
|
3024
|
+
}
|
|
3025
|
+
)
|
|
3026
|
+
] }),
|
|
3027
|
+
/* @__PURE__ */ jsxs19("div", { className: "space-y-2", children: [
|
|
3028
|
+
/* @__PURE__ */ jsx31(Label, { htmlFor: "contact-identifier", children: "Identificador (WhatsApp ID)" }),
|
|
3029
|
+
/* @__PURE__ */ jsx31(
|
|
3030
|
+
Input,
|
|
3031
|
+
{
|
|
3032
|
+
id: "contact-identifier",
|
|
3033
|
+
value: identifier,
|
|
3034
|
+
onChange: (e) => setIdentifier(e.target.value),
|
|
3035
|
+
placeholder: "5511999999999@s.whatsapp.net",
|
|
3036
|
+
disabled: isPending
|
|
3037
|
+
}
|
|
3038
|
+
)
|
|
3039
|
+
] }),
|
|
3040
|
+
/* @__PURE__ */ jsxs19(DialogFooter, { children: [
|
|
3041
|
+
/* @__PURE__ */ jsx31(
|
|
3042
|
+
Button,
|
|
3043
|
+
{
|
|
3044
|
+
type: "button",
|
|
3045
|
+
variant: "outline",
|
|
3046
|
+
onClick: () => onOpenChange(false),
|
|
3047
|
+
disabled: isPending,
|
|
3048
|
+
children: "Cancelar"
|
|
3049
|
+
}
|
|
3050
|
+
),
|
|
3051
|
+
/* @__PURE__ */ jsxs19(Button, { type: "submit", disabled: isPending || !name.trim(), children: [
|
|
3052
|
+
isPending ? /* @__PURE__ */ jsx31(Loader25, { className: "mr-2 h-4 w-4 animate-spin" }) : null,
|
|
3053
|
+
isEditing ? "Salvar" : "Criar"
|
|
3054
|
+
] })
|
|
3055
|
+
] })
|
|
3056
|
+
] })
|
|
3057
|
+
] }) });
|
|
3058
|
+
}
|
|
3059
|
+
|
|
3060
|
+
// src/components/contacts-table.tsx
|
|
3061
|
+
import { useMemo as useMemo4, useState as useState11 } from "react";
|
|
3062
|
+
|
|
3063
|
+
// src/components/data-table.tsx
|
|
3064
|
+
import {
|
|
3065
|
+
flexRender,
|
|
3066
|
+
getCoreRowModel,
|
|
3067
|
+
getSortedRowModel,
|
|
3068
|
+
useReactTable
|
|
3069
|
+
} from "@tanstack/react-table";
|
|
3070
|
+
import { useState as useState10 } from "react";
|
|
3071
|
+
|
|
3072
|
+
// src/components/ui/table.tsx
|
|
3073
|
+
import { jsx as jsx32 } from "react/jsx-runtime";
|
|
3074
|
+
function Table({ className, ...props }) {
|
|
3075
|
+
return /* @__PURE__ */ jsx32("div", { "data-slot": "table-container", className: "relative w-full overflow-x-auto", children: /* @__PURE__ */ jsx32(
|
|
3076
|
+
"table",
|
|
3077
|
+
{
|
|
3078
|
+
"data-slot": "table",
|
|
3079
|
+
className: cn("w-full caption-bottom text-sm", className),
|
|
3080
|
+
...props
|
|
3081
|
+
}
|
|
3082
|
+
) });
|
|
3083
|
+
}
|
|
3084
|
+
function TableHeader({ className, ...props }) {
|
|
3085
|
+
return /* @__PURE__ */ jsx32(
|
|
3086
|
+
"thead",
|
|
3087
|
+
{
|
|
3088
|
+
"data-slot": "table-header",
|
|
3089
|
+
className: cn("[&_tr]:border-b", className),
|
|
3090
|
+
...props
|
|
3091
|
+
}
|
|
3092
|
+
);
|
|
3093
|
+
}
|
|
3094
|
+
function TableBody({ className, ...props }) {
|
|
3095
|
+
return /* @__PURE__ */ jsx32(
|
|
3096
|
+
"tbody",
|
|
3097
|
+
{
|
|
3098
|
+
"data-slot": "table-body",
|
|
3099
|
+
className: cn("[&_tr:last-child]:border-0", className),
|
|
3100
|
+
...props
|
|
3101
|
+
}
|
|
3102
|
+
);
|
|
3103
|
+
}
|
|
3104
|
+
function TableRow({ className, ...props }) {
|
|
3105
|
+
return /* @__PURE__ */ jsx32(
|
|
3106
|
+
"tr",
|
|
3107
|
+
{
|
|
3108
|
+
"data-slot": "table-row",
|
|
3109
|
+
className: cn("hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors", className),
|
|
3110
|
+
...props
|
|
3111
|
+
}
|
|
3112
|
+
);
|
|
3113
|
+
}
|
|
3114
|
+
function TableHead({ className, ...props }) {
|
|
3115
|
+
return /* @__PURE__ */ jsx32(
|
|
3116
|
+
"th",
|
|
3117
|
+
{
|
|
3118
|
+
"data-slot": "table-head",
|
|
3119
|
+
className: cn("text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0", className),
|
|
3120
|
+
...props
|
|
3121
|
+
}
|
|
3122
|
+
);
|
|
3123
|
+
}
|
|
3124
|
+
function TableCell({ className, ...props }) {
|
|
3125
|
+
return /* @__PURE__ */ jsx32(
|
|
3126
|
+
"td",
|
|
3127
|
+
{
|
|
3128
|
+
"data-slot": "table-cell",
|
|
3129
|
+
className: cn("p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0", className),
|
|
3130
|
+
...props
|
|
3131
|
+
}
|
|
3132
|
+
);
|
|
3133
|
+
}
|
|
3134
|
+
|
|
3135
|
+
// src/components/data-table.tsx
|
|
3136
|
+
import { ChevronLeft, ChevronRight as ChevronRight2, ArrowUp, ArrowDown, ArrowUpDown } from "lucide-react";
|
|
3137
|
+
import { jsx as jsx33, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
3138
|
+
function DataTable({
|
|
3139
|
+
columns,
|
|
3140
|
+
data,
|
|
3141
|
+
isLoading,
|
|
3142
|
+
emptyMessage = "Nenhum registro encontrado",
|
|
3143
|
+
total,
|
|
3144
|
+
page = 1,
|
|
3145
|
+
onPageChange,
|
|
3146
|
+
pageSize = 15,
|
|
3147
|
+
onRowClick,
|
|
3148
|
+
selectedRowId,
|
|
3149
|
+
getRowId,
|
|
3150
|
+
compact
|
|
3151
|
+
}) {
|
|
3152
|
+
const [sorting, setSorting] = useState10([]);
|
|
3153
|
+
const table = useReactTable({
|
|
3154
|
+
data,
|
|
3155
|
+
columns,
|
|
3156
|
+
getCoreRowModel: getCoreRowModel(),
|
|
3157
|
+
getSortedRowModel: getSortedRowModel(),
|
|
3158
|
+
onSortingChange: setSorting,
|
|
3159
|
+
state: { sorting }
|
|
3160
|
+
});
|
|
3161
|
+
const totalPages = total ? Math.ceil(total / pageSize) : 1;
|
|
3162
|
+
const showPagination = onPageChange && total && total > pageSize;
|
|
3163
|
+
return /* @__PURE__ */ jsxs20("div", { className: "space-y-4", children: [
|
|
3164
|
+
/* @__PURE__ */ jsx33("div", { className: "rounded-md border", children: /* @__PURE__ */ jsxs20(Table, { children: [
|
|
3165
|
+
/* @__PURE__ */ jsx33(TableHeader, { children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx33(TableRow, { children: headerGroup.headers.map((header) => /* @__PURE__ */ jsx33(
|
|
3166
|
+
TableHead,
|
|
3167
|
+
{
|
|
3168
|
+
className: cn(compact && "py-1.5 text-xs"),
|
|
3169
|
+
style: { width: header.getSize() !== 150 ? header.getSize() : void 0 },
|
|
3170
|
+
children: header.isPlaceholder ? null : header.column.getCanSort() ? /* @__PURE__ */ jsxs20(
|
|
3171
|
+
"button",
|
|
3172
|
+
{
|
|
3173
|
+
type: "button",
|
|
3174
|
+
className: "flex items-center gap-1 hover:text-foreground -ml-1 px-1 py-0.5 rounded cursor-pointer select-none",
|
|
3175
|
+
onClick: header.column.getToggleSortingHandler(),
|
|
3176
|
+
children: [
|
|
3177
|
+
flexRender(header.column.columnDef.header, header.getContext()),
|
|
3178
|
+
{
|
|
3179
|
+
asc: /* @__PURE__ */ jsx33(ArrowUp, { className: "h-3.5 w-3.5" }),
|
|
3180
|
+
desc: /* @__PURE__ */ jsx33(ArrowDown, { className: "h-3.5 w-3.5" })
|
|
3181
|
+
}[header.column.getIsSorted()] ?? /* @__PURE__ */ jsx33(ArrowUpDown, { className: "h-3.5 w-3.5 text-muted-foreground/50" })
|
|
3182
|
+
]
|
|
3183
|
+
}
|
|
3184
|
+
) : flexRender(header.column.columnDef.header, header.getContext())
|
|
3185
|
+
},
|
|
3186
|
+
header.id
|
|
3187
|
+
)) }, headerGroup.id)) }),
|
|
3188
|
+
/* @__PURE__ */ jsx33(TableBody, { children: isLoading ? Array.from({ length: compact ? 3 : 5 }).map((_, i) => /* @__PURE__ */ jsx33(TableRow, { children: columns.map((_2, j) => /* @__PURE__ */ jsx33(TableCell, { className: cn(compact && "py-1.5"), children: /* @__PURE__ */ jsx33(Skeleton, { className: "h-4 w-full max-w-[120px]" }) }, j)) }, i)) : table.getRowModel().rows.length === 0 ? /* @__PURE__ */ jsx33(TableRow, { children: /* @__PURE__ */ jsx33(
|
|
3189
|
+
TableCell,
|
|
3190
|
+
{
|
|
3191
|
+
colSpan: columns.length,
|
|
3192
|
+
className: cn(
|
|
3193
|
+
"text-center text-muted-foreground",
|
|
3194
|
+
compact ? "py-4" : "py-8"
|
|
3195
|
+
),
|
|
3196
|
+
children: emptyMessage
|
|
3197
|
+
}
|
|
3198
|
+
) }) : table.getRowModel().rows.map((row) => {
|
|
3199
|
+
const rowId = getRowId ? getRowId(row.original) : void 0;
|
|
3200
|
+
const isSelected = selectedRowId != null && rowId != null && rowId === selectedRowId;
|
|
3201
|
+
return /* @__PURE__ */ jsx33(
|
|
3202
|
+
TableRow,
|
|
3203
|
+
{
|
|
3204
|
+
className: cn(
|
|
3205
|
+
onRowClick && "cursor-pointer",
|
|
3206
|
+
isSelected && "bg-accent"
|
|
3207
|
+
),
|
|
3208
|
+
onClick: () => onRowClick?.(row.original),
|
|
3209
|
+
children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx33(TableCell, { className: cn(compact && "py-1.5"), children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))
|
|
3210
|
+
},
|
|
3211
|
+
row.id
|
|
3212
|
+
);
|
|
3213
|
+
}) })
|
|
3214
|
+
] }) }),
|
|
3215
|
+
showPagination && /* @__PURE__ */ jsxs20("div", { className: "flex items-center justify-between px-2", children: [
|
|
3216
|
+
/* @__PURE__ */ jsxs20("p", { className: "text-sm text-muted-foreground", children: [
|
|
3217
|
+
total,
|
|
3218
|
+
" registro",
|
|
3219
|
+
total !== 1 ? "s" : ""
|
|
3220
|
+
] }),
|
|
3221
|
+
/* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2", children: [
|
|
3222
|
+
/* @__PURE__ */ jsxs20(
|
|
3223
|
+
Button,
|
|
3224
|
+
{
|
|
3225
|
+
variant: "outline",
|
|
3226
|
+
size: "sm",
|
|
3227
|
+
onClick: () => onPageChange(page - 1),
|
|
3228
|
+
disabled: page <= 1,
|
|
3229
|
+
children: [
|
|
3230
|
+
/* @__PURE__ */ jsx33(ChevronLeft, { className: "h-4 w-4" }),
|
|
3231
|
+
"Anterior"
|
|
3232
|
+
]
|
|
3233
|
+
}
|
|
3234
|
+
),
|
|
3235
|
+
/* @__PURE__ */ jsxs20("span", { className: "text-sm text-muted-foreground", children: [
|
|
3236
|
+
page,
|
|
3237
|
+
" de ",
|
|
3238
|
+
totalPages
|
|
3239
|
+
] }),
|
|
3240
|
+
/* @__PURE__ */ jsxs20(
|
|
3241
|
+
Button,
|
|
3242
|
+
{
|
|
3243
|
+
variant: "outline",
|
|
3244
|
+
size: "sm",
|
|
3245
|
+
onClick: () => onPageChange(page + 1),
|
|
3246
|
+
disabled: page >= totalPages,
|
|
3247
|
+
children: [
|
|
3248
|
+
"Pr\xF3ximo",
|
|
3249
|
+
/* @__PURE__ */ jsx33(ChevronRight2, { className: "h-4 w-4" })
|
|
3250
|
+
]
|
|
3251
|
+
}
|
|
3252
|
+
)
|
|
3253
|
+
] })
|
|
3254
|
+
] })
|
|
3255
|
+
] });
|
|
3256
|
+
}
|
|
3257
|
+
|
|
3258
|
+
// src/components/contacts-table.tsx
|
|
3259
|
+
import { Pencil as Pencil2, Trash2 as Trash23, Search as Search3 } from "lucide-react";
|
|
3260
|
+
import { format as format3 } from "date-fns";
|
|
3261
|
+
import { ptBR as ptBR4 } from "date-fns/locale";
|
|
3262
|
+
import { toast as toast5 } from "sonner";
|
|
3263
|
+
import { Fragment as Fragment3, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
3264
|
+
function useColumns(onEdit, onDelete) {
|
|
3265
|
+
return [
|
|
3266
|
+
{
|
|
3267
|
+
accessorKey: "name",
|
|
3268
|
+
header: "Nome",
|
|
3269
|
+
cell: ({ row }) => /* @__PURE__ */ jsx34("span", { className: "font-medium", children: row.original.name }),
|
|
3270
|
+
sortingFn: (rowA, rowB) => rowA.original.name.toLowerCase().localeCompare(rowB.original.name.toLowerCase())
|
|
3271
|
+
},
|
|
3272
|
+
{
|
|
3273
|
+
accessorKey: "phone_number",
|
|
3274
|
+
header: "Telefone",
|
|
3275
|
+
cell: ({ row }) => row.original.phone_number || "\u2014",
|
|
3276
|
+
sortingFn: (rowA, rowB) => {
|
|
3277
|
+
const a = rowA.original.phone_number || "";
|
|
3278
|
+
const b = rowB.original.phone_number || "";
|
|
3279
|
+
return a.localeCompare(b);
|
|
3280
|
+
}
|
|
3281
|
+
},
|
|
3282
|
+
{
|
|
3283
|
+
accessorKey: "identifier",
|
|
3284
|
+
header: "Identificador",
|
|
3285
|
+
cell: ({ row }) => /* @__PURE__ */ jsx34("span", { className: "text-muted-foreground text-xs font-mono", children: row.original.identifier || "\u2014" }),
|
|
3286
|
+
sortingFn: (rowA, rowB) => {
|
|
3287
|
+
const a = rowA.original.identifier || "";
|
|
3288
|
+
const b = rowB.original.identifier || "";
|
|
3289
|
+
return a.localeCompare(b);
|
|
3290
|
+
}
|
|
3291
|
+
},
|
|
3292
|
+
{
|
|
3293
|
+
accessorKey: "datetime_add",
|
|
3294
|
+
header: "Criado em",
|
|
3295
|
+
cell: ({ row }) => /* @__PURE__ */ jsx34("span", { className: "text-muted-foreground text-sm", children: format3(new Date(row.original.datetime_add), "dd/MM/yyyy", { locale: ptBR4 }) })
|
|
3296
|
+
},
|
|
3297
|
+
{
|
|
3298
|
+
id: "actions",
|
|
3299
|
+
size: 80,
|
|
3300
|
+
enableSorting: false,
|
|
3301
|
+
cell: ({ row }) => /* @__PURE__ */ jsxs21("div", { className: "flex items-center gap-1", children: [
|
|
3302
|
+
/* @__PURE__ */ jsxs21(Tooltip, { children: [
|
|
3303
|
+
/* @__PURE__ */ jsx34(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx34(
|
|
3304
|
+
Button,
|
|
3305
|
+
{
|
|
3306
|
+
variant: "ghost",
|
|
3307
|
+
size: "icon",
|
|
3308
|
+
className: "h-8 w-8",
|
|
3309
|
+
onClick: () => onEdit(row.original),
|
|
3310
|
+
children: /* @__PURE__ */ jsx34(Pencil2, { className: "h-4 w-4" })
|
|
3311
|
+
}
|
|
3312
|
+
) }),
|
|
3313
|
+
/* @__PURE__ */ jsx34(TooltipContent, { children: "Editar" })
|
|
3314
|
+
] }),
|
|
3315
|
+
/* @__PURE__ */ jsxs21(Tooltip, { children: [
|
|
3316
|
+
/* @__PURE__ */ jsx34(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx34(
|
|
3317
|
+
Button,
|
|
3318
|
+
{
|
|
3319
|
+
variant: "ghost",
|
|
3320
|
+
size: "icon",
|
|
3321
|
+
className: "h-8 w-8 text-destructive hover:text-destructive",
|
|
3322
|
+
onClick: () => onDelete(row.original.id),
|
|
3323
|
+
children: /* @__PURE__ */ jsx34(Trash23, { className: "h-4 w-4" })
|
|
3324
|
+
}
|
|
3325
|
+
) }),
|
|
3326
|
+
/* @__PURE__ */ jsx34(TooltipContent, { children: "Excluir" })
|
|
3327
|
+
] })
|
|
3328
|
+
] })
|
|
3329
|
+
}
|
|
3330
|
+
];
|
|
3331
|
+
}
|
|
3332
|
+
function ContactsTable({ config }) {
|
|
3333
|
+
const [search, setSearch] = useState11("");
|
|
3334
|
+
const [page, setPage] = useState11(1);
|
|
3335
|
+
const queryParams = useMemo4(() => {
|
|
3336
|
+
const params = {
|
|
3337
|
+
limit: "15",
|
|
3338
|
+
page: String(page)
|
|
3339
|
+
};
|
|
3340
|
+
if (search) {
|
|
3341
|
+
params.search = search;
|
|
3342
|
+
}
|
|
3343
|
+
return params;
|
|
3344
|
+
}, [search, page]);
|
|
3345
|
+
const { data, isLoading } = useContacts(config, queryParams);
|
|
3346
|
+
const deleteContact = useDeleteContact(config);
|
|
3347
|
+
const [editContact, setEditContact] = useState11(null);
|
|
3348
|
+
const [deleteId, setDeleteId] = useState11(null);
|
|
3349
|
+
const contacts = data?.data || [];
|
|
3350
|
+
const total = data?.total || 0;
|
|
3351
|
+
const columns = useColumns(
|
|
3352
|
+
(contact) => setEditContact(contact),
|
|
3353
|
+
(id) => setDeleteId(id)
|
|
3354
|
+
);
|
|
3355
|
+
function handleDelete() {
|
|
3356
|
+
if (!deleteId) return;
|
|
3357
|
+
deleteContact.mutate(deleteId, {
|
|
3358
|
+
onSuccess: () => {
|
|
3359
|
+
toast5.success("Contato exclu\xEDdo");
|
|
3360
|
+
setDeleteId(null);
|
|
3361
|
+
},
|
|
3362
|
+
onError: () => toast5.error("Erro ao excluir contato")
|
|
3363
|
+
});
|
|
3364
|
+
}
|
|
3365
|
+
function handleSearchChange(value) {
|
|
3366
|
+
setSearch(value);
|
|
3367
|
+
setPage(1);
|
|
3368
|
+
}
|
|
3369
|
+
return /* @__PURE__ */ jsxs21(Fragment3, { children: [
|
|
3370
|
+
/* @__PURE__ */ jsx34("div", { className: "flex items-center gap-3", children: /* @__PURE__ */ jsxs21("div", { className: "relative flex-1 max-w-md", children: [
|
|
3371
|
+
/* @__PURE__ */ jsx34(Search3, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }),
|
|
3372
|
+
/* @__PURE__ */ jsx34(
|
|
3373
|
+
Input,
|
|
3374
|
+
{
|
|
3375
|
+
placeholder: "Buscar por nome, telefone ou identificador...",
|
|
3376
|
+
value: search,
|
|
3377
|
+
onChange: (e) => handleSearchChange(e.target.value),
|
|
3378
|
+
className: "pl-9"
|
|
3379
|
+
}
|
|
3380
|
+
)
|
|
3381
|
+
] }) }),
|
|
3382
|
+
/* @__PURE__ */ jsx34(
|
|
3383
|
+
DataTable,
|
|
3384
|
+
{
|
|
3385
|
+
columns,
|
|
3386
|
+
data: contacts,
|
|
3387
|
+
isLoading,
|
|
3388
|
+
emptyMessage: "Nenhum contato encontrado",
|
|
3389
|
+
total,
|
|
3390
|
+
page,
|
|
3391
|
+
onPageChange: setPage,
|
|
3392
|
+
pageSize: 15
|
|
3393
|
+
}
|
|
3394
|
+
),
|
|
3395
|
+
/* @__PURE__ */ jsx34(
|
|
3396
|
+
ContactFormDialog,
|
|
3397
|
+
{
|
|
3398
|
+
open: !!editContact,
|
|
3399
|
+
onOpenChange: (open) => !open && setEditContact(null),
|
|
3400
|
+
contact: editContact ?? void 0,
|
|
3401
|
+
config
|
|
3402
|
+
}
|
|
3403
|
+
),
|
|
3404
|
+
/* @__PURE__ */ jsx34(AlertDialog, { open: !!deleteId, onOpenChange: (open) => !open && setDeleteId(null), children: /* @__PURE__ */ jsxs21(AlertDialogContent, { children: [
|
|
3405
|
+
/* @__PURE__ */ jsxs21(AlertDialogHeader, { children: [
|
|
3406
|
+
/* @__PURE__ */ jsx34(AlertDialogTitle, { children: "Excluir contato?" }),
|
|
3407
|
+
/* @__PURE__ */ jsx34(AlertDialogDescription, { children: "Esta a\xE7\xE3o n\xE3o pode ser desfeita. O contato ser\xE1 removido permanentemente." })
|
|
3408
|
+
] }),
|
|
3409
|
+
/* @__PURE__ */ jsxs21(AlertDialogFooter, { children: [
|
|
3410
|
+
/* @__PURE__ */ jsx34(AlertDialogCancel, { children: "Cancelar" }),
|
|
3411
|
+
/* @__PURE__ */ jsx34(
|
|
3412
|
+
AlertDialogAction,
|
|
3413
|
+
{
|
|
3414
|
+
onClick: handleDelete,
|
|
3415
|
+
className: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
3416
|
+
children: "Excluir"
|
|
3417
|
+
}
|
|
3418
|
+
)
|
|
3419
|
+
] })
|
|
3420
|
+
] }) })
|
|
3421
|
+
] });
|
|
3422
|
+
}
|
|
3423
|
+
|
|
3424
|
+
// src/components/inbox-page.tsx
|
|
3425
|
+
import { useState as useState12, useCallback as useCallback3 } from "react";
|
|
3426
|
+
import { MessageCircle, User } from "lucide-react";
|
|
3427
|
+
import { Fragment as Fragment4, jsx as jsx35, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
3428
|
+
function InboxPage({ config, filterChannelId }) {
|
|
3429
|
+
const [selectedInbox, setSelectedInbox] = useState12(null);
|
|
3430
|
+
const [showContactPanel, setShowContactPanel] = useState12(false);
|
|
3431
|
+
const { data: inboxes, isLoading: inboxesLoading } = useInboxes(config);
|
|
3432
|
+
const { data: messages = [], isLoading: messagesLoading } = useInboxMessages(config, selectedInbox?.id ?? null);
|
|
3433
|
+
const { data: contact, isLoading: contactLoading } = useGetContact(
|
|
3434
|
+
config,
|
|
3435
|
+
showContactPanel && selectedInbox?.id_contact ? selectedInbox.id_contact : null
|
|
3436
|
+
);
|
|
3437
|
+
const sendMessage = useSendMessage(config);
|
|
3438
|
+
const retryMessage = useRetryMessage(config);
|
|
3439
|
+
const revokeMessage = useRevokeMessage(config);
|
|
3440
|
+
const editMessage = useEditMessage(config);
|
|
3441
|
+
const updateInbox = useUpdateInbox(config);
|
|
3442
|
+
const deleteInbox = useDeleteInbox(config);
|
|
3443
|
+
const handleSend = useCallback3(
|
|
3444
|
+
(content) => {
|
|
3445
|
+
if (!selectedInbox) return;
|
|
3446
|
+
sendMessage.mutate({ idInbox: selectedInbox.id, content });
|
|
3447
|
+
},
|
|
3448
|
+
[selectedInbox, sendMessage]
|
|
3449
|
+
);
|
|
3450
|
+
const handleStatusChange = useCallback3(
|
|
3451
|
+
(status) => {
|
|
3452
|
+
if (!selectedInbox) return;
|
|
3453
|
+
updateInbox.mutate({ id: selectedInbox.id, body: { status } });
|
|
3454
|
+
},
|
|
3455
|
+
[selectedInbox, updateInbox]
|
|
3456
|
+
);
|
|
3457
|
+
const handleRevoke = useCallback3(
|
|
3458
|
+
(message) => {
|
|
3459
|
+
if (!selectedInbox) return;
|
|
3460
|
+
revokeMessage.mutate({ id: message.id, idInbox: selectedInbox.id });
|
|
3461
|
+
},
|
|
3462
|
+
[selectedInbox, revokeMessage]
|
|
3463
|
+
);
|
|
3464
|
+
const handleEdit = useCallback3(
|
|
3465
|
+
(message, newContent) => {
|
|
3466
|
+
if (!selectedInbox) return;
|
|
3467
|
+
editMessage.mutate({
|
|
3468
|
+
id: message.id,
|
|
3469
|
+
idInbox: selectedInbox.id,
|
|
3470
|
+
content: newContent
|
|
3471
|
+
});
|
|
3472
|
+
},
|
|
3473
|
+
[selectedInbox, editMessage]
|
|
3474
|
+
);
|
|
3475
|
+
return /* @__PURE__ */ jsxs22("div", { className: "flex h-full", children: [
|
|
3476
|
+
/* @__PURE__ */ jsx35("div", { className: "w-[360px] shrink-0 border-r flex flex-col", children: /* @__PURE__ */ jsx35(
|
|
3477
|
+
InboxSidebar,
|
|
3478
|
+
{
|
|
3479
|
+
inboxes,
|
|
3480
|
+
isLoading: inboxesLoading,
|
|
3481
|
+
selectedInboxId: selectedInbox?.id ?? null,
|
|
3482
|
+
onSelectInbox: (inbox) => {
|
|
3483
|
+
setSelectedInbox(inbox);
|
|
3484
|
+
setShowContactPanel(false);
|
|
3485
|
+
},
|
|
3486
|
+
onDeleteInbox: (id) => deleteInbox.mutate(id),
|
|
3487
|
+
filterChannelId
|
|
3488
|
+
}
|
|
3489
|
+
) }),
|
|
3490
|
+
/* @__PURE__ */ jsx35("div", { className: "flex-1 flex min-w-0", children: selectedInbox ? /* @__PURE__ */ jsxs22(Fragment4, { children: [
|
|
3491
|
+
/* @__PURE__ */ jsx35("div", { className: "flex-1 flex flex-col min-w-0", children: /* @__PURE__ */ jsx35(
|
|
3492
|
+
ChatView,
|
|
3493
|
+
{
|
|
3494
|
+
messages,
|
|
3495
|
+
isLoading: messagesLoading,
|
|
3496
|
+
onSend: handleSend,
|
|
3497
|
+
onRetry: retryMessage,
|
|
3498
|
+
onRevoke: handleRevoke,
|
|
3499
|
+
onEdit: handleEdit,
|
|
3500
|
+
renderHeader: /* @__PURE__ */ jsxs22("div", { className: "flex items-center justify-between border-b px-4 py-3", children: [
|
|
3501
|
+
/* @__PURE__ */ jsx35("span", { className: "text-sm font-medium", children: selectedInbox.contact_name || "Conversa" }),
|
|
3502
|
+
/* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-2", children: [
|
|
3503
|
+
/* @__PURE__ */ jsxs22(
|
|
3504
|
+
Select,
|
|
3505
|
+
{
|
|
3506
|
+
value: selectedInbox.status,
|
|
3507
|
+
onValueChange: handleStatusChange,
|
|
3508
|
+
children: [
|
|
3509
|
+
/* @__PURE__ */ jsx35(SelectTrigger, { className: "w-[130px] h-8 text-xs", children: /* @__PURE__ */ jsx35(SelectValue, { placeholder: "Status" }) }),
|
|
3510
|
+
/* @__PURE__ */ jsxs22(SelectContent, { children: [
|
|
3511
|
+
/* @__PURE__ */ jsx35(SelectItem, { value: "open", children: "Aberta" }),
|
|
3512
|
+
/* @__PURE__ */ jsx35(SelectItem, { value: "pending", children: "Pendente" }),
|
|
3513
|
+
/* @__PURE__ */ jsx35(SelectItem, { value: "resolved", children: "Resolvida" })
|
|
3514
|
+
] })
|
|
3515
|
+
]
|
|
3516
|
+
}
|
|
3517
|
+
),
|
|
3518
|
+
selectedInbox.id_contact && /* @__PURE__ */ jsx35(
|
|
3519
|
+
Button,
|
|
3520
|
+
{
|
|
3521
|
+
variant: "ghost",
|
|
3522
|
+
size: "icon",
|
|
3523
|
+
className: "h-8 w-8",
|
|
3524
|
+
onClick: () => setShowContactPanel((v) => !v),
|
|
3525
|
+
children: /* @__PURE__ */ jsx35(User, { className: "h-4 w-4" })
|
|
3526
|
+
}
|
|
3527
|
+
)
|
|
3528
|
+
] })
|
|
3529
|
+
] })
|
|
3530
|
+
}
|
|
3531
|
+
) }),
|
|
3532
|
+
showContactPanel && selectedInbox.id_contact && /* @__PURE__ */ jsx35(
|
|
3533
|
+
ContactInfoPanel,
|
|
3534
|
+
{
|
|
3535
|
+
contact: contact ?? null,
|
|
3536
|
+
isLoading: contactLoading,
|
|
3537
|
+
onClose: () => setShowContactPanel(false)
|
|
3538
|
+
}
|
|
3539
|
+
)
|
|
3540
|
+
] }) : /* @__PURE__ */ jsxs22("div", { className: "flex-1 flex flex-col items-center justify-center text-muted-foreground gap-3", children: [
|
|
3541
|
+
/* @__PURE__ */ jsx35(MessageCircle, { className: "h-12 w-12 opacity-20" }),
|
|
3542
|
+
/* @__PURE__ */ jsx35("p", { className: "text-sm", children: "Selecione uma conversa para come\xE7ar" })
|
|
3543
|
+
] }) })
|
|
3544
|
+
] });
|
|
3545
|
+
}
|
|
3546
|
+
|
|
3547
|
+
// src/components/channels-page.tsx
|
|
3548
|
+
import { useState as useState13 } from "react";
|
|
3549
|
+
import { Plus as Plus2, QrCode, Unplug, LogOut } from "lucide-react";
|
|
3550
|
+
import { toast as toast6 } from "sonner";
|
|
3551
|
+
import { Fragment as Fragment5, jsx as jsx36, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
3552
|
+
function ChannelCardWrapper({
|
|
3553
|
+
channel,
|
|
3554
|
+
config,
|
|
3555
|
+
renderAgentSelect,
|
|
3556
|
+
getAgentInfo
|
|
3557
|
+
}) {
|
|
3558
|
+
const { data: status } = useChannelWhatsappStatus(config, channel.id);
|
|
3559
|
+
const disconnectChannel = useDisconnectChannel(config);
|
|
3560
|
+
const logoutChannel = useLogoutChannel(config);
|
|
3561
|
+
const deleteChannel = useDeleteChannel(config);
|
|
3562
|
+
const [qrOpen, setQrOpen] = useState13(false);
|
|
3563
|
+
const [editOpen, setEditOpen] = useState13(false);
|
|
3564
|
+
const [deleteOpen, setDeleteOpen] = useState13(false);
|
|
3565
|
+
const isFullyConnected = status?.connected === true && status?.logged_in === true;
|
|
3566
|
+
const hasSession = !!channel.external_id;
|
|
3567
|
+
const agentInfo = getAgentInfo?.(channel.id_agent);
|
|
3568
|
+
function handleDisconnect() {
|
|
3569
|
+
disconnectChannel.mutate(channel.id, {
|
|
3570
|
+
onSuccess: () => toast6.success("Canal desconectado"),
|
|
3571
|
+
onError: () => toast6.error("Erro ao desconectar")
|
|
3572
|
+
});
|
|
3573
|
+
}
|
|
3574
|
+
function handleLogout() {
|
|
3575
|
+
logoutChannel.mutate(channel.id, {
|
|
3576
|
+
onSuccess: () => toast6.success("Logout realizado. Ser\xE1 necess\xE1rio novo QR code."),
|
|
3577
|
+
onError: () => toast6.error("Erro ao fazer logout")
|
|
3578
|
+
});
|
|
3579
|
+
}
|
|
3580
|
+
function handleDelete() {
|
|
3581
|
+
deleteChannel.mutate(channel.id, {
|
|
3582
|
+
onSuccess: () => {
|
|
3583
|
+
toast6.success("Canal exclu\xEDdo");
|
|
3584
|
+
setDeleteOpen(false);
|
|
3585
|
+
},
|
|
3586
|
+
onError: () => toast6.error("Erro ao excluir canal")
|
|
3587
|
+
});
|
|
3588
|
+
}
|
|
3589
|
+
return /* @__PURE__ */ jsxs23(Fragment5, { children: [
|
|
3590
|
+
/* @__PURE__ */ jsx36(
|
|
3591
|
+
ChannelCard,
|
|
3592
|
+
{
|
|
3593
|
+
channel,
|
|
3594
|
+
config,
|
|
3595
|
+
onEdit: () => setEditOpen(true),
|
|
3596
|
+
onDelete: () => setDeleteOpen(true),
|
|
3597
|
+
linkedAgentName: agentInfo?.name,
|
|
3598
|
+
linkedAgentActive: agentInfo?.active !== false,
|
|
3599
|
+
actions: /* @__PURE__ */ jsxs23("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
3600
|
+
!isFullyConnected && /* @__PURE__ */ jsxs23(
|
|
3601
|
+
Button,
|
|
3602
|
+
{
|
|
3603
|
+
size: "sm",
|
|
3604
|
+
variant: "outline",
|
|
3605
|
+
onClick: () => setQrOpen(true),
|
|
3606
|
+
children: [
|
|
3607
|
+
/* @__PURE__ */ jsx36(QrCode, { className: "mr-1.5 h-3.5 w-3.5" }),
|
|
3608
|
+
"Conectar via QR"
|
|
3609
|
+
]
|
|
3610
|
+
}
|
|
3611
|
+
),
|
|
3612
|
+
isFullyConnected && /* @__PURE__ */ jsxs23(
|
|
3613
|
+
Button,
|
|
3614
|
+
{
|
|
3615
|
+
size: "sm",
|
|
3616
|
+
variant: "outline",
|
|
3617
|
+
onClick: handleDisconnect,
|
|
3618
|
+
disabled: disconnectChannel.isPending,
|
|
3619
|
+
children: [
|
|
3620
|
+
/* @__PURE__ */ jsx36(Unplug, { className: "mr-1.5 h-3.5 w-3.5" }),
|
|
3621
|
+
"Desconectar"
|
|
3622
|
+
]
|
|
3623
|
+
}
|
|
3624
|
+
),
|
|
3625
|
+
hasSession && /* @__PURE__ */ jsxs23(
|
|
3626
|
+
Button,
|
|
3627
|
+
{
|
|
3628
|
+
size: "sm",
|
|
3629
|
+
variant: "outline",
|
|
3630
|
+
className: "text-destructive hover:text-destructive",
|
|
3631
|
+
onClick: handleLogout,
|
|
3632
|
+
disabled: logoutChannel.isPending,
|
|
3633
|
+
children: [
|
|
3634
|
+
/* @__PURE__ */ jsx36(LogOut, { className: "mr-1.5 h-3.5 w-3.5" }),
|
|
3635
|
+
"Logout"
|
|
3636
|
+
]
|
|
3637
|
+
}
|
|
3638
|
+
)
|
|
3639
|
+
] })
|
|
3640
|
+
}
|
|
3641
|
+
),
|
|
3642
|
+
/* @__PURE__ */ jsx36(
|
|
3643
|
+
WhatsappQrDialog,
|
|
3644
|
+
{
|
|
3645
|
+
open: qrOpen,
|
|
3646
|
+
onOpenChange: setQrOpen,
|
|
3647
|
+
channelId: channel.id,
|
|
3648
|
+
config
|
|
3649
|
+
}
|
|
3650
|
+
),
|
|
3651
|
+
/* @__PURE__ */ jsx36(
|
|
3652
|
+
ChannelEditDialog,
|
|
3653
|
+
{
|
|
3654
|
+
open: editOpen,
|
|
3655
|
+
onOpenChange: setEditOpen,
|
|
3656
|
+
channel,
|
|
3657
|
+
config,
|
|
3658
|
+
renderAgentSelect
|
|
3659
|
+
}
|
|
3660
|
+
),
|
|
3661
|
+
/* @__PURE__ */ jsx36(AlertDialog, { open: deleteOpen, onOpenChange: setDeleteOpen, children: /* @__PURE__ */ jsxs23(AlertDialogContent, { children: [
|
|
3662
|
+
/* @__PURE__ */ jsxs23(AlertDialogHeader, { children: [
|
|
3663
|
+
/* @__PURE__ */ jsx36(AlertDialogTitle, { children: "Excluir canal?" }),
|
|
3664
|
+
/* @__PURE__ */ jsxs23(AlertDialogDescription, { children: [
|
|
3665
|
+
'O canal "',
|
|
3666
|
+
channel.name,
|
|
3667
|
+
'" ser\xE1 removido permanentemente. Todas as conversas associadas ser\xE3o perdidas.'
|
|
3668
|
+
] })
|
|
3669
|
+
] }),
|
|
3670
|
+
/* @__PURE__ */ jsxs23(AlertDialogFooter, { children: [
|
|
3671
|
+
/* @__PURE__ */ jsx36(AlertDialogCancel, { children: "Cancelar" }),
|
|
3672
|
+
/* @__PURE__ */ jsx36(
|
|
3673
|
+
AlertDialogAction,
|
|
3674
|
+
{
|
|
3675
|
+
className: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
3676
|
+
onClick: handleDelete,
|
|
3677
|
+
children: "Excluir"
|
|
3678
|
+
}
|
|
3679
|
+
)
|
|
3680
|
+
] })
|
|
3681
|
+
] }) })
|
|
3682
|
+
] });
|
|
3683
|
+
}
|
|
3684
|
+
function ChannelsPage({
|
|
3685
|
+
config,
|
|
3686
|
+
renderAgentSelect,
|
|
3687
|
+
getAgentInfo
|
|
3688
|
+
}) {
|
|
3689
|
+
const { data: channels, isLoading } = useChannels(config);
|
|
3690
|
+
const [createOpen, setCreateOpen] = useState13(false);
|
|
3691
|
+
return /* @__PURE__ */ jsxs23("div", { className: "flex flex-col gap-6 p-4 flex-1 min-h-0 overflow-y-auto", children: [
|
|
3692
|
+
/* @__PURE__ */ jsxs23("div", { className: "flex items-center justify-between", children: [
|
|
3693
|
+
/* @__PURE__ */ jsxs23("div", { children: [
|
|
3694
|
+
/* @__PURE__ */ jsx36("h1", { className: "text-xl font-semibold", children: "Canais" }),
|
|
3695
|
+
/* @__PURE__ */ jsx36("p", { className: "text-sm text-muted-foreground", children: "Gerencie seus canais de comunica\xE7\xE3o" })
|
|
3696
|
+
] }),
|
|
3697
|
+
/* @__PURE__ */ jsxs23(Button, { onClick: () => setCreateOpen(true), children: [
|
|
3698
|
+
/* @__PURE__ */ jsx36(Plus2, { className: "mr-2 h-4 w-4" }),
|
|
3699
|
+
"Novo Canal"
|
|
3700
|
+
] })
|
|
3701
|
+
] }),
|
|
3702
|
+
isLoading ? /* @__PURE__ */ jsx36("div", { className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsxs23("div", { className: "rounded-lg border p-4 space-y-3", children: [
|
|
3703
|
+
/* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-2", children: [
|
|
3704
|
+
/* @__PURE__ */ jsx36(Skeleton, { className: "h-9 w-9 rounded-md" }),
|
|
3705
|
+
/* @__PURE__ */ jsxs23("div", { className: "space-y-1", children: [
|
|
3706
|
+
/* @__PURE__ */ jsx36(Skeleton, { className: "h-4 w-24" }),
|
|
3707
|
+
/* @__PURE__ */ jsx36(Skeleton, { className: "h-3 w-32" })
|
|
3708
|
+
] })
|
|
3709
|
+
] }),
|
|
3710
|
+
/* @__PURE__ */ jsx36(Skeleton, { className: "h-8 w-full" })
|
|
3711
|
+
] }, i)) }) : !channels?.length ? /* @__PURE__ */ jsxs23("div", { className: "flex flex-col items-center justify-center py-12 text-muted-foreground gap-3", children: [
|
|
3712
|
+
/* @__PURE__ */ jsx36(WhatsappIcon, { className: "h-12 w-12 opacity-20" }),
|
|
3713
|
+
/* @__PURE__ */ jsx36("p", { className: "text-sm", children: "Nenhum canal configurado" }),
|
|
3714
|
+
/* @__PURE__ */ jsxs23(Button, { variant: "outline", onClick: () => setCreateOpen(true), children: [
|
|
3715
|
+
/* @__PURE__ */ jsx36(Plus2, { className: "mr-2 h-4 w-4" }),
|
|
3716
|
+
"Criar primeiro canal"
|
|
3717
|
+
] })
|
|
3718
|
+
] }) : /* @__PURE__ */ jsx36("div", { className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3", children: channels.map((channel) => /* @__PURE__ */ jsx36(
|
|
3719
|
+
ChannelCardWrapper,
|
|
3720
|
+
{
|
|
3721
|
+
channel,
|
|
3722
|
+
config,
|
|
3723
|
+
renderAgentSelect,
|
|
3724
|
+
getAgentInfo
|
|
3725
|
+
},
|
|
3726
|
+
channel.id
|
|
3727
|
+
)) }),
|
|
3728
|
+
/* @__PURE__ */ jsx36(
|
|
3729
|
+
ChannelCreateDialog,
|
|
3730
|
+
{
|
|
3731
|
+
open: createOpen,
|
|
3732
|
+
onOpenChange: setCreateOpen,
|
|
3733
|
+
config,
|
|
3734
|
+
renderAgentSelect
|
|
3735
|
+
}
|
|
3736
|
+
)
|
|
3737
|
+
] });
|
|
3738
|
+
}
|
|
3739
|
+
|
|
3740
|
+
// src/components/contacts-page.tsx
|
|
3741
|
+
import { useState as useState14 } from "react";
|
|
3742
|
+
import { Plus as Plus3 } from "lucide-react";
|
|
3743
|
+
import { jsx as jsx37, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
3744
|
+
function ContactsPage({ config }) {
|
|
3745
|
+
const [createOpen, setCreateOpen] = useState14(false);
|
|
3746
|
+
return /* @__PURE__ */ jsxs24("div", { className: "flex flex-col gap-6 p-4 flex-1 min-h-0 overflow-y-auto", children: [
|
|
3747
|
+
/* @__PURE__ */ jsxs24("div", { className: "flex items-center justify-between", children: [
|
|
3748
|
+
/* @__PURE__ */ jsxs24("div", { children: [
|
|
3749
|
+
/* @__PURE__ */ jsx37("h1", { className: "text-xl font-semibold", children: "Contatos" }),
|
|
3750
|
+
/* @__PURE__ */ jsx37("p", { className: "text-sm text-muted-foreground", children: "Gerencie seus contatos de conversas" })
|
|
3751
|
+
] }),
|
|
3752
|
+
/* @__PURE__ */ jsxs24(Button, { onClick: () => setCreateOpen(true), children: [
|
|
3753
|
+
/* @__PURE__ */ jsx37(Plus3, { className: "mr-2 h-4 w-4" }),
|
|
3754
|
+
"Novo Contato"
|
|
3755
|
+
] })
|
|
3756
|
+
] }),
|
|
3757
|
+
/* @__PURE__ */ jsx37(ContactsTable, { config }),
|
|
3758
|
+
/* @__PURE__ */ jsx37(
|
|
3759
|
+
ContactFormDialog,
|
|
3760
|
+
{
|
|
3761
|
+
open: createOpen,
|
|
3762
|
+
onOpenChange: setCreateOpen,
|
|
3763
|
+
config
|
|
3764
|
+
}
|
|
3765
|
+
)
|
|
3766
|
+
] });
|
|
3767
|
+
}
|
|
3768
|
+
|
|
3769
|
+
// src/components/chat-dashboard.tsx
|
|
3770
|
+
import { useState as useState15, useMemo as useMemo5 } from "react";
|
|
3771
|
+
|
|
3772
|
+
// src/components/ui/card.tsx
|
|
3773
|
+
import { jsx as jsx38 } from "react/jsx-runtime";
|
|
3774
|
+
function Card({
|
|
3775
|
+
className,
|
|
3776
|
+
size = "default",
|
|
3777
|
+
...props
|
|
3778
|
+
}) {
|
|
3779
|
+
return /* @__PURE__ */ jsx38(
|
|
3780
|
+
"div",
|
|
3781
|
+
{
|
|
3782
|
+
"data-slot": "card",
|
|
3783
|
+
"data-size": size,
|
|
3784
|
+
className: cn("ring-foreground/10 bg-card text-card-foreground gap-6 overflow-hidden rounded-xl py-6 text-sm shadow-xs ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col", className),
|
|
3785
|
+
...props
|
|
3786
|
+
}
|
|
3787
|
+
);
|
|
3788
|
+
}
|
|
3789
|
+
function CardHeader({ className, ...props }) {
|
|
3790
|
+
return /* @__PURE__ */ jsx38(
|
|
3791
|
+
"div",
|
|
3792
|
+
{
|
|
3793
|
+
"data-slot": "card-header",
|
|
3794
|
+
className: cn(
|
|
3795
|
+
"gap-1 rounded-t-xl px-6 group-data-[size=sm]/card:px-4 [.border-b]:pb-6 group-data-[size=sm]/card:[.border-b]:pb-4 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]",
|
|
3796
|
+
className
|
|
3797
|
+
),
|
|
3798
|
+
...props
|
|
3799
|
+
}
|
|
3800
|
+
);
|
|
3801
|
+
}
|
|
3802
|
+
function CardTitle({ className, ...props }) {
|
|
3803
|
+
return /* @__PURE__ */ jsx38(
|
|
3804
|
+
"div",
|
|
3805
|
+
{
|
|
3806
|
+
"data-slot": "card-title",
|
|
3807
|
+
className: cn("text-base leading-normal font-medium group-data-[size=sm]/card:text-sm", className),
|
|
3808
|
+
...props
|
|
3809
|
+
}
|
|
3810
|
+
);
|
|
3811
|
+
}
|
|
3812
|
+
function CardContent({ className, ...props }) {
|
|
3813
|
+
return /* @__PURE__ */ jsx38(
|
|
3814
|
+
"div",
|
|
3815
|
+
{
|
|
3816
|
+
"data-slot": "card-content",
|
|
3817
|
+
className: cn("px-6 group-data-[size=sm]/card:px-4", className),
|
|
3818
|
+
...props
|
|
3819
|
+
}
|
|
3820
|
+
);
|
|
3821
|
+
}
|
|
3822
|
+
|
|
3823
|
+
// src/components/ui/progress.tsx
|
|
3824
|
+
import { Progress as ProgressPrimitive } from "radix-ui";
|
|
3825
|
+
import { jsx as jsx39 } from "react/jsx-runtime";
|
|
3826
|
+
function Progress({
|
|
3827
|
+
className,
|
|
3828
|
+
value,
|
|
3829
|
+
...props
|
|
3830
|
+
}) {
|
|
3831
|
+
return /* @__PURE__ */ jsx39(
|
|
3832
|
+
ProgressPrimitive.Root,
|
|
3833
|
+
{
|
|
3834
|
+
"data-slot": "progress",
|
|
3835
|
+
className: cn(
|
|
3836
|
+
"bg-muted h-1.5 rounded-full relative flex w-full items-center overflow-x-hidden",
|
|
3837
|
+
className
|
|
3838
|
+
),
|
|
3839
|
+
...props,
|
|
3840
|
+
children: /* @__PURE__ */ jsx39(
|
|
3841
|
+
ProgressPrimitive.Indicator,
|
|
3842
|
+
{
|
|
3843
|
+
"data-slot": "progress-indicator",
|
|
3844
|
+
className: "bg-primary size-full flex-1 transition-all",
|
|
3845
|
+
style: { transform: `translateX(-${100 - (value || 0)}%)` }
|
|
3846
|
+
}
|
|
3847
|
+
)
|
|
3848
|
+
}
|
|
3849
|
+
);
|
|
3850
|
+
}
|
|
3851
|
+
|
|
3852
|
+
// src/components/chat-dashboard.tsx
|
|
3853
|
+
import {
|
|
3854
|
+
Inbox as InboxIcon2,
|
|
3855
|
+
MessageCircle as MessageCircle2,
|
|
3856
|
+
Clock as Clock2,
|
|
3857
|
+
CircleCheck,
|
|
3858
|
+
ArrowRight
|
|
3859
|
+
} from "lucide-react";
|
|
3860
|
+
import { formatDistanceToNow as formatDistanceToNow2 } from "date-fns";
|
|
3861
|
+
import { ptBR as ptBR5 } from "date-fns/locale";
|
|
3862
|
+
import { Fragment as Fragment6, jsx as jsx40, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
3863
|
+
function getGreeting(userName) {
|
|
3864
|
+
const hour = (/* @__PURE__ */ new Date()).getHours();
|
|
3865
|
+
const prefix = hour < 12 ? "Bom dia" : hour < 18 ? "Boa tarde" : "Boa noite";
|
|
3866
|
+
return userName ? `${prefix}, ${userName}!` : `${prefix}!`;
|
|
3867
|
+
}
|
|
3868
|
+
var statusColors2 = {
|
|
3869
|
+
open: "bg-green-500/10 text-green-600 border-green-200",
|
|
3870
|
+
pending: "bg-yellow-500/10 text-yellow-600 border-yellow-200",
|
|
3871
|
+
resolved: "bg-zinc-500/10 text-zinc-500 border-zinc-200"
|
|
3872
|
+
};
|
|
3873
|
+
var statusLabels2 = {
|
|
3874
|
+
open: "Aberta",
|
|
3875
|
+
pending: "Pendente",
|
|
3876
|
+
resolved: "Resolvida"
|
|
3877
|
+
};
|
|
3878
|
+
function StatCard({
|
|
3879
|
+
title,
|
|
3880
|
+
value,
|
|
3881
|
+
total,
|
|
3882
|
+
icon: Icon,
|
|
3883
|
+
loading,
|
|
3884
|
+
accentColor,
|
|
3885
|
+
onClick
|
|
3886
|
+
}) {
|
|
3887
|
+
const pct = total && value ? Math.round(value / total * 100) : 0;
|
|
3888
|
+
return /* @__PURE__ */ jsxs25(
|
|
3889
|
+
Card,
|
|
3890
|
+
{
|
|
3891
|
+
className: cn("cursor-pointer transition-shadow hover:shadow-md border-l-4", accentColor),
|
|
3892
|
+
onClick,
|
|
3893
|
+
children: [
|
|
3894
|
+
/* @__PURE__ */ jsxs25(CardHeader, { className: "flex flex-row items-center justify-between pb-2", children: [
|
|
3895
|
+
/* @__PURE__ */ jsx40(CardTitle, { className: "text-sm font-medium text-muted-foreground", children: title }),
|
|
3896
|
+
/* @__PURE__ */ jsx40(Icon, { className: "h-4 w-4 text-muted-foreground" })
|
|
3897
|
+
] }),
|
|
3898
|
+
/* @__PURE__ */ jsx40(CardContent, { className: "space-y-2", children: loading ? /* @__PURE__ */ jsx40(Skeleton, { className: "h-8 w-16" }) : /* @__PURE__ */ jsxs25(Fragment6, { children: [
|
|
3899
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex items-baseline gap-2", children: [
|
|
3900
|
+
/* @__PURE__ */ jsx40("p", { className: "text-2xl font-bold", children: value ?? 0 }),
|
|
3901
|
+
total !== void 0 && total > 0 && title !== "Total" && /* @__PURE__ */ jsxs25("span", { className: "text-xs text-muted-foreground", children: [
|
|
3902
|
+
"de ",
|
|
3903
|
+
total
|
|
3904
|
+
] })
|
|
3905
|
+
] }),
|
|
3906
|
+
title !== "Total" && /* @__PURE__ */ jsx40(Progress, { value: pct, className: "h-1" })
|
|
3907
|
+
] }) })
|
|
3908
|
+
]
|
|
3909
|
+
}
|
|
3910
|
+
);
|
|
3911
|
+
}
|
|
3912
|
+
function ChannelHealthCard({
|
|
3913
|
+
channel,
|
|
3914
|
+
config,
|
|
3915
|
+
onClick
|
|
3916
|
+
}) {
|
|
3917
|
+
const { data: status } = useChannelWhatsappStatus(config, channel.id);
|
|
3918
|
+
return /* @__PURE__ */ jsxs25(
|
|
3919
|
+
"div",
|
|
3920
|
+
{
|
|
3921
|
+
className: "flex items-center gap-3 rounded-lg border p-3 cursor-pointer transition-colors hover:bg-accent",
|
|
3922
|
+
onClick,
|
|
3923
|
+
children: [
|
|
3924
|
+
/* @__PURE__ */ jsx40("div", { className: "flex h-9 w-9 items-center justify-center rounded-md bg-green-500/10", children: /* @__PURE__ */ jsx40(WhatsappIcon, { className: "h-5 w-5 text-green-600" }) }),
|
|
3925
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex-1 min-w-0", children: [
|
|
3926
|
+
/* @__PURE__ */ jsx40("p", { className: "text-sm font-medium truncate", children: channel.name }),
|
|
3927
|
+
/* @__PURE__ */ jsx40("p", { className: "text-xs text-muted-foreground truncate", children: channel.identifier || "Sem n\xFAmero" })
|
|
3928
|
+
] }),
|
|
3929
|
+
/* @__PURE__ */ jsx40(WhatsappStatusBadge, { status, hasSession: !!channel.external_id })
|
|
3930
|
+
]
|
|
3931
|
+
}
|
|
3932
|
+
);
|
|
3933
|
+
}
|
|
3934
|
+
function RecentConversationItem({
|
|
3935
|
+
inbox,
|
|
3936
|
+
onClick
|
|
3937
|
+
}) {
|
|
3938
|
+
const timestamp = inbox.last_message_at || inbox.datetime_add;
|
|
3939
|
+
const timeAgo = formatDistanceToNow2(new Date(timestamp), {
|
|
3940
|
+
addSuffix: true,
|
|
3941
|
+
locale: ptBR5
|
|
3942
|
+
});
|
|
3943
|
+
const lastMessage = inbox.last_message_content_type === "text" ? inbox.last_message_content : inbox.last_message_content_type ? `[${inbox.last_message_content_type}]` : null;
|
|
3944
|
+
const directionPrefix = inbox.last_message_direction === "outbound" ? "Voc\xEA: " : "";
|
|
3945
|
+
return /* @__PURE__ */ jsxs25(
|
|
3946
|
+
"div",
|
|
3947
|
+
{
|
|
3948
|
+
className: "flex items-center gap-3 rounded-lg p-3 cursor-pointer transition-colors hover:bg-accent",
|
|
3949
|
+
onClick,
|
|
3950
|
+
children: [
|
|
3951
|
+
/* @__PURE__ */ jsx40(ContactAvatar, { name: inbox.contact_name || "?", className: "h-9 w-9" }),
|
|
3952
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex-1 min-w-0", children: [
|
|
3953
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex items-center justify-between gap-2", children: [
|
|
3954
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-2 min-w-0", children: [
|
|
3955
|
+
/* @__PURE__ */ jsx40("span", { className: "text-sm font-medium truncate", children: inbox.contact_name || "Desconhecido" }),
|
|
3956
|
+
inbox.channel_name && /* @__PURE__ */ jsx40(Badge, { variant: "outline", className: "shrink-0 text-[10px] px-1.5 py-0 text-muted-foreground", children: inbox.channel_name })
|
|
3957
|
+
] }),
|
|
3958
|
+
/* @__PURE__ */ jsx40("span", { className: "text-xs text-muted-foreground shrink-0", children: timeAgo })
|
|
3959
|
+
] }),
|
|
3960
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex items-center justify-between gap-2 mt-0.5", children: [
|
|
3961
|
+
/* @__PURE__ */ jsx40("p", { className: "text-xs text-muted-foreground truncate", children: lastMessage ? `${directionPrefix}${lastMessage}` : "Sem mensagens" }),
|
|
3962
|
+
/* @__PURE__ */ jsx40(
|
|
3963
|
+
Badge,
|
|
3964
|
+
{
|
|
3965
|
+
variant: "outline",
|
|
3966
|
+
className: cn("shrink-0 text-[10px] px-1.5 py-0", statusColors2[inbox.status]),
|
|
3967
|
+
children: statusLabels2[inbox.status] || inbox.status
|
|
3968
|
+
}
|
|
3969
|
+
)
|
|
3970
|
+
] })
|
|
3971
|
+
] })
|
|
3972
|
+
]
|
|
3973
|
+
}
|
|
3974
|
+
);
|
|
3975
|
+
}
|
|
3976
|
+
function ChatDashboard({
|
|
3977
|
+
config,
|
|
3978
|
+
userName,
|
|
3979
|
+
onNavigateToInbox,
|
|
3980
|
+
onNavigateToChannels,
|
|
3981
|
+
renderExtraSection
|
|
3982
|
+
}) {
|
|
3983
|
+
const [selectedChannelId, setSelectedChannelId] = useState15(null);
|
|
3984
|
+
const { data: stats, isLoading: statsLoading } = useInboxStats(config);
|
|
3985
|
+
const { data: inboxes, isLoading: inboxesLoading } = useInboxes(config);
|
|
3986
|
+
const { data: channels, isLoading: channelsLoading } = useChannels(config);
|
|
3987
|
+
const filteredInboxes = useMemo5(() => {
|
|
3988
|
+
if (!inboxes) return [];
|
|
3989
|
+
if (!selectedChannelId) return inboxes;
|
|
3990
|
+
return inboxes.filter((i) => i.id_channel === selectedChannelId);
|
|
3991
|
+
}, [inboxes, selectedChannelId]);
|
|
3992
|
+
const filteredStats = useMemo5(() => {
|
|
3993
|
+
if (!selectedChannelId) return stats;
|
|
3994
|
+
const open = filteredInboxes.filter((i) => i.status === "open").length;
|
|
3995
|
+
const pending = filteredInboxes.filter((i) => i.status === "pending").length;
|
|
3996
|
+
const resolved = filteredInboxes.filter((i) => i.status === "resolved").length;
|
|
3997
|
+
return { total: filteredInboxes.length, open, pending, resolved };
|
|
3998
|
+
}, [stats, filteredInboxes, selectedChannelId]);
|
|
3999
|
+
const recentInboxes = useMemo5(() => {
|
|
4000
|
+
return [...filteredInboxes].sort((a, b) => {
|
|
4001
|
+
const da = a.last_message_at || a.datetime_add;
|
|
4002
|
+
const db = b.last_message_at || b.datetime_add;
|
|
4003
|
+
return new Date(db).getTime() - new Date(da).getTime();
|
|
4004
|
+
}).slice(0, 5);
|
|
4005
|
+
}, [filteredInboxes]);
|
|
4006
|
+
return /* @__PURE__ */ jsxs25("div", { className: "flex flex-col gap-6 p-4 flex-1 min-h-0 overflow-y-auto", children: [
|
|
4007
|
+
/* @__PURE__ */ jsxs25("div", { children: [
|
|
4008
|
+
/* @__PURE__ */ jsx40("h1", { className: "text-xl font-semibold", children: getGreeting(userName) }),
|
|
4009
|
+
/* @__PURE__ */ jsx40("p", { className: "text-sm text-muted-foreground", children: "Aqui est\xE1 o resumo das suas conversas e canais." })
|
|
4010
|
+
] }),
|
|
4011
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-2 flex-wrap", children: [
|
|
4012
|
+
/* @__PURE__ */ jsx40(
|
|
4013
|
+
"button",
|
|
4014
|
+
{
|
|
4015
|
+
className: cn(
|
|
4016
|
+
"inline-flex items-center gap-1.5 rounded-full border px-3 py-1 text-xs font-medium transition-colors",
|
|
4017
|
+
!selectedChannelId ? "bg-primary text-primary-foreground border-primary" : "bg-background text-muted-foreground hover:bg-accent hover:text-accent-foreground"
|
|
4018
|
+
),
|
|
4019
|
+
onClick: () => setSelectedChannelId(null),
|
|
4020
|
+
children: "Todos os canais"
|
|
4021
|
+
}
|
|
4022
|
+
),
|
|
4023
|
+
channelsLoading ? /* @__PURE__ */ jsxs25(Fragment6, { children: [
|
|
4024
|
+
/* @__PURE__ */ jsx40(Skeleton, { className: "h-7 w-24 rounded-full" }),
|
|
4025
|
+
/* @__PURE__ */ jsx40(Skeleton, { className: "h-7 w-28 rounded-full" })
|
|
4026
|
+
] }) : channels?.map((channel) => /* @__PURE__ */ jsxs25(
|
|
4027
|
+
"button",
|
|
4028
|
+
{
|
|
4029
|
+
className: cn(
|
|
4030
|
+
"inline-flex items-center gap-1.5 rounded-full border px-3 py-1 text-xs font-medium transition-colors",
|
|
4031
|
+
selectedChannelId === channel.id ? "bg-primary text-primary-foreground border-primary" : "bg-background text-muted-foreground hover:bg-accent hover:text-accent-foreground"
|
|
4032
|
+
),
|
|
4033
|
+
onClick: () => setSelectedChannelId(channel.id),
|
|
4034
|
+
children: [
|
|
4035
|
+
/* @__PURE__ */ jsx40(WhatsappIcon, { className: "h-3 w-3" }),
|
|
4036
|
+
channel.name
|
|
4037
|
+
]
|
|
4038
|
+
},
|
|
4039
|
+
channel.id
|
|
4040
|
+
))
|
|
4041
|
+
] }),
|
|
4042
|
+
/* @__PURE__ */ jsxs25("div", { className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-4", children: [
|
|
4043
|
+
/* @__PURE__ */ jsx40(
|
|
4044
|
+
StatCard,
|
|
4045
|
+
{
|
|
4046
|
+
title: "Total",
|
|
4047
|
+
value: filteredStats?.total,
|
|
4048
|
+
total: filteredStats?.total,
|
|
4049
|
+
icon: InboxIcon2,
|
|
4050
|
+
loading: statsLoading && !selectedChannelId,
|
|
4051
|
+
accentColor: "border-l-blue-500",
|
|
4052
|
+
onClick: () => onNavigateToInbox?.()
|
|
4053
|
+
}
|
|
4054
|
+
),
|
|
4055
|
+
/* @__PURE__ */ jsx40(
|
|
4056
|
+
StatCard,
|
|
4057
|
+
{
|
|
4058
|
+
title: "Abertas",
|
|
4059
|
+
value: filteredStats?.open,
|
|
4060
|
+
total: filteredStats?.total,
|
|
4061
|
+
icon: MessageCircle2,
|
|
4062
|
+
loading: statsLoading && !selectedChannelId,
|
|
4063
|
+
accentColor: "border-l-green-500",
|
|
4064
|
+
onClick: () => onNavigateToInbox?.({ status: "open" })
|
|
4065
|
+
}
|
|
4066
|
+
),
|
|
4067
|
+
/* @__PURE__ */ jsx40(
|
|
4068
|
+
StatCard,
|
|
4069
|
+
{
|
|
4070
|
+
title: "Pendentes",
|
|
4071
|
+
value: filteredStats?.pending,
|
|
4072
|
+
total: filteredStats?.total,
|
|
4073
|
+
icon: Clock2,
|
|
4074
|
+
loading: statsLoading && !selectedChannelId,
|
|
4075
|
+
accentColor: "border-l-yellow-500",
|
|
4076
|
+
onClick: () => onNavigateToInbox?.({ status: "pending" })
|
|
4077
|
+
}
|
|
4078
|
+
),
|
|
4079
|
+
/* @__PURE__ */ jsx40(
|
|
4080
|
+
StatCard,
|
|
4081
|
+
{
|
|
4082
|
+
title: "Resolvidas",
|
|
4083
|
+
value: filteredStats?.resolved,
|
|
4084
|
+
total: filteredStats?.total,
|
|
4085
|
+
icon: CircleCheck,
|
|
4086
|
+
loading: statsLoading && !selectedChannelId,
|
|
4087
|
+
accentColor: "border-l-zinc-400",
|
|
4088
|
+
onClick: () => onNavigateToInbox?.({ status: "resolved" })
|
|
4089
|
+
}
|
|
4090
|
+
)
|
|
4091
|
+
] }),
|
|
4092
|
+
/* @__PURE__ */ jsxs25(Card, { children: [
|
|
4093
|
+
/* @__PURE__ */ jsxs25(CardHeader, { className: "flex flex-row items-center justify-between", children: [
|
|
4094
|
+
/* @__PURE__ */ jsx40(CardTitle, { className: "text-sm font-medium", children: "Sa\xFAde dos Canais" }),
|
|
4095
|
+
onNavigateToChannels && /* @__PURE__ */ jsxs25(
|
|
4096
|
+
"button",
|
|
4097
|
+
{
|
|
4098
|
+
onClick: onNavigateToChannels,
|
|
4099
|
+
className: "text-xs text-primary hover:underline flex items-center gap-1",
|
|
4100
|
+
children: [
|
|
4101
|
+
"Gerenciar",
|
|
4102
|
+
/* @__PURE__ */ jsx40(ArrowRight, { className: "h-3 w-3" })
|
|
4103
|
+
]
|
|
4104
|
+
}
|
|
4105
|
+
)
|
|
4106
|
+
] }),
|
|
4107
|
+
/* @__PURE__ */ jsx40(CardContent, { children: channelsLoading ? /* @__PURE__ */ jsx40("div", { className: "space-y-3", children: [1, 2].map((i) => /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-3 p-3", children: [
|
|
4108
|
+
/* @__PURE__ */ jsx40(Skeleton, { className: "h-8 w-8 rounded" }),
|
|
4109
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex-1 space-y-1", children: [
|
|
4110
|
+
/* @__PURE__ */ jsx40(Skeleton, { className: "h-4 w-24" }),
|
|
4111
|
+
/* @__PURE__ */ jsx40(Skeleton, { className: "h-3 w-32" })
|
|
4112
|
+
] })
|
|
4113
|
+
] }, i)) }) : !channels?.length ? /* @__PURE__ */ jsx40("div", { className: "flex items-center justify-center h-full text-sm text-muted-foreground py-8", children: "Nenhum canal configurado" }) : /* @__PURE__ */ jsx40("div", { className: "space-y-2", children: channels.map((channel) => /* @__PURE__ */ jsx40(
|
|
4114
|
+
ChannelHealthCard,
|
|
4115
|
+
{
|
|
4116
|
+
channel,
|
|
4117
|
+
config,
|
|
4118
|
+
onClick: () => onNavigateToInbox?.({ channelId: channel.id })
|
|
4119
|
+
},
|
|
4120
|
+
channel.id
|
|
4121
|
+
)) }) })
|
|
4122
|
+
] }),
|
|
4123
|
+
renderExtraSection?.(),
|
|
4124
|
+
/* @__PURE__ */ jsxs25(Card, { children: [
|
|
4125
|
+
/* @__PURE__ */ jsxs25(CardHeader, { className: "flex flex-row items-center justify-between", children: [
|
|
4126
|
+
/* @__PURE__ */ jsx40(CardTitle, { className: "text-sm font-medium", children: "Conversas Recentes" }),
|
|
4127
|
+
onNavigateToInbox && /* @__PURE__ */ jsxs25(
|
|
4128
|
+
"button",
|
|
4129
|
+
{
|
|
4130
|
+
onClick: () => onNavigateToInbox(),
|
|
4131
|
+
className: "text-xs text-primary hover:underline flex items-center gap-1",
|
|
4132
|
+
children: [
|
|
4133
|
+
"Ver todas",
|
|
4134
|
+
/* @__PURE__ */ jsx40(ArrowRight, { className: "h-3 w-3" })
|
|
4135
|
+
]
|
|
4136
|
+
}
|
|
4137
|
+
)
|
|
4138
|
+
] }),
|
|
4139
|
+
/* @__PURE__ */ jsx40(CardContent, { className: "p-0", children: inboxesLoading ? /* @__PURE__ */ jsx40("div", { className: "space-y-2 p-4", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-3 p-3", children: [
|
|
4140
|
+
/* @__PURE__ */ jsx40(Skeleton, { className: "h-9 w-9 rounded-full shrink-0" }),
|
|
4141
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex-1 space-y-1.5", children: [
|
|
4142
|
+
/* @__PURE__ */ jsx40(Skeleton, { className: "h-4 w-28" }),
|
|
4143
|
+
/* @__PURE__ */ jsx40(Skeleton, { className: "h-3 w-40" })
|
|
4144
|
+
] })
|
|
4145
|
+
] }, i)) }) : recentInboxes.length === 0 ? /* @__PURE__ */ jsx40("div", { className: "flex items-center justify-center py-8 text-sm text-muted-foreground", children: "Nenhuma conversa encontrada" }) : /* @__PURE__ */ jsx40("div", { className: "divide-y", children: recentInboxes.map((inbox) => /* @__PURE__ */ jsx40(
|
|
4146
|
+
RecentConversationItem,
|
|
4147
|
+
{
|
|
4148
|
+
inbox,
|
|
4149
|
+
onClick: () => onNavigateToInbox?.()
|
|
4150
|
+
},
|
|
4151
|
+
inbox.id
|
|
4152
|
+
)) }) })
|
|
4153
|
+
] })
|
|
4154
|
+
] });
|
|
4155
|
+
}
|
|
2477
4156
|
export {
|
|
4157
|
+
ChannelCard,
|
|
4158
|
+
ChannelCreateDialog,
|
|
4159
|
+
ChannelEditDialog,
|
|
4160
|
+
ChannelsPage,
|
|
4161
|
+
ChatDashboard,
|
|
2478
4162
|
ChatInput,
|
|
2479
4163
|
ChatView,
|
|
2480
4164
|
ContactAvatar,
|
|
4165
|
+
ContactFormDialog,
|
|
2481
4166
|
ContactInfoPanel,
|
|
4167
|
+
ContactsPage,
|
|
4168
|
+
ContactsTable,
|
|
2482
4169
|
DEFAULT_CHANNEL_STATUS_POLLING,
|
|
2483
4170
|
DEFAULT_INBOX_POLLING,
|
|
2484
4171
|
DEFAULT_MESSAGES_POLLING,
|
|
2485
4172
|
DEFAULT_QR_POLLING,
|
|
4173
|
+
DataTable,
|
|
2486
4174
|
InboxItem,
|
|
4175
|
+
InboxPage,
|
|
2487
4176
|
InboxSidebar,
|
|
2488
4177
|
MessageBubble,
|
|
2489
4178
|
NewConversationDialog,
|
|
4179
|
+
WhatsappIcon,
|
|
4180
|
+
WhatsappQrDialog,
|
|
4181
|
+
WhatsappStatusBadge,
|
|
2490
4182
|
cn,
|
|
2491
4183
|
createGchatClient,
|
|
2492
4184
|
formatDateGroup,
|