@ezychat/web 0.1.45 → 0.1.47
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-lib/chunks/{admin-DviFuAwJ.js → admin-AHBgqp7Z.js} +64 -4
- package/dist-lib/chunks/{admin-CO4a2wgk.js → admin-C6ooeiNQ.js} +64 -4
- package/dist-lib/chunks/{app-C2A7ZZsu.js → app-3jWCMPbu.js} +3 -1
- package/dist-lib/chunks/{app-RX8Fy2Oz.js → app-Diwr0AD0.js} +3 -1
- package/dist-lib/chunks/{inbox-DccyncVL.js → inbox-BiE_pegy.js} +27 -13
- package/dist-lib/chunks/{inbox-Bdg6faLa.js → inbox-DSbsQcvO.js} +27 -13
- package/dist-lib/ezychat-web.css +1 -1
- package/dist-lib/index.js +2853 -1809
- package/dist-lib/types/admin/AdminLayout.d.ts +14 -1
- package/dist-lib/types/admin/InboxAccess.d.ts +9 -0
- package/dist-lib/types/admin/InboxesPage.d.ts +3 -0
- package/dist-lib/types/admin/inbox-status.d.ts +16 -0
- package/dist-lib/types/api/ai.d.ts +2 -0
- package/dist-lib/types/api/inbox-mutations.d.ts +65 -0
- package/dist-lib/types/api/queries.d.ts +63 -3
- package/dist-lib/types/extensions/types.d.ts +1 -1
- package/dist-lib/types/i18n/resources.d.ts +76 -0
- package/dist-lib/types/inbox/ChatFilters.d.ts +4 -1
- package/dist-lib/types/inbox/ChatListItem.d.ts +4 -2
- package/dist-lib/types/inbox/ConversationHeader.d.ts +4 -2
- package/dist-lib/types/inbox/InboxChip.d.ts +6 -0
- package/dist-lib/types/inbox/WaBanner.d.ts +6 -1
- package/dist-lib/types/inbox/useDirectory.d.ts +20 -3
- package/dist-lib/types/inbox/useVisibleInboxes.d.ts +12 -0
- package/dist-lib/types/lib/whatsapp-format.d.ts +5 -0
- package/dist-lib/types/wa/PhoneLink.d.ts +7 -2
- package/package.json +2 -2
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { type LucideIcon } from 'lucide-react';
|
|
2
2
|
import { type WebExtension, type WebExtensionAdminSection } from '../extensions';
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Every core admin section. `labelKey` is a translation key in the `admin` namespace. WhatsApp and
|
|
5
|
+
* Inboxes never show together: see `visibleAdminNav`.
|
|
6
|
+
*/
|
|
4
7
|
export declare const ADMIN_NAV: readonly [{
|
|
5
8
|
readonly to: "members";
|
|
6
9
|
readonly labelKey: "nav.members";
|
|
@@ -13,6 +16,10 @@ export declare const ADMIN_NAV: readonly [{
|
|
|
13
16
|
readonly to: "whatsapp";
|
|
14
17
|
readonly labelKey: "nav.whatsapp";
|
|
15
18
|
readonly icon: LucideIcon;
|
|
19
|
+
}, {
|
|
20
|
+
readonly to: "inboxes";
|
|
21
|
+
readonly labelKey: "inboxes.title";
|
|
22
|
+
readonly icon: LucideIcon;
|
|
16
23
|
}, {
|
|
17
24
|
readonly to: "tunnel";
|
|
18
25
|
readonly labelKey: "nav.tunnel";
|
|
@@ -26,6 +33,12 @@ export declare const ADMIN_NAV: readonly [{
|
|
|
26
33
|
readonly labelKey: "nav.audit";
|
|
27
34
|
readonly icon: LucideIcon;
|
|
28
35
|
}];
|
|
36
|
+
export type AdminNavItem = (typeof ADMIN_NAV)[number];
|
|
37
|
+
/**
|
|
38
|
+
* The sections in the nav: **Inboxes** replaces **WhatsApp** only in an edition that allows more than
|
|
39
|
+
* one WhatsApp number (Lite allows one, so it keeps WhatsApp).
|
|
40
|
+
*/
|
|
41
|
+
export declare function visibleAdminNav(multiInbox: boolean): AdminNavItem[];
|
|
29
42
|
/** The core section an extension admin route declares for this path (e.g. `members`). */
|
|
30
43
|
export declare function extensionSectionOf(extensions: readonly WebExtension[], pathname: string): WebExtensionAdminSection | undefined;
|
|
31
44
|
/**
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Inbox, User } from '@ezychat/shared';
|
|
2
|
+
/**
|
|
3
|
+
* Members → Inbox access: which inboxes a member may open. Admins always see every inbox; an agent
|
|
4
|
+
* sees only the chats (and notifications) of the inboxes ticked here. `inboxes` are the active ones.
|
|
5
|
+
*/
|
|
6
|
+
export declare function InboxAccessCell({ user, inboxes }: Readonly<{
|
|
7
|
+
user: User;
|
|
8
|
+
inboxes: Inbox[];
|
|
9
|
+
}>): import("react").JSX.Element;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { WaState } from '@ezychat/shared';
|
|
2
|
+
import type { StatusTone } from '../components/app';
|
|
3
|
+
/** What the inbox card says next to its status: scan the QR code, or the phone is offline. */
|
|
4
|
+
export type InboxNotice = 'scan' | 'offline' | 'replaced' | null;
|
|
5
|
+
export interface InboxCardStatus {
|
|
6
|
+
tone: StatusTone;
|
|
7
|
+
/** Translation key in the `admin` namespace. */
|
|
8
|
+
labelKey: string;
|
|
9
|
+
notice: InboxNotice;
|
|
10
|
+
/** The phone must scan a QR code (or enter a pairing code) before the inbox works. */
|
|
11
|
+
needsLink: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** WhatsApp connection state of one inbox → the status its card shows. */
|
|
14
|
+
export declare function inboxCardStatus(state: WaState): InboxCardStatus;
|
|
15
|
+
/** Phone-number digits from WhatsApp (`60112233445`) → `+60112233445`; anything else as it is. */
|
|
16
|
+
export declare function formatInboxNumber(number: string | null): string | null;
|
|
@@ -12,6 +12,7 @@ export declare function useAiMember(): import("@tanstack/react-query").UseQueryR
|
|
|
12
12
|
locale: "en" | "ms" | "zh-CN" | null;
|
|
13
13
|
kind?: "human" | "ai" | undefined;
|
|
14
14
|
aiRole?: "sales" | undefined;
|
|
15
|
+
inboxIds?: number[] | undefined;
|
|
15
16
|
} | null;
|
|
16
17
|
settings: {
|
|
17
18
|
displayName: string;
|
|
@@ -146,6 +147,7 @@ export declare function useAiMemberAction(): import("@tanstack/react-query").Use
|
|
|
146
147
|
locale: "en" | "ms" | "zh-CN" | null;
|
|
147
148
|
kind?: "human" | "ai" | undefined;
|
|
148
149
|
aiRole?: "sales" | undefined;
|
|
150
|
+
inboxIds?: number[] | undefined;
|
|
149
151
|
} | null;
|
|
150
152
|
settings: {
|
|
151
153
|
displayName: string;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { UpdateInboxBody } from '@ezychat/shared';
|
|
2
|
+
import { type WaAction } from './queries';
|
|
3
|
+
export declare function useCreateInbox(): import("@tanstack/react-query").UseMutationResult<{
|
|
4
|
+
id: number;
|
|
5
|
+
name: string;
|
|
6
|
+
tone: "success" | "primary" | "warning" | "info";
|
|
7
|
+
number: string | null;
|
|
8
|
+
status: {
|
|
9
|
+
state: "open" | "qr" | "disconnected" | "connecting" | "logged_out" | "replaced" | "blocked";
|
|
10
|
+
me: {
|
|
11
|
+
jid: string;
|
|
12
|
+
name: string | null;
|
|
13
|
+
lid?: string | null | undefined;
|
|
14
|
+
} | null;
|
|
15
|
+
qr: string | null;
|
|
16
|
+
lastError: string | null;
|
|
17
|
+
};
|
|
18
|
+
removed: boolean;
|
|
19
|
+
memberIds?: number[] | undefined;
|
|
20
|
+
}, Error, {
|
|
21
|
+
name: string;
|
|
22
|
+
memberIds?: number[] | undefined;
|
|
23
|
+
}, unknown>;
|
|
24
|
+
export declare function useUpdateInbox(): import("@tanstack/react-query").UseMutationResult<{
|
|
25
|
+
id: number;
|
|
26
|
+
name: string;
|
|
27
|
+
tone: "success" | "primary" | "warning" | "info";
|
|
28
|
+
number: string | null;
|
|
29
|
+
status: {
|
|
30
|
+
state: "open" | "qr" | "disconnected" | "connecting" | "logged_out" | "replaced" | "blocked";
|
|
31
|
+
me: {
|
|
32
|
+
jid: string;
|
|
33
|
+
name: string | null;
|
|
34
|
+
lid?: string | null | undefined;
|
|
35
|
+
} | null;
|
|
36
|
+
qr: string | null;
|
|
37
|
+
lastError: string | null;
|
|
38
|
+
};
|
|
39
|
+
removed: boolean;
|
|
40
|
+
memberIds?: number[] | undefined;
|
|
41
|
+
}, Error, {
|
|
42
|
+
id: number;
|
|
43
|
+
body: UpdateInboxBody;
|
|
44
|
+
}, unknown>;
|
|
45
|
+
export declare function useRemoveInbox(): import("@tanstack/react-query").UseMutationResult<unknown, Error, number, unknown>;
|
|
46
|
+
/** Log out, link again or take over the phone of one inbox. */
|
|
47
|
+
export declare function useInboxWaAction(inboxId: number): import("@tanstack/react-query").UseMutationResult<unknown, Error, WaAction, unknown>;
|
|
48
|
+
/** Link one inbox by phone number: the 8-character code to enter in WhatsApp → Linked devices. */
|
|
49
|
+
export declare function useInboxPairingCode(inboxId: number): import("@tanstack/react-query").UseMutationResult<{
|
|
50
|
+
code: string;
|
|
51
|
+
}, Error, string, unknown>;
|
|
52
|
+
/** Members → Inbox access: the inboxes one agent may open. */
|
|
53
|
+
export declare function useSetUserInboxes(userId: number): import("@tanstack/react-query").UseMutationResult<{
|
|
54
|
+
id: number;
|
|
55
|
+
username: string;
|
|
56
|
+
displayName: string;
|
|
57
|
+
role: "admin" | "agent";
|
|
58
|
+
mustChangePassword: boolean;
|
|
59
|
+
disabled: boolean;
|
|
60
|
+
createdAt: number;
|
|
61
|
+
locale: "en" | "ms" | "zh-CN" | null;
|
|
62
|
+
kind?: "human" | "ai" | undefined;
|
|
63
|
+
aiRole?: "sales" | undefined;
|
|
64
|
+
inboxIds?: number[] | undefined;
|
|
65
|
+
}, Error, number[], unknown>;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { type InfiniteData, type QueryClient } from '@tanstack/react-query';
|
|
2
|
-
import { type CreateApiTokenBodyInput, type UpdateApiTokenBody, type Chat, type ChatListResponse, type ChatStatus, type Message, type MessageListResponse, type PatchUserBody, type Settings, type
|
|
2
|
+
import { type CreateApiTokenBodyInput, type UpdateApiTokenBody, type Chat, type ChatListResponse, type ChatStatus, type DirectoryUser, type InboxStatusPayload, type Message, type MessageListResponse, type PatchUserBody, type Settings, type WaStatus } from '@ezychat/shared';
|
|
3
3
|
export interface ChatFilters {
|
|
4
4
|
status?: ChatStatus;
|
|
5
5
|
assigned: 'me' | 'none' | 'any';
|
|
6
6
|
q?: string;
|
|
7
7
|
/** customer tag filter */
|
|
8
8
|
tag?: string;
|
|
9
|
+
/** One inbox (multi-inbox); unset = every inbox the user may open. */
|
|
10
|
+
inbox?: number;
|
|
9
11
|
}
|
|
10
12
|
export declare const qk: {
|
|
11
13
|
me: readonly ["me"];
|
|
@@ -24,6 +26,7 @@ export declare const qk: {
|
|
|
24
26
|
users: readonly ["users"];
|
|
25
27
|
directory: readonly ["users", "directory"];
|
|
26
28
|
wa: readonly ["wa"];
|
|
29
|
+
inboxes: readonly ["inboxes"];
|
|
27
30
|
tunnel: readonly ["tunnel"];
|
|
28
31
|
cloudflareSetup: readonly ["cloudflare-setup"];
|
|
29
32
|
settings: readonly ["settings"];
|
|
@@ -65,6 +68,7 @@ export declare function useMe(): import("@tanstack/react-query").UseQueryResult<
|
|
|
65
68
|
locale: "en" | "ms" | "zh-CN" | null;
|
|
66
69
|
kind?: "human" | "ai" | undefined;
|
|
67
70
|
aiRole?: "sales" | undefined;
|
|
71
|
+
inboxIds?: number[] | undefined;
|
|
68
72
|
} | null, Error>;
|
|
69
73
|
export declare function useSetupStatus(): import("@tanstack/react-query").UseQueryResult<{
|
|
70
74
|
needsSetup: boolean;
|
|
@@ -86,6 +90,7 @@ export declare function useLogin(): import("@tanstack/react-query").UseMutationR
|
|
|
86
90
|
locale: "en" | "ms" | "zh-CN" | null;
|
|
87
91
|
kind?: "human" | "ai" | undefined;
|
|
88
92
|
aiRole?: "sales" | undefined;
|
|
93
|
+
inboxIds?: number[] | undefined;
|
|
89
94
|
} | null, Error, {
|
|
90
95
|
username: string;
|
|
91
96
|
password: string;
|
|
@@ -108,6 +113,7 @@ export declare function useSetMyLocale(): import("@tanstack/react-query").UseMut
|
|
|
108
113
|
locale: "en" | "ms" | "zh-CN" | null;
|
|
109
114
|
kind?: "human" | "ai" | undefined;
|
|
110
115
|
aiRole?: "sales" | undefined;
|
|
116
|
+
inboxIds?: number[] | undefined;
|
|
111
117
|
}, Error, "en" | "ms" | "zh-CN" | null, unknown>;
|
|
112
118
|
export declare function useChangePassword(): import("@tanstack/react-query").UseMutationResult<unknown, Error, {
|
|
113
119
|
currentPassword: string;
|
|
@@ -116,6 +122,8 @@ export declare function useChangePassword(): import("@tanstack/react-query").Use
|
|
|
116
122
|
export declare function useChats(filters: ChatFilters): import("@tanstack/react-query").UseInfiniteQueryResult<InfiniteData<{
|
|
117
123
|
chats: {
|
|
118
124
|
jid: string;
|
|
125
|
+
inboxId: number;
|
|
126
|
+
waJid: string;
|
|
119
127
|
type: "dm" | "group";
|
|
120
128
|
name: string;
|
|
121
129
|
avatarUrl: string | null;
|
|
@@ -134,6 +142,8 @@ export declare function useChats(filters: ChatFilters): import("@tanstack/react-
|
|
|
134
142
|
export declare function useChat(jid: string | null | undefined): import("@tanstack/react-query").UseQueryResult<{
|
|
135
143
|
chat: {
|
|
136
144
|
jid: string;
|
|
145
|
+
inboxId: number;
|
|
146
|
+
waJid: string;
|
|
137
147
|
type: "dm" | "group";
|
|
138
148
|
name: string;
|
|
139
149
|
avatarUrl: string | null;
|
|
@@ -300,6 +310,8 @@ export declare function useRetryMessage(): import("@tanstack/react-query").UseMu
|
|
|
300
310
|
}, Error, string, unknown>;
|
|
301
311
|
export declare function usePatchChat(jid: string): import("@tanstack/react-query").UseMutationResult<{
|
|
302
312
|
jid: string;
|
|
313
|
+
inboxId: number;
|
|
314
|
+
waJid: string;
|
|
303
315
|
type: "dm" | "group";
|
|
304
316
|
name: string;
|
|
305
317
|
avatarUrl: string | null;
|
|
@@ -448,9 +460,10 @@ export declare function useUsers(opts?: {
|
|
|
448
460
|
locale: "en" | "ms" | "zh-CN" | null;
|
|
449
461
|
kind?: "human" | "ai" | undefined;
|
|
450
462
|
aiRole?: "sales" | undefined;
|
|
463
|
+
inboxIds?: number[] | undefined;
|
|
451
464
|
}[], Error>;
|
|
452
465
|
/** Public team-member fields every logged-in user may see (`GET /api/users/directory`). */
|
|
453
|
-
export type DirectoryUser
|
|
466
|
+
export type { DirectoryUser };
|
|
454
467
|
/**
|
|
455
468
|
* Team directory for every role (agents included). Needs the server route
|
|
456
469
|
* `GET /api/users/directory`; when it is missing (404) or forbidden, resolves to an empty list
|
|
@@ -458,7 +471,15 @@ export type DirectoryUser = Pick<User, 'id' | 'displayName' | 'role' | 'disabled
|
|
|
458
471
|
*/
|
|
459
472
|
export declare function useUserDirectory(opts?: {
|
|
460
473
|
enabled?: boolean;
|
|
461
|
-
}): import("@tanstack/react-query").UseQueryResult<
|
|
474
|
+
}): import("@tanstack/react-query").UseQueryResult<{
|
|
475
|
+
id: number;
|
|
476
|
+
displayName: string;
|
|
477
|
+
role: "admin" | "agent";
|
|
478
|
+
disabled: boolean;
|
|
479
|
+
inboxIds: "all" | number[];
|
|
480
|
+
kind?: "human" | "ai" | undefined;
|
|
481
|
+
aiRole?: "sales" | undefined;
|
|
482
|
+
}[], Error>;
|
|
462
483
|
export declare function useCreateUser(): import("@tanstack/react-query").UseMutationResult<{
|
|
463
484
|
id: number;
|
|
464
485
|
username: string;
|
|
@@ -470,6 +491,7 @@ export declare function useCreateUser(): import("@tanstack/react-query").UseMuta
|
|
|
470
491
|
locale: "en" | "ms" | "zh-CN" | null;
|
|
471
492
|
kind?: "human" | "ai" | undefined;
|
|
472
493
|
aiRole?: "sales" | undefined;
|
|
494
|
+
inboxIds?: number[] | undefined;
|
|
473
495
|
}, Error, {
|
|
474
496
|
username: string;
|
|
475
497
|
displayName: string;
|
|
@@ -487,6 +509,7 @@ export declare function usePatchUser(): import("@tanstack/react-query").UseMutat
|
|
|
487
509
|
locale: "en" | "ms" | "zh-CN" | null;
|
|
488
510
|
kind?: "human" | "ai" | undefined;
|
|
489
511
|
aiRole?: "sales" | undefined;
|
|
512
|
+
inboxIds?: number[] | undefined;
|
|
490
513
|
}, Error, {
|
|
491
514
|
id: number;
|
|
492
515
|
patch: PatchUserBody;
|
|
@@ -507,6 +530,43 @@ export declare function useWaStatus(opts?: {
|
|
|
507
530
|
qr: string | null;
|
|
508
531
|
lastError: string | null;
|
|
509
532
|
}, Error>;
|
|
533
|
+
/** The inbox 1 number (the number linked before multi-inbox). */
|
|
534
|
+
export declare const PRIMARY_INBOX_ID = 1;
|
|
535
|
+
/**
|
|
536
|
+
* The inboxes the signed-in user may open (`GET /api/inboxes`; the server filters them). A server
|
|
537
|
+
* without the route (404) counts as one inbox, so no inbox UI shows.
|
|
538
|
+
*/
|
|
539
|
+
export declare function useInboxes(opts?: {
|
|
540
|
+
enabled?: boolean;
|
|
541
|
+
}): import("@tanstack/react-query").UseQueryResult<{
|
|
542
|
+
inboxes: {
|
|
543
|
+
id: number;
|
|
544
|
+
name: string;
|
|
545
|
+
tone: "success" | "primary" | "warning" | "info";
|
|
546
|
+
number: string | null;
|
|
547
|
+
status: {
|
|
548
|
+
state: "open" | "qr" | "disconnected" | "connecting" | "logged_out" | "replaced" | "blocked";
|
|
549
|
+
me: {
|
|
550
|
+
jid: string;
|
|
551
|
+
name: string | null;
|
|
552
|
+
lid?: string | null | undefined;
|
|
553
|
+
} | null;
|
|
554
|
+
qr: string | null;
|
|
555
|
+
lastError: string | null;
|
|
556
|
+
};
|
|
557
|
+
removed: boolean;
|
|
558
|
+
memberIds?: number[] | undefined;
|
|
559
|
+
}[];
|
|
560
|
+
maxInboxes: number;
|
|
561
|
+
canAdd: boolean;
|
|
562
|
+
}, Error>;
|
|
563
|
+
/** Applies a socket `inbox:status` to the cached inbox list. */
|
|
564
|
+
export declare function patchInboxStatusInCache(qc: QueryClient, p: InboxStatusPayload): void;
|
|
565
|
+
/**
|
|
566
|
+
* Connection status of one inbox. Inbox 1 reads `GET /api/wa/status` as before (Lite unchanged);
|
|
567
|
+
* other inboxes read the inbox list, which `inbox:status` keeps current.
|
|
568
|
+
*/
|
|
569
|
+
export declare function useInboxStatus(inboxId: number | undefined): WaStatus | undefined;
|
|
510
570
|
export type WaAction = 'logout' | 'relink' | 'takeover';
|
|
511
571
|
export declare function useWaAction(): import("@tanstack/react-query").UseMutationResult<unknown, Error, WaAction, unknown>;
|
|
512
572
|
/** Link by phone number: returns the 8-character code to enter in WhatsApp → Linked devices. */
|
|
@@ -36,7 +36,7 @@ export interface WebExtensionRoute {
|
|
|
36
36
|
section?: WebExtensionAdminSection;
|
|
37
37
|
}
|
|
38
38
|
/** Core admin sections an extension admin route may sit under (the `ADMIN_NAV` paths). */
|
|
39
|
-
export declare const WEB_EXTENSION_ADMIN_SECTIONS: readonly ["members", "quick-replies", "whatsapp", "tunnel", "settings", "audit"];
|
|
39
|
+
export declare const WEB_EXTENSION_ADMIN_SECTIONS: readonly ["members", "quick-replies", "whatsapp", "inboxes", "tunnel", "settings", "audit"];
|
|
40
40
|
export type WebExtensionAdminSection = (typeof WEB_EXTENSION_ADMIN_SECTIONS)[number];
|
|
41
41
|
/** A small badge on an admin nav link: a count, or a dot that asks for attention. */
|
|
42
42
|
export interface WebExtensionNavBadge {
|
|
@@ -149,6 +149,8 @@ export declare const enResources: {
|
|
|
149
149
|
noInstallerFeed: string;
|
|
150
150
|
unverifiedFeed: string;
|
|
151
151
|
releaseNotes: string;
|
|
152
|
+
whatsNew: string;
|
|
153
|
+
updatedTo: string;
|
|
152
154
|
};
|
|
153
155
|
share: {
|
|
154
156
|
title: string;
|
|
@@ -628,6 +630,7 @@ export declare const enResources: {
|
|
|
628
630
|
waBanner: {
|
|
629
631
|
settingsShort: string;
|
|
630
632
|
openSettings: string;
|
|
633
|
+
openInboxes: string;
|
|
631
634
|
connecting: {
|
|
632
635
|
title: string;
|
|
633
636
|
body: string;
|
|
@@ -652,6 +655,19 @@ export declare const enResources: {
|
|
|
652
655
|
title: string;
|
|
653
656
|
body: string;
|
|
654
657
|
};
|
|
658
|
+
inbox: {
|
|
659
|
+
connecting: string;
|
|
660
|
+
disconnected: string;
|
|
661
|
+
qr: string;
|
|
662
|
+
loggedOut: string;
|
|
663
|
+
replaced: string;
|
|
664
|
+
blocked: string;
|
|
665
|
+
};
|
|
666
|
+
};
|
|
667
|
+
inboxFilter: {
|
|
668
|
+
label: string;
|
|
669
|
+
all: string;
|
|
670
|
+
option: string;
|
|
655
671
|
};
|
|
656
672
|
phoneLink: {
|
|
657
673
|
label: string;
|
|
@@ -994,6 +1010,10 @@ export declare const enResources: {
|
|
|
994
1010
|
apiTokenAuthFailed: string;
|
|
995
1011
|
mcpEnable: string;
|
|
996
1012
|
mcpDisable: string;
|
|
1013
|
+
inboxCreate: string;
|
|
1014
|
+
inboxUpdate: string;
|
|
1015
|
+
inboxRemove: string;
|
|
1016
|
+
inboxAccessUpdate: string;
|
|
997
1017
|
};
|
|
998
1018
|
fields: {
|
|
999
1019
|
chat: string;
|
|
@@ -1033,6 +1053,9 @@ export declare const enResources: {
|
|
|
1033
1053
|
lanEnabled: string;
|
|
1034
1054
|
historyDays: string;
|
|
1035
1055
|
expires: string;
|
|
1056
|
+
inbox: string;
|
|
1057
|
+
inboxes: string;
|
|
1058
|
+
members: string;
|
|
1036
1059
|
};
|
|
1037
1060
|
reasons: {
|
|
1038
1061
|
rateLimited: string;
|
|
@@ -1694,6 +1717,59 @@ export declare const enResources: {
|
|
|
1694
1717
|
correction: string;
|
|
1695
1718
|
};
|
|
1696
1719
|
};
|
|
1720
|
+
inboxes: {
|
|
1721
|
+
title: string;
|
|
1722
|
+
add: string;
|
|
1723
|
+
emptyTitle: string;
|
|
1724
|
+
emptyBody: string;
|
|
1725
|
+
description: string;
|
|
1726
|
+
edit: string;
|
|
1727
|
+
remove: string;
|
|
1728
|
+
status: string;
|
|
1729
|
+
whoCanOpen: string;
|
|
1730
|
+
admin: string;
|
|
1731
|
+
accessHint: string;
|
|
1732
|
+
typeLinkedPhone: string;
|
|
1733
|
+
numberAfterScan: string;
|
|
1734
|
+
limitReached: string;
|
|
1735
|
+
membersField: string;
|
|
1736
|
+
membersFieldHint: string;
|
|
1737
|
+
membersFieldNoAgents: string;
|
|
1738
|
+
membersFieldNone: string;
|
|
1739
|
+
removeTitle: string;
|
|
1740
|
+
removeConfirm: string;
|
|
1741
|
+
removeBodyPhone: string;
|
|
1742
|
+
removed: string;
|
|
1743
|
+
required: string;
|
|
1744
|
+
saved: string;
|
|
1745
|
+
createdPhone: string;
|
|
1746
|
+
addTitle: string;
|
|
1747
|
+
editTitle: string;
|
|
1748
|
+
dialogDescription: string;
|
|
1749
|
+
save: string;
|
|
1750
|
+
name: string;
|
|
1751
|
+
nameHint: string;
|
|
1752
|
+
scanTitle: string;
|
|
1753
|
+
scanBody: string;
|
|
1754
|
+
scanSteps: string;
|
|
1755
|
+
linkedAs: string;
|
|
1756
|
+
relink: string;
|
|
1757
|
+
offlineTitle: string;
|
|
1758
|
+
offlineBody: string;
|
|
1759
|
+
state: {
|
|
1760
|
+
connected: string;
|
|
1761
|
+
waiting_scan: string;
|
|
1762
|
+
phone_offline: string;
|
|
1763
|
+
};
|
|
1764
|
+
membersAccess: string;
|
|
1765
|
+
membersAccessAll: string;
|
|
1766
|
+
membersAccessNone: string;
|
|
1767
|
+
membersAccessTitle: string;
|
|
1768
|
+
membersAccessDescription: string;
|
|
1769
|
+
membersAccessNoInboxes: string;
|
|
1770
|
+
membersAccessSave: string;
|
|
1771
|
+
membersAccessSaved: string;
|
|
1772
|
+
};
|
|
1697
1773
|
};
|
|
1698
1774
|
errors: {
|
|
1699
1775
|
generic: string;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import type { Inbox } from '@ezychat/shared';
|
|
1
2
|
import { type ChatFilters as Filters } from '../api/queries';
|
|
2
3
|
export interface ChatFiltersProps {
|
|
3
4
|
value: Filters;
|
|
4
5
|
onChange(next: Filters): void;
|
|
6
|
+
/** The active inboxes the user may open; the inbox filter shows with two or more. */
|
|
7
|
+
inboxes?: readonly Inbox[];
|
|
5
8
|
}
|
|
6
|
-
export declare function ChatFilters({ value, onChange }: ChatFiltersProps): import("react").JSX.Element;
|
|
9
|
+
export declare function ChatFilters({ value, onChange, inboxes }: ChatFiltersProps): import("react").JSX.Element;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Chat } from '@ezychat/shared';
|
|
1
|
+
import type { Chat, Inbox } from '@ezychat/shared';
|
|
2
2
|
export interface ChatListItemProps {
|
|
3
3
|
chat: Chat;
|
|
4
4
|
active: boolean;
|
|
@@ -6,8 +6,10 @@ export interface ChatListItemProps {
|
|
|
6
6
|
assigneeName: string | null;
|
|
7
7
|
/** The current user owns this chat: the chip gets the accent outline. */
|
|
8
8
|
assignedToMe?: boolean;
|
|
9
|
+
/** The chat's inbox, only while the user sees two or more inboxes. */
|
|
10
|
+
inbox?: Pick<Inbox, 'name' | 'tone'>;
|
|
9
11
|
}
|
|
10
12
|
export declare function GroupIcon({ className }: {
|
|
11
13
|
className?: string;
|
|
12
14
|
}): import("react").JSX.Element;
|
|
13
|
-
export declare function ChatListItem({ chat, active, assigneeName, assignedToMe, }: ChatListItemProps): import("react").JSX.Element;
|
|
15
|
+
export declare function ChatListItem({ chat, active, assigneeName, assignedToMe, inbox, }: ChatListItemProps): import("react").JSX.Element;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Chat } from '@ezychat/shared';
|
|
1
|
+
import type { Chat, Inbox } from '@ezychat/shared';
|
|
2
2
|
import type { Directory } from './useDirectory';
|
|
3
3
|
export interface ConversationHeaderProps {
|
|
4
4
|
chat: Chat;
|
|
@@ -20,6 +20,8 @@ export interface ConversationHeaderProps {
|
|
|
20
20
|
onOpenInfo?(): void;
|
|
21
21
|
/** Group chats: WhatsApp's member count, shown under the name once known. */
|
|
22
22
|
memberCount?: number | null;
|
|
23
|
+
/** The chat's inbox, only while the user sees two or more inboxes (replies leave from it). */
|
|
24
|
+
inbox?: Pick<Inbox, 'name' | 'tone'>;
|
|
23
25
|
busy?: boolean;
|
|
24
26
|
}
|
|
25
|
-
export declare function ConversationHeader({ chat, directory, onBack, onAssign, onToggleStatus, notesOpen, notesCount, onToggleNotes, showCustomer, customerOpen, onToggleCustomer, onOpenInfo, memberCount, busy, }: ConversationHeaderProps): import("react").JSX.Element;
|
|
27
|
+
export declare function ConversationHeader({ chat, directory, onBack, onAssign, onToggleStatus, notesOpen, notesCount, onToggleNotes, showCustomer, customerOpen, onToggleCustomer, onOpenInfo, memberCount, inbox, busy, }: ConversationHeaderProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Inbox } from '@ezychat/shared';
|
|
2
|
+
/** The inbox a chat belongs to, in that inbox's colour (shown only with two or more inboxes). */
|
|
3
|
+
export declare function InboxChip({ inbox, className, }: Readonly<{
|
|
4
|
+
inbox: Pick<Inbox, 'name' | 'tone'>;
|
|
5
|
+
className?: string;
|
|
6
|
+
}>): import("react").JSX.Element;
|
|
@@ -4,6 +4,11 @@ type WaStateName = WaStatus['state'];
|
|
|
4
4
|
export declare function waQueuesMessages(state: WaStateName | undefined): boolean;
|
|
5
5
|
/** States where sending is pointless until an admin acts (composer disabled). */
|
|
6
6
|
export declare function waSendingBlocked(state: WaStateName | undefined): boolean;
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Top-of-inbox banner shown whenever a WhatsApp connection isn't open. With one inbox (Lite) it is
|
|
9
|
+
* today's single banner; with two or more visible inboxes there is one per inbox that is not open,
|
|
10
|
+
* titled with that inbox's name. A user who sees one inbox other than inbox 1 gets that inbox's
|
|
11
|
+
* banner (named, never inbox 1's state); with several inboxes and none visible there is no banner.
|
|
12
|
+
*/
|
|
8
13
|
export declare function WaBanner(): import("react").JSX.Element | null;
|
|
9
14
|
export {};
|
|
@@ -1,18 +1,35 @@
|
|
|
1
1
|
import type { User } from '@ezychat/shared';
|
|
2
2
|
import { type DirectoryUser } from '../api/queries';
|
|
3
|
+
/**
|
|
4
|
+
* A team member as the inbox knows them: the directory row (agents), or the full user (admins read
|
|
5
|
+
* `GET /api/users`, where `inboxIds` is set for agents only). No `inboxIds` means every inbox only
|
|
6
|
+
* in a single-inbox install; with several inboxes it means no inbox (fail closed).
|
|
7
|
+
*/
|
|
8
|
+
export type DirectoryEntry = Omit<DirectoryUser, 'inboxIds'> & {
|
|
9
|
+
inboxIds?: DirectoryUser['inboxIds'];
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* The member may open chats of this inbox (admins and the AI member open every inbox). A row without
|
|
13
|
+
* `inboxIds` opens every inbox only when `multiInbox` is false (one inbox, no inbox UI).
|
|
14
|
+
*/
|
|
15
|
+
export declare function canOpenInbox(u: DirectoryEntry, inboxId: number, multiInbox?: boolean): boolean;
|
|
3
16
|
export interface Directory {
|
|
4
17
|
me: User | null;
|
|
5
18
|
isAdmin: boolean;
|
|
6
|
-
byId: Map<number,
|
|
19
|
+
byId: Map<number, DirectoryEntry>;
|
|
7
20
|
/** Users that can be picked as assignee (active only). */
|
|
8
|
-
assignable:
|
|
21
|
+
assignable: DirectoryEntry[];
|
|
22
|
+
/** Active users who may open chats of this inbox: the assign dropdown of such a chat. */
|
|
23
|
+
assignableFor(inboxId: number): DirectoryEntry[];
|
|
9
24
|
/** Display name for a user id ("You" for the current user when `youLabel`). */
|
|
10
25
|
nameOf(id: number | null | undefined, opts?: {
|
|
11
26
|
youLabel?: boolean;
|
|
12
27
|
}): string | null;
|
|
13
28
|
}
|
|
14
29
|
/** Pure builder (exported for tests). */
|
|
15
|
-
export declare function buildDirectory(me: User | null, isAdmin: boolean, list: readonly
|
|
30
|
+
export declare function buildDirectory(me: User | null, isAdmin: boolean, list: readonly DirectoryEntry[] | undefined, opts?: {
|
|
31
|
+
multiInbox?: boolean;
|
|
32
|
+
}): Directory;
|
|
16
33
|
/**
|
|
17
34
|
* Team member lookup for the inbox. Admins use the full `GET /api/users`; every other role
|
|
18
35
|
* uses the public `GET /api/users/directory` ({id, displayName, role, disabled}). Ids that are
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Inbox } from '@ezychat/shared';
|
|
2
|
+
export { useInboxStatus } from '../api/queries';
|
|
3
|
+
/**
|
|
4
|
+
* The active inboxes the signed-in user may open. The server already filters `GET /api/inboxes` by
|
|
5
|
+
* access, so there is no access logic here; removed inboxes (admins only) are left out.
|
|
6
|
+
*/
|
|
7
|
+
export declare function useVisibleInboxes(): Inbox[];
|
|
8
|
+
/**
|
|
9
|
+
* The inbox chip of a chat: the chat's inbox while the user sees two or more inboxes, otherwise
|
|
10
|
+
* undefined (one inbox and Lite show no chip). Removed inboxes still name their read-only chats.
|
|
11
|
+
*/
|
|
12
|
+
export declare function useInboxOfChat(): (inboxId: number) => Inbox | undefined;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
/** A message body as WhatsApp shows it (React nodes for a `whitespace-pre-wrap` element). */
|
|
3
|
+
export declare function formatWhatsApp(text: string): React.ReactNode[];
|
|
4
|
+
/** The same text without the marks WhatsApp hides, for one-line previews (quotes, chat list). */
|
|
5
|
+
export declare function plainWhatsApp(text: string): string;
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
/** "ABCD1234" → "ABCD-1234", the way WhatsApp displays pairing codes. */
|
|
2
2
|
export declare function formatPairingCode(code: string): string;
|
|
3
|
-
/**
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Link WhatsApp with a phone number + 8-character pairing code instead of scanning a QR. Without
|
|
5
|
+
* `inboxId` it links the first number (`/wa/pairing-code`, as before multi-inbox).
|
|
6
|
+
*/
|
|
7
|
+
export declare function PhoneLink({ inboxId }?: Readonly<{
|
|
8
|
+
inboxId?: number;
|
|
9
|
+
}>): import("react").JSX.Element;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ezychat/web",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.47",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "EzyChat core: the team inbox web app (React) as a library, with createInboxApp and its extension API.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"build:lib": "node ../../scripts/build-web-lib.mjs"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@ezychat/shared": "0.1.
|
|
43
|
+
"@ezychat/shared": "0.1.47",
|
|
44
44
|
"@fontsource-variable/inter": "^5.3.0",
|
|
45
45
|
"@tanstack/react-query": "^5.104.4",
|
|
46
46
|
"@types/react": "^19.3.0",
|