@greatapps/greatchat-ui 0.1.0 → 0.1.2
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 +283 -1
- package/dist/index.js +1472 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
- package/src/components/chat-input.tsx +54 -0
- package/src/components/chat-view.tsx +135 -0
- package/src/components/index.ts +8 -0
- package/src/components/message-bubble.tsx +394 -0
- package/src/components/ui/alert-dialog.tsx +167 -0
- package/src/components/ui/badge.tsx +44 -0
- package/src/components/ui/button.tsx +62 -0
- package/src/components/ui/dropdown-menu.tsx +173 -0
- package/src/components/ui/select.tsx +156 -0
- package/src/components/ui/skeleton.tsx +16 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/hooks/index.ts +14 -0
- package/src/hooks/types.ts +40 -0
- package/src/hooks/use-channels.ts +163 -0
- package/src/hooks/use-contacts.ts +94 -0
- package/src/hooks/use-inbox-messages.ts +405 -0
- package/src/hooks/use-inboxes.ts +127 -0
- package/src/index.ts +8 -0
- package/src/utils/format-date.ts +13 -0
- package/src/utils/group-messages.ts +22 -0
- package/src/utils/index.ts +2 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui";
|
|
3
|
+
import { Check, ChevronRight } from "lucide-react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "../../lib/utils";
|
|
6
|
+
|
|
7
|
+
function DropdownMenu({
|
|
8
|
+
...props
|
|
9
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
|
|
10
|
+
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function DropdownMenuTrigger({
|
|
14
|
+
...props
|
|
15
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
|
|
16
|
+
return (
|
|
17
|
+
<DropdownMenuPrimitive.Trigger
|
|
18
|
+
data-slot="dropdown-menu-trigger"
|
|
19
|
+
{...props}
|
|
20
|
+
/>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function DropdownMenuContent({
|
|
25
|
+
className,
|
|
26
|
+
align = "start",
|
|
27
|
+
sideOffset = 4,
|
|
28
|
+
...props
|
|
29
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
|
|
30
|
+
return (
|
|
31
|
+
<DropdownMenuPrimitive.Portal>
|
|
32
|
+
<DropdownMenuPrimitive.Content
|
|
33
|
+
data-slot="dropdown-menu-content"
|
|
34
|
+
sideOffset={sideOffset}
|
|
35
|
+
align={align}
|
|
36
|
+
className={cn(
|
|
37
|
+
"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 bg-popover text-popover-foreground min-w-32 rounded-md p-1 shadow-md ring-1 duration-100 z-50 overflow-x-hidden overflow-y-auto",
|
|
38
|
+
className,
|
|
39
|
+
)}
|
|
40
|
+
{...props}
|
|
41
|
+
/>
|
|
42
|
+
</DropdownMenuPrimitive.Portal>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function DropdownMenuGroup({
|
|
47
|
+
...props
|
|
48
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
|
|
49
|
+
return (
|
|
50
|
+
<DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function DropdownMenuItem({
|
|
55
|
+
className,
|
|
56
|
+
inset,
|
|
57
|
+
variant = "default",
|
|
58
|
+
...props
|
|
59
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
|
|
60
|
+
inset?: boolean;
|
|
61
|
+
variant?: "default" | "destructive";
|
|
62
|
+
}) {
|
|
63
|
+
return (
|
|
64
|
+
<DropdownMenuPrimitive.Item
|
|
65
|
+
data-slot="dropdown-menu-item"
|
|
66
|
+
data-inset={inset}
|
|
67
|
+
data-variant={variant}
|
|
68
|
+
className={cn(
|
|
69
|
+
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive gap-2 rounded-sm px-2 py-1.5 text-sm data-inset:pl-8 [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
70
|
+
className,
|
|
71
|
+
)}
|
|
72
|
+
{...props}
|
|
73
|
+
/>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function DropdownMenuCheckboxItem({
|
|
78
|
+
className,
|
|
79
|
+
children,
|
|
80
|
+
checked,
|
|
81
|
+
...props
|
|
82
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
|
|
83
|
+
return (
|
|
84
|
+
<DropdownMenuPrimitive.CheckboxItem
|
|
85
|
+
data-slot="dropdown-menu-checkbox-item"
|
|
86
|
+
className={cn(
|
|
87
|
+
"focus:bg-accent focus:text-accent-foreground gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
88
|
+
className,
|
|
89
|
+
)}
|
|
90
|
+
checked={checked}
|
|
91
|
+
{...props}
|
|
92
|
+
>
|
|
93
|
+
<span className="absolute right-2 flex items-center justify-center pointer-events-none">
|
|
94
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
95
|
+
<Check />
|
|
96
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
97
|
+
</span>
|
|
98
|
+
{children}
|
|
99
|
+
</DropdownMenuPrimitive.CheckboxItem>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function DropdownMenuSeparator({
|
|
104
|
+
className,
|
|
105
|
+
...props
|
|
106
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
|
|
107
|
+
return (
|
|
108
|
+
<DropdownMenuPrimitive.Separator
|
|
109
|
+
data-slot="dropdown-menu-separator"
|
|
110
|
+
className={cn("bg-border -mx-1 my-1 h-px", className)}
|
|
111
|
+
{...props}
|
|
112
|
+
/>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function DropdownMenuSub({
|
|
117
|
+
...props
|
|
118
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
|
|
119
|
+
return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function DropdownMenuSubTrigger({
|
|
123
|
+
className,
|
|
124
|
+
inset,
|
|
125
|
+
children,
|
|
126
|
+
...props
|
|
127
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
|
|
128
|
+
inset?: boolean;
|
|
129
|
+
}) {
|
|
130
|
+
return (
|
|
131
|
+
<DropdownMenuPrimitive.SubTrigger
|
|
132
|
+
data-slot="dropdown-menu-sub-trigger"
|
|
133
|
+
data-inset={inset}
|
|
134
|
+
className={cn(
|
|
135
|
+
"focus:bg-accent focus:text-accent-foreground data-open:bg-accent gap-2 rounded-sm px-2 py-1.5 text-sm data-inset:pl-8 [&_svg:not([class*='size-'])]:size-4 flex cursor-default items-center outline-hidden select-none [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
136
|
+
className,
|
|
137
|
+
)}
|
|
138
|
+
{...props}
|
|
139
|
+
>
|
|
140
|
+
{children}
|
|
141
|
+
<ChevronRight className="ml-auto" />
|
|
142
|
+
</DropdownMenuPrimitive.SubTrigger>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function DropdownMenuSubContent({
|
|
147
|
+
className,
|
|
148
|
+
...props
|
|
149
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
|
|
150
|
+
return (
|
|
151
|
+
<DropdownMenuPrimitive.SubContent
|
|
152
|
+
data-slot="dropdown-menu-sub-content"
|
|
153
|
+
className={cn(
|
|
154
|
+
"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 ring-foreground/10 bg-popover text-popover-foreground min-w-[96px] rounded-md p-1 shadow-lg ring-1 duration-100 z-50 overflow-hidden",
|
|
155
|
+
className,
|
|
156
|
+
)}
|
|
157
|
+
{...props}
|
|
158
|
+
/>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export {
|
|
163
|
+
DropdownMenu,
|
|
164
|
+
DropdownMenuTrigger,
|
|
165
|
+
DropdownMenuContent,
|
|
166
|
+
DropdownMenuGroup,
|
|
167
|
+
DropdownMenuItem,
|
|
168
|
+
DropdownMenuCheckboxItem,
|
|
169
|
+
DropdownMenuSeparator,
|
|
170
|
+
DropdownMenuSub,
|
|
171
|
+
DropdownMenuSubTrigger,
|
|
172
|
+
DropdownMenuSubContent,
|
|
173
|
+
};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Select as SelectPrimitive } from "radix-ui";
|
|
3
|
+
import { Check, ChevronDown, ChevronUp, ChevronsUpDown } from "lucide-react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "../../lib/utils";
|
|
6
|
+
|
|
7
|
+
function Select({
|
|
8
|
+
...props
|
|
9
|
+
}: React.ComponentProps<typeof SelectPrimitive.Root>) {
|
|
10
|
+
return <SelectPrimitive.Root data-slot="select" {...props} />;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function SelectGroup({
|
|
14
|
+
className,
|
|
15
|
+
...props
|
|
16
|
+
}: React.ComponentProps<typeof SelectPrimitive.Group>) {
|
|
17
|
+
return (
|
|
18
|
+
<SelectPrimitive.Group
|
|
19
|
+
data-slot="select-group"
|
|
20
|
+
className={cn("scroll-my-1 p-1", className)}
|
|
21
|
+
{...props}
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function SelectValue({
|
|
27
|
+
...props
|
|
28
|
+
}: React.ComponentProps<typeof SelectPrimitive.Value>) {
|
|
29
|
+
return <SelectPrimitive.Value data-slot="select-value" {...props} />;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function SelectTrigger({
|
|
33
|
+
className,
|
|
34
|
+
size = "default",
|
|
35
|
+
children,
|
|
36
|
+
...props
|
|
37
|
+
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
|
|
38
|
+
size?: "sm" | "default";
|
|
39
|
+
}) {
|
|
40
|
+
return (
|
|
41
|
+
<SelectPrimitive.Trigger
|
|
42
|
+
data-slot="select-trigger"
|
|
43
|
+
data-size={size}
|
|
44
|
+
className={cn(
|
|
45
|
+
"border-input data-placeholder:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 gap-1.5 rounded-md border bg-transparent py-2 pr-2 pl-2.5 text-sm shadow-xs transition-[color,box-shadow] focus-visible:ring-3 data-[size=default]:h-9 data-[size=sm]:h-8 [&_svg:not([class*='size-'])]:size-4 flex w-fit items-center justify-between whitespace-nowrap outline-none disabled:cursor-not-allowed disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
46
|
+
className,
|
|
47
|
+
)}
|
|
48
|
+
{...props}
|
|
49
|
+
>
|
|
50
|
+
{children}
|
|
51
|
+
<SelectPrimitive.Icon asChild>
|
|
52
|
+
<ChevronsUpDown className="text-muted-foreground size-4 pointer-events-none" />
|
|
53
|
+
</SelectPrimitive.Icon>
|
|
54
|
+
</SelectPrimitive.Trigger>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function SelectContent({
|
|
59
|
+
className,
|
|
60
|
+
children,
|
|
61
|
+
position = "item-aligned",
|
|
62
|
+
align = "center",
|
|
63
|
+
...props
|
|
64
|
+
}: React.ComponentProps<typeof SelectPrimitive.Content>) {
|
|
65
|
+
return (
|
|
66
|
+
<SelectPrimitive.Portal>
|
|
67
|
+
<SelectPrimitive.Content
|
|
68
|
+
data-slot="select-content"
|
|
69
|
+
className={cn(
|
|
70
|
+
"bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 min-w-36 rounded-md shadow-md ring-1 duration-100 relative z-50 max-h-(--radix-select-content-available-height) overflow-x-hidden overflow-y-auto",
|
|
71
|
+
position === "popper" &&
|
|
72
|
+
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
73
|
+
className,
|
|
74
|
+
)}
|
|
75
|
+
position={position}
|
|
76
|
+
align={align}
|
|
77
|
+
{...props}
|
|
78
|
+
>
|
|
79
|
+
<SelectScrollUpButton />
|
|
80
|
+
<SelectPrimitive.Viewport>{children}</SelectPrimitive.Viewport>
|
|
81
|
+
<SelectScrollDownButton />
|
|
82
|
+
</SelectPrimitive.Content>
|
|
83
|
+
</SelectPrimitive.Portal>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function SelectItem({
|
|
88
|
+
className,
|
|
89
|
+
children,
|
|
90
|
+
...props
|
|
91
|
+
}: React.ComponentProps<typeof SelectPrimitive.Item>) {
|
|
92
|
+
return (
|
|
93
|
+
<SelectPrimitive.Item
|
|
94
|
+
data-slot="select-item"
|
|
95
|
+
className={cn(
|
|
96
|
+
"focus:bg-accent focus:text-accent-foreground gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex w-full cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
97
|
+
className,
|
|
98
|
+
)}
|
|
99
|
+
{...props}
|
|
100
|
+
>
|
|
101
|
+
<span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center">
|
|
102
|
+
<SelectPrimitive.ItemIndicator>
|
|
103
|
+
<Check className="pointer-events-none" />
|
|
104
|
+
</SelectPrimitive.ItemIndicator>
|
|
105
|
+
</span>
|
|
106
|
+
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
|
107
|
+
</SelectPrimitive.Item>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function SelectScrollUpButton({
|
|
112
|
+
className,
|
|
113
|
+
...props
|
|
114
|
+
}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
|
|
115
|
+
return (
|
|
116
|
+
<SelectPrimitive.ScrollUpButton
|
|
117
|
+
data-slot="select-scroll-up-button"
|
|
118
|
+
className={cn(
|
|
119
|
+
"bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4",
|
|
120
|
+
className,
|
|
121
|
+
)}
|
|
122
|
+
{...props}
|
|
123
|
+
>
|
|
124
|
+
<ChevronUp />
|
|
125
|
+
</SelectPrimitive.ScrollUpButton>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function SelectScrollDownButton({
|
|
130
|
+
className,
|
|
131
|
+
...props
|
|
132
|
+
}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
|
|
133
|
+
return (
|
|
134
|
+
<SelectPrimitive.ScrollDownButton
|
|
135
|
+
data-slot="select-scroll-down-button"
|
|
136
|
+
className={cn(
|
|
137
|
+
"bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4",
|
|
138
|
+
className,
|
|
139
|
+
)}
|
|
140
|
+
{...props}
|
|
141
|
+
>
|
|
142
|
+
<ChevronDown />
|
|
143
|
+
</SelectPrimitive.ScrollDownButton>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export {
|
|
148
|
+
Select,
|
|
149
|
+
SelectContent,
|
|
150
|
+
SelectGroup,
|
|
151
|
+
SelectItem,
|
|
152
|
+
SelectScrollDownButton,
|
|
153
|
+
SelectScrollUpButton,
|
|
154
|
+
SelectTrigger,
|
|
155
|
+
SelectValue,
|
|
156
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { cn } from "../../lib/utils";
|
|
2
|
+
|
|
3
|
+
function Skeleton({
|
|
4
|
+
className,
|
|
5
|
+
...props
|
|
6
|
+
}: React.ComponentProps<"div">) {
|
|
7
|
+
return (
|
|
8
|
+
<div
|
|
9
|
+
data-slot="skeleton"
|
|
10
|
+
className={cn("bg-muted rounded-md animate-pulse", className)}
|
|
11
|
+
{...props}
|
|
12
|
+
/>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export { Skeleton };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../../lib/utils";
|
|
4
|
+
|
|
5
|
+
function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
|
|
6
|
+
return (
|
|
7
|
+
<textarea
|
|
8
|
+
data-slot="textarea"
|
|
9
|
+
className={cn(
|
|
10
|
+
"border-input dark:bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 rounded-md border bg-transparent px-2.5 py-2 text-base shadow-xs transition-[color,box-shadow] focus-visible:ring-3 md:text-sm placeholder:text-muted-foreground flex field-sizing-content min-h-16 w-full outline-none disabled:cursor-not-allowed disabled:opacity-50",
|
|
11
|
+
className,
|
|
12
|
+
)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { Textarea };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Config & Defaults
|
|
2
|
+
export { type GchatHookConfig, DEFAULT_INBOX_POLLING, DEFAULT_MESSAGES_POLLING, DEFAULT_CHANNEL_STATUS_POLLING, DEFAULT_QR_POLLING } from "./types";
|
|
3
|
+
|
|
4
|
+
// Inboxes
|
|
5
|
+
export { useInboxes, useInbox, useInboxStats, useCreateInbox, useUpdateInbox, useDeleteInbox } from "./use-inboxes";
|
|
6
|
+
|
|
7
|
+
// Inbox Messages
|
|
8
|
+
export { useInboxMessages, useSendMessage, useRetryMessage, useRevokeMessage, useEditMessage } from "./use-inbox-messages";
|
|
9
|
+
|
|
10
|
+
// Contacts
|
|
11
|
+
export { useContacts, useGetContact, useCreateContact, useUpdateContact, useDeleteContact } from "./use-contacts";
|
|
12
|
+
|
|
13
|
+
// Channels
|
|
14
|
+
export { useChannels, useCreateChannel, useUpdateChannel, useDeleteChannel, useConnectChannel, useChannelWhatsappStatus, useChannelQR, useDisconnectChannel, useLogoutChannel } from "./use-channels";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { createGchatClient } from "../client";
|
|
3
|
+
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Hook Configuration
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
export interface GchatHookConfig {
|
|
9
|
+
accountId: number;
|
|
10
|
+
token: string;
|
|
11
|
+
baseUrl: string;
|
|
12
|
+
language?: string;
|
|
13
|
+
idWl?: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Polling Defaults (ms)
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
export const DEFAULT_INBOX_POLLING = 5000;
|
|
21
|
+
export const DEFAULT_MESSAGES_POLLING = 3000;
|
|
22
|
+
export const DEFAULT_CHANNEL_STATUS_POLLING = 5000;
|
|
23
|
+
export const DEFAULT_QR_POLLING = 2000;
|
|
24
|
+
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Internal helper — memoized client from config
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
export function useGchatClient(config: GchatHookConfig) {
|
|
30
|
+
return useMemo(
|
|
31
|
+
() =>
|
|
32
|
+
createGchatClient({
|
|
33
|
+
baseUrl: config.baseUrl,
|
|
34
|
+
token: config.token,
|
|
35
|
+
language: config.language,
|
|
36
|
+
idWl: config.idWl,
|
|
37
|
+
}),
|
|
38
|
+
[config.baseUrl, config.token, config.language, config.idWl],
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
2
|
+
import type { Channel, WhatsappStatus } from "../types";
|
|
3
|
+
import {
|
|
4
|
+
type GchatHookConfig,
|
|
5
|
+
DEFAULT_CHANNEL_STATUS_POLLING,
|
|
6
|
+
DEFAULT_QR_POLLING,
|
|
7
|
+
useGchatClient,
|
|
8
|
+
} from "./types";
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Queries
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
export function useChannels(config: GchatHookConfig) {
|
|
15
|
+
const client = useGchatClient(config);
|
|
16
|
+
|
|
17
|
+
return useQuery({
|
|
18
|
+
queryKey: ["greatchat", "channels", config.accountId],
|
|
19
|
+
queryFn: () => client.listChannels(config.accountId),
|
|
20
|
+
enabled: !!config.accountId && !!config.token,
|
|
21
|
+
select: (res) => res.data || [],
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function useChannelWhatsappStatus(
|
|
26
|
+
config: GchatHookConfig,
|
|
27
|
+
channelId: number | null,
|
|
28
|
+
enabled = true,
|
|
29
|
+
pollingInterval?: number,
|
|
30
|
+
) {
|
|
31
|
+
const client = useGchatClient(config);
|
|
32
|
+
|
|
33
|
+
return useQuery({
|
|
34
|
+
queryKey: ["greatchat", "channel-status", config.accountId, channelId],
|
|
35
|
+
queryFn: () =>
|
|
36
|
+
client.getChannelWhatsappStatus(config.accountId, channelId!),
|
|
37
|
+
enabled: !!config.accountId && !!config.token && !!channelId && enabled,
|
|
38
|
+
refetchInterval: pollingInterval ?? DEFAULT_CHANNEL_STATUS_POLLING,
|
|
39
|
+
select: (res) => {
|
|
40
|
+
const d = res.data;
|
|
41
|
+
return (Array.isArray(d) ? d[0] : d) as WhatsappStatus;
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function useChannelQR(
|
|
47
|
+
config: GchatHookConfig,
|
|
48
|
+
channelId: number | null,
|
|
49
|
+
enabled = false,
|
|
50
|
+
pollingInterval?: number,
|
|
51
|
+
) {
|
|
52
|
+
const client = useGchatClient(config);
|
|
53
|
+
|
|
54
|
+
return useQuery({
|
|
55
|
+
queryKey: ["greatchat", "channel-qr", config.accountId, channelId],
|
|
56
|
+
queryFn: () => client.getChannelQR(config.accountId, channelId!),
|
|
57
|
+
enabled: !!config.accountId && !!config.token && !!channelId && enabled,
|
|
58
|
+
refetchInterval: pollingInterval ?? DEFAULT_QR_POLLING,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
// Mutations
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
|
|
66
|
+
export function useCreateChannel(config: GchatHookConfig) {
|
|
67
|
+
const client = useGchatClient(config);
|
|
68
|
+
const queryClient = useQueryClient();
|
|
69
|
+
|
|
70
|
+
return useMutation({
|
|
71
|
+
mutationFn: (
|
|
72
|
+
body: Pick<Channel, "name" | "type" | "provider"> &
|
|
73
|
+
Partial<Pick<Channel, "identifier" | "id_agent">>,
|
|
74
|
+
) => client.createChannel(config.accountId, body),
|
|
75
|
+
onSuccess: () => {
|
|
76
|
+
queryClient.invalidateQueries({
|
|
77
|
+
queryKey: ["greatchat", "channels"],
|
|
78
|
+
});
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function useUpdateChannel(config: GchatHookConfig) {
|
|
84
|
+
const client = useGchatClient(config);
|
|
85
|
+
const queryClient = useQueryClient();
|
|
86
|
+
|
|
87
|
+
return useMutation({
|
|
88
|
+
mutationFn: ({
|
|
89
|
+
id,
|
|
90
|
+
body,
|
|
91
|
+
}: {
|
|
92
|
+
id: number;
|
|
93
|
+
body: Partial<
|
|
94
|
+
Pick<Channel, "name" | "identifier" | "status" | "id_agent">
|
|
95
|
+
>;
|
|
96
|
+
}) => client.updateChannel(config.accountId, id, body),
|
|
97
|
+
onSuccess: () => {
|
|
98
|
+
queryClient.invalidateQueries({
|
|
99
|
+
queryKey: ["greatchat", "channels"],
|
|
100
|
+
});
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function useDeleteChannel(config: GchatHookConfig) {
|
|
106
|
+
const client = useGchatClient(config);
|
|
107
|
+
const queryClient = useQueryClient();
|
|
108
|
+
|
|
109
|
+
return useMutation({
|
|
110
|
+
mutationFn: (channelId: number) =>
|
|
111
|
+
client.deleteChannel(config.accountId, channelId),
|
|
112
|
+
onSuccess: () => {
|
|
113
|
+
queryClient.invalidateQueries({
|
|
114
|
+
queryKey: ["greatchat", "channels"],
|
|
115
|
+
});
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function useConnectChannel(config: GchatHookConfig) {
|
|
121
|
+
const client = useGchatClient(config);
|
|
122
|
+
|
|
123
|
+
return useMutation({
|
|
124
|
+
mutationFn: (channelId: number) =>
|
|
125
|
+
client.connectChannel(config.accountId, channelId),
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function useDisconnectChannel(config: GchatHookConfig) {
|
|
130
|
+
const client = useGchatClient(config);
|
|
131
|
+
const queryClient = useQueryClient();
|
|
132
|
+
|
|
133
|
+
return useMutation({
|
|
134
|
+
mutationFn: (channelId: number) =>
|
|
135
|
+
client.disconnectChannel(config.accountId, channelId),
|
|
136
|
+
onSuccess: () => {
|
|
137
|
+
queryClient.invalidateQueries({
|
|
138
|
+
queryKey: ["greatchat", "channel-status"],
|
|
139
|
+
});
|
|
140
|
+
queryClient.invalidateQueries({
|
|
141
|
+
queryKey: ["greatchat", "channels"],
|
|
142
|
+
});
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function useLogoutChannel(config: GchatHookConfig) {
|
|
148
|
+
const client = useGchatClient(config);
|
|
149
|
+
const queryClient = useQueryClient();
|
|
150
|
+
|
|
151
|
+
return useMutation({
|
|
152
|
+
mutationFn: (channelId: number) =>
|
|
153
|
+
client.logoutChannel(config.accountId, channelId),
|
|
154
|
+
onSuccess: () => {
|
|
155
|
+
queryClient.invalidateQueries({
|
|
156
|
+
queryKey: ["greatchat", "channel-status"],
|
|
157
|
+
});
|
|
158
|
+
queryClient.invalidateQueries({
|
|
159
|
+
queryKey: ["greatchat", "channels"],
|
|
160
|
+
});
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
2
|
+
import type { Contact } from "../types";
|
|
3
|
+
import { type GchatHookConfig, useGchatClient } from "./types";
|
|
4
|
+
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Queries
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
export function useContacts(
|
|
10
|
+
config: GchatHookConfig,
|
|
11
|
+
params?: Record<string, string>,
|
|
12
|
+
) {
|
|
13
|
+
const client = useGchatClient(config);
|
|
14
|
+
|
|
15
|
+
return useQuery({
|
|
16
|
+
queryKey: ["greatchat", "contacts", config.accountId, params],
|
|
17
|
+
queryFn: () => client.listContacts(config.accountId, params),
|
|
18
|
+
enabled: !!config.accountId && !!config.token,
|
|
19
|
+
select: (res) => ({ data: res.data || [], total: res.total || 0 }),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function useGetContact(
|
|
24
|
+
config: GchatHookConfig,
|
|
25
|
+
contactId: number | null,
|
|
26
|
+
) {
|
|
27
|
+
const client = useGchatClient(config);
|
|
28
|
+
|
|
29
|
+
return useQuery({
|
|
30
|
+
queryKey: ["greatchat", "contact", config.accountId, contactId],
|
|
31
|
+
queryFn: () => client.getContact(config.accountId, contactId!),
|
|
32
|
+
enabled: !!config.accountId && !!config.token && !!contactId,
|
|
33
|
+
select: (res) => {
|
|
34
|
+
const d = res.data;
|
|
35
|
+
return (Array.isArray(d) ? d[0] : d) as Contact;
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// Mutations
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
export function useCreateContact(config: GchatHookConfig) {
|
|
45
|
+
const client = useGchatClient(config);
|
|
46
|
+
const queryClient = useQueryClient();
|
|
47
|
+
|
|
48
|
+
return useMutation({
|
|
49
|
+
mutationFn: (body: {
|
|
50
|
+
name: string;
|
|
51
|
+
phone_number?: string;
|
|
52
|
+
identifier?: string;
|
|
53
|
+
}) => client.createContact(config.accountId, body),
|
|
54
|
+
onSuccess: () => {
|
|
55
|
+
queryClient.invalidateQueries({
|
|
56
|
+
queryKey: ["greatchat", "contacts"],
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function useUpdateContact(config: GchatHookConfig) {
|
|
63
|
+
const client = useGchatClient(config);
|
|
64
|
+
const queryClient = useQueryClient();
|
|
65
|
+
|
|
66
|
+
return useMutation({
|
|
67
|
+
mutationFn: ({
|
|
68
|
+
id,
|
|
69
|
+
body,
|
|
70
|
+
}: {
|
|
71
|
+
id: number;
|
|
72
|
+
body: { name?: string; phone_number?: string; identifier?: string };
|
|
73
|
+
}) => client.updateContact(config.accountId, id, body),
|
|
74
|
+
onSuccess: () => {
|
|
75
|
+
queryClient.invalidateQueries({
|
|
76
|
+
queryKey: ["greatchat", "contacts"],
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function useDeleteContact(config: GchatHookConfig) {
|
|
83
|
+
const client = useGchatClient(config);
|
|
84
|
+
const queryClient = useQueryClient();
|
|
85
|
+
|
|
86
|
+
return useMutation({
|
|
87
|
+
mutationFn: (id: number) => client.deleteContact(config.accountId, id),
|
|
88
|
+
onSuccess: () => {
|
|
89
|
+
queryClient.invalidateQueries({
|
|
90
|
+
queryKey: ["greatchat", "contacts"],
|
|
91
|
+
});
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
}
|