@greatapps/greatchat-ui 0.1.0

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,138 @@
1
+ import { ClassValue } from 'clsx';
2
+
3
+ interface ApiResponse<T> {
4
+ status: 0 | 1;
5
+ message: string;
6
+ data: T;
7
+ total?: number;
8
+ }
9
+ interface Channel {
10
+ id: number;
11
+ id_account: number;
12
+ name: string;
13
+ type: "whatsapp_unofficial";
14
+ provider: "gwhatsmeow";
15
+ identifier: string | null;
16
+ external_id: string | null;
17
+ config: string | null;
18
+ status: "active" | "inactive";
19
+ id_agent: number | null;
20
+ deleted: number;
21
+ datetime_add: string;
22
+ datetime_alt: string | null;
23
+ }
24
+ interface Contact {
25
+ id: number;
26
+ id_account: number;
27
+ name: string;
28
+ phone_number: string | null;
29
+ identifier: string | null;
30
+ metadata: string | null;
31
+ deleted: number;
32
+ datetime_add: string;
33
+ datetime_alt: string | null;
34
+ }
35
+ interface Inbox {
36
+ id: number;
37
+ id_account: number;
38
+ id_channel: number;
39
+ id_contact: number;
40
+ id_agent: number | null;
41
+ status: "open" | "pending" | "resolved";
42
+ last_message_at: string | null;
43
+ external_id: string | null;
44
+ contact_name: string | null;
45
+ contact_phone: string | null;
46
+ contact_identifier: string | null;
47
+ channel_name: string | null;
48
+ channel_identifier: string | null;
49
+ channel_type: string | null;
50
+ last_message_content: string | null;
51
+ last_message_content_type: string | null;
52
+ last_message_direction: "inbound" | "outbound" | null;
53
+ last_message_source: "contact" | "agent" | "bot" | null;
54
+ datetime_add: string;
55
+ datetime_alt: string | null;
56
+ }
57
+ type MessageContentType = "text" | "image" | "audio" | "video" | "document" | "location" | "contacts";
58
+ interface InboxMessage {
59
+ id: number;
60
+ id_account: number;
61
+ id_inbox: number;
62
+ id_contact: number | null;
63
+ direction: "inbound" | "outbound";
64
+ content: string | null;
65
+ content_type: MessageContentType;
66
+ content_url: string | null;
67
+ metadata: string | null;
68
+ external_id: string | null;
69
+ status: "sent" | "delivered" | "read" | "failed" | "received" | "pending";
70
+ source: "contact" | "agent" | "bot";
71
+ is_private: boolean;
72
+ datetime_add: string;
73
+ datetime_alt: string | null;
74
+ /** UI-only: error message when status is "failed" */
75
+ _error?: string;
76
+ }
77
+ interface InboxStats {
78
+ total: number;
79
+ open: number;
80
+ pending: number;
81
+ resolved: number;
82
+ }
83
+ interface WhatsappStatus {
84
+ status: string;
85
+ connected: boolean;
86
+ logged_in: boolean;
87
+ jid?: string;
88
+ }
89
+
90
+ interface GchatClientConfig {
91
+ baseUrl: string;
92
+ token: string;
93
+ language?: string;
94
+ idWl?: number;
95
+ }
96
+ declare function createGchatClient(config: GchatClientConfig): {
97
+ listInboxes: (idAccount: number, params?: Record<string, string>) => Promise<ApiResponse<Inbox[]>>;
98
+ getInbox: (idAccount: number, id: number) => Promise<ApiResponse<Inbox>>;
99
+ getInboxStats: (idAccount: number) => Promise<ApiResponse<InboxStats>>;
100
+ createInbox: (idAccount: number, body: {
101
+ id_channel: number;
102
+ id_contact: number;
103
+ }) => Promise<ApiResponse<Inbox>>;
104
+ updateInbox: (idAccount: number, id: number, body: Partial<Pick<Inbox, "status" | "id_agent">>) => Promise<ApiResponse<Inbox>>;
105
+ deleteInbox: (idAccount: number, id: number) => Promise<ApiResponse<void>>;
106
+ listInboxMessages: (idAccount: number, params: Record<string, string>) => Promise<ApiResponse<InboxMessage[]>>;
107
+ sendMessage: (idAccount: number, body: {
108
+ id_inbox: number;
109
+ content: string;
110
+ content_type?: string;
111
+ source?: string;
112
+ direction: "outbound";
113
+ }) => Promise<ApiResponse<InboxMessage>>;
114
+ revokeMessage: (idAccount: number, id: number) => Promise<ApiResponse<void>>;
115
+ editMessage: (idAccount: number, id: number, body: {
116
+ content: string;
117
+ }) => Promise<ApiResponse<void>>;
118
+ listContacts: (idAccount: number, params?: Record<string, string>) => Promise<ApiResponse<Contact[]>>;
119
+ getContact: (idAccount: number, id: number) => Promise<ApiResponse<Contact>>;
120
+ createContact: (idAccount: number, body: Pick<Contact, "name"> & Partial<Pick<Contact, "phone_number" | "identifier">>) => Promise<ApiResponse<Contact>>;
121
+ updateContact: (idAccount: number, id: number, body: Partial<Pick<Contact, "name" | "phone_number" | "identifier">>) => Promise<ApiResponse<Contact>>;
122
+ deleteContact: (idAccount: number, id: number) => Promise<ApiResponse<void>>;
123
+ listChannels: (idAccount: number) => Promise<ApiResponse<Channel[]>>;
124
+ getChannel: (idAccount: number, id: number) => Promise<ApiResponse<Channel>>;
125
+ createChannel: (idAccount: number, body: Pick<Channel, "name" | "type" | "provider"> & Partial<Pick<Channel, "identifier" | "id_agent">>) => Promise<ApiResponse<Channel>>;
126
+ updateChannel: (idAccount: number, id: number, body: Partial<Pick<Channel, "name" | "identifier" | "status" | "id_agent">>) => Promise<ApiResponse<Channel>>;
127
+ deleteChannel: (idAccount: number, id: number) => Promise<ApiResponse<void>>;
128
+ connectChannel: (idAccount: number, id: number) => Promise<ApiResponse<unknown>>;
129
+ getChannelQR: (idAccount: number, id: number) => Promise<string | null>;
130
+ getChannelWhatsappStatus: (idAccount: number, id: number) => Promise<ApiResponse<WhatsappStatus>>;
131
+ disconnectChannel: (idAccount: number, id: number) => Promise<ApiResponse<unknown>>;
132
+ logoutChannel: (idAccount: number, id: number) => Promise<ApiResponse<unknown>>;
133
+ provisionChannel: (idAccount: number, id: number) => Promise<ApiResponse<unknown>>;
134
+ };
135
+
136
+ declare function cn(...inputs: ClassValue[]): string;
137
+
138
+ export { type ApiResponse, type Channel, type Contact, type GchatClientConfig, type Inbox, type InboxMessage, type InboxStats, type MessageContentType, type WhatsappStatus, cn, createGchatClient };
package/dist/index.js ADDED
@@ -0,0 +1,108 @@
1
+ // src/client/index.ts
2
+ function createGchatClient(config) {
3
+ const { baseUrl, token, language = "pt-br", idWl = 1 } = config;
4
+ function buildUrl(idAccount, path) {
5
+ return `${baseUrl}/v1/${language}/${idWl}/accounts/${idAccount}/${path}`;
6
+ }
7
+ async function request(method, idAccount, path, body, params) {
8
+ const url = new URL(buildUrl(idAccount, path));
9
+ if (params) {
10
+ Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));
11
+ }
12
+ const res = await fetch(url.toString(), {
13
+ method,
14
+ headers: {
15
+ "Content-Type": "application/json",
16
+ Authorization: `Bearer ${token}`
17
+ },
18
+ body: body ? JSON.stringify(body) : void 0
19
+ });
20
+ if (!res.ok) {
21
+ let message = `HTTP ${res.status}: ${res.statusText}`;
22
+ try {
23
+ const errorBody = await res.json();
24
+ message = errorBody.message || message;
25
+ } catch {
26
+ }
27
+ throw new Error(message);
28
+ }
29
+ const json = await res.json();
30
+ if (method !== "GET" && json.status === 0) {
31
+ throw new Error(json.message || "Opera\xE7\xE3o falhou");
32
+ }
33
+ return json;
34
+ }
35
+ return {
36
+ // --- Inboxes ---
37
+ listInboxes: (idAccount, params) => request("GET", idAccount, "inboxes", void 0, params),
38
+ getInbox: (idAccount, id) => request("GET", idAccount, `inboxes/${id}`),
39
+ getInboxStats: (idAccount) => request("GET", idAccount, "inboxes/stats"),
40
+ createInbox: (idAccount, body) => request("POST", idAccount, "inboxes", body),
41
+ updateInbox: (idAccount, id, body) => request("PUT", idAccount, `inboxes/${id}`, body),
42
+ deleteInbox: (idAccount, id) => request("DELETE", idAccount, `inboxes/${id}`),
43
+ // --- Inbox Messages ---
44
+ listInboxMessages: (idAccount, params) => request(
45
+ "GET",
46
+ idAccount,
47
+ "inbox-messages",
48
+ void 0,
49
+ params
50
+ ),
51
+ sendMessage: (idAccount, body) => request("POST", idAccount, "inbox-messages", body),
52
+ revokeMessage: (idAccount, id) => request("POST", idAccount, `inbox-messages/${id}/revoke`),
53
+ editMessage: (idAccount, id, body) => request("POST", idAccount, `inbox-messages/${id}/edit`, body),
54
+ // --- Contacts ---
55
+ listContacts: (idAccount, params) => request("GET", idAccount, "contacts", void 0, params),
56
+ getContact: (idAccount, id) => request("GET", idAccount, `contacts/${id}`),
57
+ createContact: (idAccount, body) => request("POST", idAccount, "contacts", body),
58
+ updateContact: (idAccount, id, body) => request("PUT", idAccount, `contacts/${id}`, body),
59
+ deleteContact: (idAccount, id) => request("DELETE", idAccount, `contacts/${id}`),
60
+ // --- Channels ---
61
+ listChannels: (idAccount) => request("GET", idAccount, "channels"),
62
+ getChannel: (idAccount, id) => request("GET", idAccount, `channels/${id}`),
63
+ createChannel: (idAccount, body) => request("POST", idAccount, "channels", body),
64
+ updateChannel: (idAccount, id, body) => request("PUT", idAccount, `channels/${id}`, body),
65
+ deleteChannel: (idAccount, id) => request("DELETE", idAccount, `channels/${id}`),
66
+ // --- WhatsApp Channel Management ---
67
+ connectChannel: (idAccount, id) => request("POST", idAccount, `channels/${id}/connect`),
68
+ getChannelQR: async (idAccount, id) => {
69
+ const url = buildUrl(idAccount, `channels/${id}/qr`);
70
+ const res = await fetch(url, {
71
+ headers: {
72
+ Accept: "image/png",
73
+ Authorization: `Bearer ${token}`
74
+ }
75
+ });
76
+ if (!res.ok) return null;
77
+ const contentType = res.headers.get("content-type") || "";
78
+ if (!contentType.includes("image/")) return null;
79
+ const blob = await res.blob();
80
+ return new Promise((resolve, reject) => {
81
+ const reader = new FileReader();
82
+ reader.onloadend = () => resolve(reader.result);
83
+ reader.onerror = () => reject(new Error("Failed to read QR image"));
84
+ reader.readAsDataURL(blob);
85
+ });
86
+ },
87
+ getChannelWhatsappStatus: (idAccount, id) => request(
88
+ "GET",
89
+ idAccount,
90
+ `channels/${id}/whatsapp-status`
91
+ ),
92
+ disconnectChannel: (idAccount, id) => request("POST", idAccount, `channels/${id}/disconnect`),
93
+ logoutChannel: (idAccount, id) => request("POST", idAccount, `channels/${id}/logout`),
94
+ provisionChannel: (idAccount, id) => request("POST", idAccount, `channels/${id}/provision`)
95
+ };
96
+ }
97
+
98
+ // src/lib/utils.ts
99
+ import { clsx } from "clsx";
100
+ import { twMerge } from "tailwind-merge";
101
+ function cn(...inputs) {
102
+ return twMerge(clsx(inputs));
103
+ }
104
+ export {
105
+ cn,
106
+ createGchatClient
107
+ };
108
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client/index.ts","../src/lib/utils.ts"],"sourcesContent":["import type {\n ApiResponse,\n Channel,\n Contact,\n Inbox,\n InboxMessage,\n InboxStats,\n WhatsappStatus,\n} from \"../types\";\n\nexport interface GchatClientConfig {\n baseUrl: string;\n token: string;\n language?: string;\n idWl?: number;\n}\n\nexport function createGchatClient(config: GchatClientConfig) {\n const { baseUrl, token, language = \"pt-br\", idWl = 1 } = config;\n\n function buildUrl(idAccount: number, path: string): string {\n return `${baseUrl}/v1/${language}/${idWl}/accounts/${idAccount}/${path}`;\n }\n\n async function request<T>(\n method: string,\n idAccount: number,\n path: string,\n body?: unknown,\n params?: Record<string, string>,\n ): Promise<ApiResponse<T>> {\n const url = new URL(buildUrl(idAccount, path));\n if (params) {\n Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));\n }\n\n const res = await fetch(url.toString(), {\n method,\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!res.ok) {\n let message = `HTTP ${res.status}: ${res.statusText}`;\n try {\n const errorBody = await res.json();\n message = errorBody.message || message;\n } catch {\n /* ignore parse errors */\n }\n throw new Error(message);\n }\n\n const json: ApiResponse<T> = await res.json();\n\n // For mutations, treat API-level failures as errors so TanStack Query\n // fires onError instead of onSuccess (API returns HTTP 200 even on failure)\n if (method !== \"GET\" && json.status === 0) {\n throw new Error(json.message || \"Operação falhou\");\n }\n\n return json;\n }\n\n return {\n // --- Inboxes ---\n listInboxes: (idAccount: number, params?: Record<string, string>) =>\n request<Inbox[]>(\"GET\", idAccount, \"inboxes\", undefined, params),\n getInbox: (idAccount: number, id: number) =>\n request<Inbox>(\"GET\", idAccount, `inboxes/${id}`),\n getInboxStats: (idAccount: number) =>\n request<InboxStats>(\"GET\", idAccount, \"inboxes/stats\"),\n createInbox: (\n idAccount: number,\n body: { id_channel: number; id_contact: number },\n ) => request<Inbox>(\"POST\", idAccount, \"inboxes\", body),\n updateInbox: (\n idAccount: number,\n id: number,\n body: Partial<Pick<Inbox, \"status\" | \"id_agent\">>,\n ) => request<Inbox>(\"PUT\", idAccount, `inboxes/${id}`, body),\n deleteInbox: (idAccount: number, id: number) =>\n request<void>(\"DELETE\", idAccount, `inboxes/${id}`),\n\n // --- Inbox Messages ---\n listInboxMessages: (idAccount: number, params: Record<string, string>) =>\n request<InboxMessage[]>(\n \"GET\",\n idAccount,\n \"inbox-messages\",\n undefined,\n params,\n ),\n sendMessage: (\n idAccount: number,\n body: {\n id_inbox: number;\n content: string;\n content_type?: string;\n source?: string;\n direction: \"outbound\";\n },\n ) => request<InboxMessage>(\"POST\", idAccount, \"inbox-messages\", body),\n revokeMessage: (idAccount: number, id: number) =>\n request<void>(\"POST\", idAccount, `inbox-messages/${id}/revoke`),\n editMessage: (idAccount: number, id: number, body: { content: string }) =>\n request<void>(\"POST\", idAccount, `inbox-messages/${id}/edit`, body),\n\n // --- Contacts ---\n listContacts: (idAccount: number, params?: Record<string, string>) =>\n request<Contact[]>(\"GET\", idAccount, \"contacts\", undefined, params),\n getContact: (idAccount: number, id: number) =>\n request<Contact>(\"GET\", idAccount, `contacts/${id}`),\n createContact: (\n idAccount: number,\n body: Pick<Contact, \"name\"> &\n Partial<Pick<Contact, \"phone_number\" | \"identifier\">>,\n ) => request<Contact>(\"POST\", idAccount, \"contacts\", body),\n updateContact: (\n idAccount: number,\n id: number,\n body: Partial<Pick<Contact, \"name\" | \"phone_number\" | \"identifier\">>,\n ) => request<Contact>(\"PUT\", idAccount, `contacts/${id}`, body),\n deleteContact: (idAccount: number, id: number) =>\n request<void>(\"DELETE\", idAccount, `contacts/${id}`),\n\n // --- Channels ---\n listChannels: (idAccount: number) =>\n request<Channel[]>(\"GET\", idAccount, \"channels\"),\n getChannel: (idAccount: number, id: number) =>\n request<Channel>(\"GET\", idAccount, `channels/${id}`),\n createChannel: (\n idAccount: number,\n body: Pick<Channel, \"name\" | \"type\" | \"provider\"> &\n Partial<Pick<Channel, \"identifier\" | \"id_agent\">>,\n ) => request<Channel>(\"POST\", idAccount, \"channels\", body),\n updateChannel: (\n idAccount: number,\n id: number,\n body: Partial<\n Pick<Channel, \"name\" | \"identifier\" | \"status\" | \"id_agent\">\n >,\n ) => request<Channel>(\"PUT\", idAccount, `channels/${id}`, body),\n deleteChannel: (idAccount: number, id: number) =>\n request<void>(\"DELETE\", idAccount, `channels/${id}`),\n\n // --- WhatsApp Channel Management ---\n connectChannel: (idAccount: number, id: number) =>\n request<unknown>(\"POST\", idAccount, `channels/${id}/connect`),\n getChannelQR: async (\n idAccount: number,\n id: number,\n ): Promise<string | null> => {\n const url = buildUrl(idAccount, `channels/${id}/qr`);\n const res = await fetch(url, {\n headers: {\n Accept: \"image/png\",\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!res.ok) return null;\n\n const contentType = res.headers.get(\"content-type\") || \"\";\n if (!contentType.includes(\"image/\")) return null;\n\n const blob = await res.blob();\n return new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onloadend = () => resolve(reader.result as string);\n reader.onerror = () => reject(new Error(\"Failed to read QR image\"));\n reader.readAsDataURL(blob);\n });\n },\n getChannelWhatsappStatus: (idAccount: number, id: number) =>\n request<WhatsappStatus>(\n \"GET\",\n idAccount,\n `channels/${id}/whatsapp-status`,\n ),\n disconnectChannel: (idAccount: number, id: number) =>\n request<unknown>(\"POST\", idAccount, `channels/${id}/disconnect`),\n logoutChannel: (idAccount: number, id: number) =>\n request<unknown>(\"POST\", idAccount, `channels/${id}/logout`),\n provisionChannel: (idAccount: number, id: number) =>\n request<unknown>(\"POST\", idAccount, `channels/${id}/provision`),\n };\n}\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@greatapps/greatchat-ui",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ }
12
+ },
13
+ "files": ["dist", "src"],
14
+ "scripts": {
15
+ "build": "tsup",
16
+ "dev": "tsup --watch"
17
+ },
18
+ "peerDependencies": {
19
+ "react": "^19",
20
+ "react-dom": "^19",
21
+ "@tanstack/react-query": "^5",
22
+ "lucide-react": "*",
23
+ "date-fns": "*",
24
+ "sonner": "*"
25
+ },
26
+ "dependencies": {
27
+ "clsx": "^2",
28
+ "tailwind-merge": "^3"
29
+ },
30
+ "devDependencies": {
31
+ "tsup": "latest",
32
+ "typescript": "^5",
33
+ "react": "^19",
34
+ "react-dom": "^19",
35
+ "@types/react": "latest",
36
+ "@types/react-dom": "latest",
37
+ "@tanstack/react-query": "latest"
38
+ }
39
+ }
@@ -0,0 +1,191 @@
1
+ import type {
2
+ ApiResponse,
3
+ Channel,
4
+ Contact,
5
+ Inbox,
6
+ InboxMessage,
7
+ InboxStats,
8
+ WhatsappStatus,
9
+ } from "../types";
10
+
11
+ export interface GchatClientConfig {
12
+ baseUrl: string;
13
+ token: string;
14
+ language?: string;
15
+ idWl?: number;
16
+ }
17
+
18
+ export function createGchatClient(config: GchatClientConfig) {
19
+ const { baseUrl, token, language = "pt-br", idWl = 1 } = config;
20
+
21
+ function buildUrl(idAccount: number, path: string): string {
22
+ return `${baseUrl}/v1/${language}/${idWl}/accounts/${idAccount}/${path}`;
23
+ }
24
+
25
+ async function request<T>(
26
+ method: string,
27
+ idAccount: number,
28
+ path: string,
29
+ body?: unknown,
30
+ params?: Record<string, string>,
31
+ ): Promise<ApiResponse<T>> {
32
+ const url = new URL(buildUrl(idAccount, path));
33
+ if (params) {
34
+ Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));
35
+ }
36
+
37
+ const res = await fetch(url.toString(), {
38
+ method,
39
+ headers: {
40
+ "Content-Type": "application/json",
41
+ Authorization: `Bearer ${token}`,
42
+ },
43
+ body: body ? JSON.stringify(body) : undefined,
44
+ });
45
+
46
+ if (!res.ok) {
47
+ let message = `HTTP ${res.status}: ${res.statusText}`;
48
+ try {
49
+ const errorBody = await res.json();
50
+ message = errorBody.message || message;
51
+ } catch {
52
+ /* ignore parse errors */
53
+ }
54
+ throw new Error(message);
55
+ }
56
+
57
+ const json: ApiResponse<T> = await res.json();
58
+
59
+ // For mutations, treat API-level failures as errors so TanStack Query
60
+ // fires onError instead of onSuccess (API returns HTTP 200 even on failure)
61
+ if (method !== "GET" && json.status === 0) {
62
+ throw new Error(json.message || "Operação falhou");
63
+ }
64
+
65
+ return json;
66
+ }
67
+
68
+ return {
69
+ // --- Inboxes ---
70
+ listInboxes: (idAccount: number, params?: Record<string, string>) =>
71
+ request<Inbox[]>("GET", idAccount, "inboxes", undefined, params),
72
+ getInbox: (idAccount: number, id: number) =>
73
+ request<Inbox>("GET", idAccount, `inboxes/${id}`),
74
+ getInboxStats: (idAccount: number) =>
75
+ request<InboxStats>("GET", idAccount, "inboxes/stats"),
76
+ createInbox: (
77
+ idAccount: number,
78
+ body: { id_channel: number; id_contact: number },
79
+ ) => request<Inbox>("POST", idAccount, "inboxes", body),
80
+ updateInbox: (
81
+ idAccount: number,
82
+ id: number,
83
+ body: Partial<Pick<Inbox, "status" | "id_agent">>,
84
+ ) => request<Inbox>("PUT", idAccount, `inboxes/${id}`, body),
85
+ deleteInbox: (idAccount: number, id: number) =>
86
+ request<void>("DELETE", idAccount, `inboxes/${id}`),
87
+
88
+ // --- Inbox Messages ---
89
+ listInboxMessages: (idAccount: number, params: Record<string, string>) =>
90
+ request<InboxMessage[]>(
91
+ "GET",
92
+ idAccount,
93
+ "inbox-messages",
94
+ undefined,
95
+ params,
96
+ ),
97
+ sendMessage: (
98
+ idAccount: number,
99
+ body: {
100
+ id_inbox: number;
101
+ content: string;
102
+ content_type?: string;
103
+ source?: string;
104
+ direction: "outbound";
105
+ },
106
+ ) => request<InboxMessage>("POST", idAccount, "inbox-messages", body),
107
+ revokeMessage: (idAccount: number, id: number) =>
108
+ request<void>("POST", idAccount, `inbox-messages/${id}/revoke`),
109
+ editMessage: (idAccount: number, id: number, body: { content: string }) =>
110
+ request<void>("POST", idAccount, `inbox-messages/${id}/edit`, body),
111
+
112
+ // --- Contacts ---
113
+ listContacts: (idAccount: number, params?: Record<string, string>) =>
114
+ request<Contact[]>("GET", idAccount, "contacts", undefined, params),
115
+ getContact: (idAccount: number, id: number) =>
116
+ request<Contact>("GET", idAccount, `contacts/${id}`),
117
+ createContact: (
118
+ idAccount: number,
119
+ body: Pick<Contact, "name"> &
120
+ Partial<Pick<Contact, "phone_number" | "identifier">>,
121
+ ) => request<Contact>("POST", idAccount, "contacts", body),
122
+ updateContact: (
123
+ idAccount: number,
124
+ id: number,
125
+ body: Partial<Pick<Contact, "name" | "phone_number" | "identifier">>,
126
+ ) => request<Contact>("PUT", idAccount, `contacts/${id}`, body),
127
+ deleteContact: (idAccount: number, id: number) =>
128
+ request<void>("DELETE", idAccount, `contacts/${id}`),
129
+
130
+ // --- Channels ---
131
+ listChannels: (idAccount: number) =>
132
+ request<Channel[]>("GET", idAccount, "channels"),
133
+ getChannel: (idAccount: number, id: number) =>
134
+ request<Channel>("GET", idAccount, `channels/${id}`),
135
+ createChannel: (
136
+ idAccount: number,
137
+ body: Pick<Channel, "name" | "type" | "provider"> &
138
+ Partial<Pick<Channel, "identifier" | "id_agent">>,
139
+ ) => request<Channel>("POST", idAccount, "channels", body),
140
+ updateChannel: (
141
+ idAccount: number,
142
+ id: number,
143
+ body: Partial<
144
+ Pick<Channel, "name" | "identifier" | "status" | "id_agent">
145
+ >,
146
+ ) => request<Channel>("PUT", idAccount, `channels/${id}`, body),
147
+ deleteChannel: (idAccount: number, id: number) =>
148
+ request<void>("DELETE", idAccount, `channels/${id}`),
149
+
150
+ // --- WhatsApp Channel Management ---
151
+ connectChannel: (idAccount: number, id: number) =>
152
+ request<unknown>("POST", idAccount, `channels/${id}/connect`),
153
+ getChannelQR: async (
154
+ idAccount: number,
155
+ id: number,
156
+ ): Promise<string | null> => {
157
+ const url = buildUrl(idAccount, `channels/${id}/qr`);
158
+ const res = await fetch(url, {
159
+ headers: {
160
+ Accept: "image/png",
161
+ Authorization: `Bearer ${token}`,
162
+ },
163
+ });
164
+
165
+ if (!res.ok) return null;
166
+
167
+ const contentType = res.headers.get("content-type") || "";
168
+ if (!contentType.includes("image/")) return null;
169
+
170
+ const blob = await res.blob();
171
+ return new Promise<string>((resolve, reject) => {
172
+ const reader = new FileReader();
173
+ reader.onloadend = () => resolve(reader.result as string);
174
+ reader.onerror = () => reject(new Error("Failed to read QR image"));
175
+ reader.readAsDataURL(blob);
176
+ });
177
+ },
178
+ getChannelWhatsappStatus: (idAccount: number, id: number) =>
179
+ request<WhatsappStatus>(
180
+ "GET",
181
+ idAccount,
182
+ `channels/${id}/whatsapp-status`,
183
+ ),
184
+ disconnectChannel: (idAccount: number, id: number) =>
185
+ request<unknown>("POST", idAccount, `channels/${id}/disconnect`),
186
+ logoutChannel: (idAccount: number, id: number) =>
187
+ request<unknown>("POST", idAccount, `channels/${id}/logout`),
188
+ provisionChannel: (idAccount: number, id: number) =>
189
+ request<unknown>("POST", idAccount, `channels/${id}/provision`),
190
+ };
191
+ }
package/src/index.ts ADDED
@@ -0,0 +1,18 @@
1
+ // Types
2
+ export type {
3
+ ApiResponse,
4
+ Channel,
5
+ Contact,
6
+ Inbox,
7
+ InboxMessage,
8
+ InboxStats,
9
+ WhatsappStatus,
10
+ MessageContentType,
11
+ } from "./types";
12
+
13
+ // Client
14
+ export { createGchatClient } from "./client";
15
+ export type { GchatClientConfig } from "./client";
16
+
17
+ // Utils
18
+ export { cn } from "./lib/utils";
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -0,0 +1,117 @@
1
+ // === API Response ===
2
+
3
+ export interface ApiResponse<T> {
4
+ status: 0 | 1;
5
+ message: string;
6
+ data: T;
7
+ total?: number;
8
+ }
9
+
10
+ // === Channel ===
11
+
12
+ export interface Channel {
13
+ id: number;
14
+ id_account: number;
15
+ name: string;
16
+ type: "whatsapp_unofficial";
17
+ provider: "gwhatsmeow";
18
+ identifier: string | null;
19
+ external_id: string | null;
20
+ config: string | null;
21
+ status: "active" | "inactive";
22
+ id_agent: number | null;
23
+ deleted: number;
24
+ datetime_add: string;
25
+ datetime_alt: string | null;
26
+ }
27
+
28
+ // === Contact ===
29
+
30
+ export interface Contact {
31
+ id: number;
32
+ id_account: number;
33
+ name: string;
34
+ phone_number: string | null;
35
+ identifier: string | null;
36
+ metadata: string | null;
37
+ deleted: number;
38
+ datetime_add: string;
39
+ datetime_alt: string | null;
40
+ }
41
+
42
+ // === Inbox ===
43
+
44
+ export interface Inbox {
45
+ id: number;
46
+ id_account: number;
47
+ id_channel: number;
48
+ id_contact: number;
49
+ id_agent: number | null;
50
+ status: "open" | "pending" | "resolved";
51
+ last_message_at: string | null;
52
+ external_id: string | null;
53
+ // Enriched fields (from JOIN queries)
54
+ contact_name: string | null;
55
+ contact_phone: string | null;
56
+ contact_identifier: string | null;
57
+ channel_name: string | null;
58
+ channel_identifier: string | null;
59
+ channel_type: string | null;
60
+ last_message_content: string | null;
61
+ last_message_content_type: string | null;
62
+ last_message_direction: "inbound" | "outbound" | null;
63
+ last_message_source: "contact" | "agent" | "bot" | null;
64
+ datetime_add: string;
65
+ datetime_alt: string | null;
66
+ }
67
+
68
+ // === Message Content Type ===
69
+
70
+ export type MessageContentType =
71
+ | "text"
72
+ | "image"
73
+ | "audio"
74
+ | "video"
75
+ | "document"
76
+ | "location"
77
+ | "contacts";
78
+
79
+ // === Inbox Message ===
80
+
81
+ export interface InboxMessage {
82
+ id: number;
83
+ id_account: number;
84
+ id_inbox: number;
85
+ id_contact: number | null;
86
+ direction: "inbound" | "outbound";
87
+ content: string | null;
88
+ content_type: MessageContentType;
89
+ content_url: string | null;
90
+ metadata: string | null;
91
+ external_id: string | null;
92
+ status: "sent" | "delivered" | "read" | "failed" | "received" | "pending";
93
+ source: "contact" | "agent" | "bot";
94
+ is_private: boolean;
95
+ datetime_add: string;
96
+ datetime_alt: string | null;
97
+ /** UI-only: error message when status is "failed" */
98
+ _error?: string;
99
+ }
100
+
101
+ // === Inbox Stats ===
102
+
103
+ export interface InboxStats {
104
+ total: number;
105
+ open: number;
106
+ pending: number;
107
+ resolved: number;
108
+ }
109
+
110
+ // === WhatsApp Status ===
111
+
112
+ export interface WhatsappStatus {
113
+ status: string;
114
+ connected: boolean;
115
+ logged_in: boolean;
116
+ jid?: string;
117
+ }