@greatapps/greatchat-ui 0.1.5 → 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.
@@ -0,0 +1,31 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Progress as ProgressPrimitive } from "radix-ui"
5
+
6
+ import { cn } from "../../lib/utils"
7
+
8
+ function Progress({
9
+ className,
10
+ value,
11
+ ...props
12
+ }: React.ComponentProps<typeof ProgressPrimitive.Root>) {
13
+ return (
14
+ <ProgressPrimitive.Root
15
+ data-slot="progress"
16
+ className={cn(
17
+ "bg-muted h-1.5 rounded-full relative flex w-full items-center overflow-x-hidden",
18
+ className
19
+ )}
20
+ {...props}
21
+ >
22
+ <ProgressPrimitive.Indicator
23
+ data-slot="progress-indicator"
24
+ className="bg-primary size-full flex-1 transition-all"
25
+ style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
26
+ />
27
+ </ProgressPrimitive.Root>
28
+ )
29
+ }
30
+
31
+ export { Progress }
@@ -0,0 +1,101 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+
5
+ import { cn } from "../../lib/utils"
6
+
7
+ function Table({ className, ...props }: React.ComponentProps<"table">) {
8
+ return (
9
+ <div data-slot="table-container" className="relative w-full overflow-x-auto">
10
+ <table
11
+ data-slot="table"
12
+ className={cn("w-full caption-bottom text-sm", className)}
13
+ {...props}
14
+ />
15
+ </div>
16
+ )
17
+ }
18
+
19
+ function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
20
+ return (
21
+ <thead
22
+ data-slot="table-header"
23
+ className={cn("[&_tr]:border-b", className)}
24
+ {...props}
25
+ />
26
+ )
27
+ }
28
+
29
+ function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
30
+ return (
31
+ <tbody
32
+ data-slot="table-body"
33
+ className={cn("[&_tr:last-child]:border-0", className)}
34
+ {...props}
35
+ />
36
+ )
37
+ }
38
+
39
+ function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
40
+ return (
41
+ <tfoot
42
+ data-slot="table-footer"
43
+ className={cn("bg-muted/50 border-t font-medium [&>tr]:last:border-b-0", className)}
44
+ {...props}
45
+ />
46
+ )
47
+ }
48
+
49
+ function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
50
+ return (
51
+ <tr
52
+ data-slot="table-row"
53
+ className={cn("hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors", className)}
54
+ {...props}
55
+ />
56
+ )
57
+ }
58
+
59
+ function TableHead({ className, ...props }: React.ComponentProps<"th">) {
60
+ return (
61
+ <th
62
+ data-slot="table-head"
63
+ className={cn("text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0", className)}
64
+ {...props}
65
+ />
66
+ )
67
+ }
68
+
69
+ function TableCell({ className, ...props }: React.ComponentProps<"td">) {
70
+ return (
71
+ <td
72
+ data-slot="table-cell"
73
+ className={cn("p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0", className)}
74
+ {...props}
75
+ />
76
+ )
77
+ }
78
+
79
+ function TableCaption({
80
+ className,
81
+ ...props
82
+ }: React.ComponentProps<"caption">) {
83
+ return (
84
+ <caption
85
+ data-slot="table-caption"
86
+ className={cn("text-muted-foreground mt-4 text-sm", className)}
87
+ {...props}
88
+ />
89
+ )
90
+ }
91
+
92
+ export {
93
+ Table,
94
+ TableHeader,
95
+ TableBody,
96
+ TableFooter,
97
+ TableHead,
98
+ TableRow,
99
+ TableCell,
100
+ TableCaption,
101
+ }
@@ -0,0 +1,21 @@
1
+ import type { SVGProps } from "react";
2
+
3
+ export function WhatsappIcon(props: SVGProps<SVGSVGElement>) {
4
+ return (
5
+ <svg
6
+ xmlns="http://www.w3.org/2000/svg"
7
+ width="24"
8
+ height="24"
9
+ viewBox="0 0 24 24"
10
+ fill="none"
11
+ stroke="currentColor"
12
+ strokeWidth={2}
13
+ strokeLinecap="round"
14
+ strokeLinejoin="round"
15
+ {...props}
16
+ >
17
+ <path d="M3 21l1.65 -3.8a9 9 0 1 1 3.4 2.9l-5.05 .9" />
18
+ <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" />
19
+ </svg>
20
+ );
21
+ }
@@ -0,0 +1,147 @@
1
+ "use client";
2
+
3
+ import { useEffect, useState, useCallback } from "react";
4
+ import type { GchatHookConfig } from "../hooks/types";
5
+ import { useConnectChannel, useChannelQR, useChannelWhatsappStatus } from "../hooks/use-channels";
6
+ import {
7
+ Dialog,
8
+ DialogContent,
9
+ DialogHeader,
10
+ DialogTitle,
11
+ DialogDescription,
12
+ } from "./ui/dialog";
13
+ import { Button } from "./ui/button";
14
+ import { Loader2, Check, AlertCircle, RefreshCw } from "lucide-react";
15
+ import { toast } from "sonner";
16
+
17
+ type QRState = "connecting" | "waiting_qr" | "showing_qr" | "connected" | "error";
18
+
19
+ export interface WhatsappQrDialogProps {
20
+ open: boolean;
21
+ onOpenChange: (open: boolean) => void;
22
+ channelId: number;
23
+ config: GchatHookConfig;
24
+ }
25
+
26
+ export function WhatsappQrDialog({
27
+ open,
28
+ onOpenChange,
29
+ channelId,
30
+ config,
31
+ }: WhatsappQrDialogProps) {
32
+ const connectChannel = useConnectChannel(config);
33
+ const [state, setState] = useState<QRState>("connecting");
34
+ const [errorMsg, setErrorMsg] = useState("");
35
+
36
+ const { data: qrUrl } = useChannelQR(
37
+ config,
38
+ channelId,
39
+ open && (state === "waiting_qr" || state === "showing_qr"),
40
+ );
41
+
42
+ const { data: status } = useChannelWhatsappStatus(
43
+ config,
44
+ channelId,
45
+ open && state !== "connected",
46
+ );
47
+
48
+ const startConnect = useCallback(() => {
49
+ setState("connecting");
50
+ setErrorMsg("");
51
+ connectChannel.mutate(channelId, {
52
+ onSuccess: () => setState("waiting_qr"),
53
+ onError: (err) => {
54
+ setState("error");
55
+ setErrorMsg(err.message || "Erro ao conectar");
56
+ },
57
+ });
58
+ }, [channelId, connectChannel]);
59
+
60
+ useEffect(() => {
61
+ if (!open) {
62
+ setState("connecting");
63
+ setErrorMsg("");
64
+ return;
65
+ }
66
+ startConnect();
67
+ // eslint-disable-next-line react-hooks/exhaustive-deps
68
+ }, [open, channelId]);
69
+
70
+ useEffect(() => {
71
+ if (qrUrl && (state === "waiting_qr" || state === "showing_qr")) {
72
+ setState("showing_qr");
73
+ }
74
+ }, [qrUrl, state]);
75
+
76
+ useEffect(() => {
77
+ if (status?.connected && status?.logged_in) {
78
+ setState("connected");
79
+ toast.success("WhatsApp conectado com sucesso!");
80
+ setTimeout(() => onOpenChange(false), 1500);
81
+ }
82
+ }, [status, onOpenChange]);
83
+
84
+ return (
85
+ <Dialog open={open} onOpenChange={onOpenChange}>
86
+ <DialogContent className="sm:max-w-md">
87
+ <DialogHeader>
88
+ <DialogTitle>Conectar WhatsApp</DialogTitle>
89
+ <DialogDescription>
90
+ Escaneie o QR code com seu WhatsApp para conectar
91
+ </DialogDescription>
92
+ </DialogHeader>
93
+
94
+ <div className="flex min-h-[300px] items-center justify-center">
95
+ {state === "connecting" && (
96
+ <div className="flex flex-col items-center gap-3 text-muted-foreground">
97
+ <Loader2 className="h-8 w-8 animate-spin" />
98
+ <p className="text-sm">Iniciando conexão...</p>
99
+ </div>
100
+ )}
101
+
102
+ {state === "waiting_qr" && (
103
+ <div className="flex flex-col items-center gap-3 text-muted-foreground">
104
+ <Loader2 className="h-8 w-8 animate-spin" />
105
+ <p className="text-sm">Gerando QR code...</p>
106
+ </div>
107
+ )}
108
+
109
+ {state === "showing_qr" && qrUrl && (
110
+ <div className="flex flex-col items-center gap-3">
111
+ <img
112
+ src={qrUrl}
113
+ alt="QR Code WhatsApp"
114
+ className="h-64 w-64 rounded-lg border"
115
+ />
116
+ <p className="text-sm text-muted-foreground">
117
+ Abra o WhatsApp &gt; Aparelhos conectados &gt; Conectar
118
+ </p>
119
+ </div>
120
+ )}
121
+
122
+ {state === "connected" && (
123
+ <div className="flex flex-col items-center gap-3 text-green-600">
124
+ <Check className="h-12 w-12" />
125
+ <p className="text-lg font-medium">Conectado!</p>
126
+ </div>
127
+ )}
128
+
129
+ {state === "error" && (
130
+ <div className="flex flex-col items-center gap-3 text-destructive">
131
+ <AlertCircle className="h-8 w-8" />
132
+ <p className="text-sm">{errorMsg || "Erro ao gerar QR code"}</p>
133
+ <Button
134
+ variant="outline"
135
+ size="sm"
136
+ onClick={startConnect}
137
+ >
138
+ <RefreshCw className="mr-2 h-4 w-4" />
139
+ Tentar novamente
140
+ </Button>
141
+ </div>
142
+ )}
143
+ </div>
144
+ </DialogContent>
145
+ </Dialog>
146
+ );
147
+ }
package/src/index.ts CHANGED
@@ -21,7 +21,7 @@ export { groupMessagesByDate, formatDateGroup, formatMessageTime } from "./utils
21
21
  // Hooks
