feat(sdk): P4.5 @insignia/iios-support-web (composes message-web + support REST)
SupportProvider (wraps MessageProvider + support client); useEscalate, useTickets/ useAssignedTickets (+createTicket/patch), useCallbackRequest, useAvailability; re-exports useThread/useMessages. Boundary allowlist: support-web -> contracts+ kernel-client+message-web+inbox-web. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "@insignia/iios-support-web",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"module": "dist/index.js",
|
||||||
|
"types": "dist/index.d.ts",
|
||||||
|
"exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" } },
|
||||||
|
"files": ["dist"],
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsup",
|
||||||
|
"typecheck": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@insignia/iios-kernel-client": "workspace:*",
|
||||||
|
"@insignia/iios-message-web": "workspace:*"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=18"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^19.0.0",
|
||||||
|
"react": "^19.0.0",
|
||||||
|
"tsup": "^8.3.5",
|
||||||
|
"typescript": "^5.7.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export {
|
||||||
|
SupportProvider,
|
||||||
|
useEscalate,
|
||||||
|
useTickets,
|
||||||
|
useAssignedTickets,
|
||||||
|
useCallbackRequest,
|
||||||
|
useAvailability,
|
||||||
|
} from './react';
|
||||||
|
// Re-export the chat hooks so a support app imports everything from one place.
|
||||||
|
export { useThread, useMessages } from '@insignia/iios-message-web';
|
||||||
|
export type { Ticket, TicketState, CallbackRequest, Message } from '@insignia/iios-kernel-client';
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||||||
|
import { MessageProvider } from '@insignia/iios-message-web';
|
||||||
|
import { RestClient, type Ticket, type CallbackRequest, type TicketState } from '@insignia/iios-kernel-client';
|
||||||
|
|
||||||
|
const SupportContext = createContext<RestClient | null>(null);
|
||||||
|
|
||||||
|
/** Wraps MessageProvider (chat) and provides a support REST client. */
|
||||||
|
export function SupportProvider({
|
||||||
|
serviceUrl,
|
||||||
|
token,
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
serviceUrl: string;
|
||||||
|
token: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}): React.ReactElement {
|
||||||
|
const client = useMemo(() => new RestClient({ serviceUrl, token }), [serviceUrl, token]);
|
||||||
|
return (
|
||||||
|
<SupportContext.Provider value={client}>
|
||||||
|
<MessageProvider serviceUrl={serviceUrl} token={token}>
|
||||||
|
{children}
|
||||||
|
</MessageProvider>
|
||||||
|
</SupportContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function useSupportClient(): RestClient {
|
||||||
|
const c = useContext(SupportContext);
|
||||||
|
if (!c) throw new Error('support hooks must be used within <SupportProvider>');
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Escalate a chat thread to support (creates + queues a ticket). */
|
||||||
|
export function useEscalate(): (threadId: string, subject?: string) => Promise<Ticket> {
|
||||||
|
const client = useSupportClient();
|
||||||
|
return (threadId, subject) => client.escalate(threadId, subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The caller's tickets (scope 'mine') or an agent's assigned tickets ('assigned'). */
|
||||||
|
export function useTickets(scope: 'mine' | 'assigned' = 'mine', pollMs = 3000): {
|
||||||
|
tickets: Ticket[];
|
||||||
|
refresh: () => Promise<void>;
|
||||||
|
createTicket: (subject: string, opts?: { priority?: string; threadId?: string }) => Promise<Ticket>;
|
||||||
|
patch: (id: string, state: TicketState) => Promise<void>;
|
||||||
|
} {
|
||||||
|
const client = useSupportClient();
|
||||||
|
const [tickets, setTickets] = useState<Ticket[]>([]);
|
||||||
|
const refresh = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
setTickets(await client.listTickets(scope));
|
||||||
|
} catch {
|
||||||
|
/* transient */
|
||||||
|
}
|
||||||
|
}, [client, scope]);
|
||||||
|
useEffect(() => {
|
||||||
|
void refresh();
|
||||||
|
const t = setInterval(() => void refresh(), pollMs);
|
||||||
|
return () => clearInterval(t);
|
||||||
|
}, [refresh, pollMs]);
|
||||||
|
const createTicket = async (subject: string, opts?: { priority?: string; threadId?: string }) => {
|
||||||
|
const t = await client.createTicket({ subject, ...opts });
|
||||||
|
await refresh();
|
||||||
|
return t;
|
||||||
|
};
|
||||||
|
const patch = async (id: string, state: TicketState) => {
|
||||||
|
await client.patchTicket(id, state);
|
||||||
|
await refresh();
|
||||||
|
};
|
||||||
|
return { tickets, refresh, createTicket, patch };
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Agent convenience: their assigned tickets. */
|
||||||
|
export function useAssignedTickets(pollMs = 3000) {
|
||||||
|
return useTickets('assigned', pollMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useCallbackRequest(): (input: {
|
||||||
|
preferChannel: string;
|
||||||
|
preferredTime?: string;
|
||||||
|
notes?: string;
|
||||||
|
ticketId?: string;
|
||||||
|
}) => Promise<CallbackRequest> {
|
||||||
|
const client = useSupportClient();
|
||||||
|
return (input) => client.requestCallback(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Agent presence: set AVAILABLE/OFFLINE. */
|
||||||
|
export function useAvailability(): (state: string) => Promise<unknown> {
|
||||||
|
const client = useSupportClient();
|
||||||
|
return (state) => client.setAvailability(state);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./dist",
|
||||||
|
"rootDir": "./src",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"lib": ["ES2022", "DOM"],
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"types": ["react"]
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.tsx"],
|
||||||
|
"exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { defineConfig } from 'tsup';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
entry: ['src/index.ts'],
|
||||||
|
format: ['esm'],
|
||||||
|
dts: true,
|
||||||
|
clean: true,
|
||||||
|
external: ['react', 'react-dom', '@insignia/iios-message-web', '@insignia/iios-kernel-client'],
|
||||||
|
});
|
||||||
Generated
+22
@@ -181,6 +181,28 @@ importers:
|
|||||||
specifier: ^5.7.3
|
specifier: ^5.7.3
|
||||||
version: 5.9.3
|
version: 5.9.3
|
||||||
|
|
||||||
|
packages/iios-support-web:
|
||||||
|
dependencies:
|
||||||
|
'@insignia/iios-kernel-client':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../iios-kernel-client
|
||||||
|
'@insignia/iios-message-web':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../iios-message-web
|
||||||
|
devDependencies:
|
||||||
|
'@types/react':
|
||||||
|
specifier: ^19.0.0
|
||||||
|
version: 19.2.17
|
||||||
|
react:
|
||||||
|
specifier: ^19.0.0
|
||||||
|
version: 19.2.7
|
||||||
|
tsup:
|
||||||
|
specifier: ^8.3.5
|
||||||
|
version: 8.5.1(jiti@2.7.0)(postcss@8.5.16)(typescript@5.9.3)
|
||||||
|
typescript:
|
||||||
|
specifier: ^5.7.3
|
||||||
|
version: 5.9.3
|
||||||
|
|
||||||
packages/iios-testkit:
|
packages/iios-testkit:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@insignia/iios-contracts':
|
'@insignia/iios-contracts':
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ const ALLOWED = {
|
|||||||
'@insignia/iios-service': ['@insignia/iios-contracts', '@insignia/iios-testkit'],
|
'@insignia/iios-service': ['@insignia/iios-contracts', '@insignia/iios-testkit'],
|
||||||
'@insignia/iios-message-web': ['@insignia/iios-contracts', '@insignia/iios-kernel-client'],
|
'@insignia/iios-message-web': ['@insignia/iios-contracts', '@insignia/iios-kernel-client'],
|
||||||
'@insignia/iios-inbox-web': ['@insignia/iios-contracts', '@insignia/iios-kernel-client'],
|
'@insignia/iios-inbox-web': ['@insignia/iios-contracts', '@insignia/iios-kernel-client'],
|
||||||
|
'@insignia/iios-support-web': [
|
||||||
|
'@insignia/iios-contracts',
|
||||||
|
'@insignia/iios-kernel-client',
|
||||||
|
'@insignia/iios-message-web',
|
||||||
|
'@insignia/iios-inbox-web',
|
||||||
|
],
|
||||||
};
|
};
|
||||||
const KNOWN = Object.keys(ALLOWED);
|
const KNOWN = Object.keys(ALLOWED);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user