22
22
  export * from "./hooks";
23
23
 
24
- // Components
24
+ // Base Components
25
25
  export {
26
26
  ChatView,
27
27
  ChatInput,
@@ -46,3 +46,38 @@ export type {
46
46
  ChannelCardProps,
47
47
  WhatsappStatusBadgeProps,
48
48
  } from "./components";
49
+
50
+ // Icons
51
+ export { WhatsappIcon } from "./components/whatsapp-icon";
52
+
53
+ // Dialogs & Forms
54
+ export { WhatsappQrDialog } from "./components/whatsapp-qr-dialog";
55
+ export type { WhatsappQrDialogProps } from "./components/whatsapp-qr-dialog";
56
+
57
+ export { ChannelCreateDialog } from "./components/channel-create-dialog";
58
+ export type { ChannelCreateDialogProps } from "./components/channel-create-dialog";
59
+
60
+ export { ChannelEditDialog } from "./components/channel-edit-dialog";
61
+ export type { ChannelEditDialogProps } from "./components/channel-edit-dialog";
62
+
63
+ export { ContactFormDialog } from "./components/contact-form-dialog";
64
+ export type { ContactFormDialogProps } from "./components/contact-form-dialog";
65
+
66
+ export { ContactsTable } from "./components/contacts-table";
67
+ export type { ContactsTableProps } from "./components/contacts-table";
68
+
69
+ export { DataTable } from "./components/data-table";
70
+ export type { DataTableProps } from "./components/data-table";
71
+
72
+ // Page Compositions
73
+ export { InboxPage } from "./components/inbox-page";
74
+ export type { InboxPageProps } from "./components/inbox-page";
75
+
76
+ export { ChannelsPage } from "./components/channels-page";
77
+ export type { ChannelsPageProps } from "./components/channels-page";
78
+
79
+ export { ContactsPage } from "./components/contacts-page";
80
+ export type { ContactsPageProps } from "./components/contacts-page";
81
+
82
+ export { ChatDashboard } from "./components/chat-dashboard";
83
+ export type { ChatDashboardProps } from "./components/chat-dashboard";