@medipha/chat
v1.0.60
Published
Embeddable Medic-ERP chat module for React applications.
Readme
@medipha/chat
@medipha/chat is an embeddable React chat module for Medic-ERP. It uses the
ERP application's React tree, BrowserRouter, authentication, and Ant Design 4
installation; it does not use an iframe or create a router.
Install
pnpm add @medipha/chatMount in Medic-ERP
The ERP imports the Ant Design 4 stylesheet exactly once, at its application entry point. The chat library deliberately does not import it.
import 'antd/dist/antd.css';
import { ChatModule } from '@medipha/chat';
import '@medipha/chat/styles.css';
<Route
path="/chat-erp/*"
element={
<ChatModule
userId={currentUserId}
accessToken={accessToken}
expireTime={expireTime}
apiUrl={CHAT_API_URL}
socketUrl={CHAT_SOCKET_URL}
hasAddGroupVerified={permissions.addGroupVerified}
hasUpdateGroupVerified={permissions.updateGroupVerified}
hasDeleteGroupVerified={permissions.deleteGroupVerified}
onUnauthorized={handleUnauthorized}
onRefreshToken={handleRefreshToken}
onNavigateOutsideChat={handleNavigateOutsideChat}
/>
}
/>;The ERP must own the surrounding BrowserRouter. ChatModule adds only
relative routes: /chat-erp for the conversation list and
/chat-erp/:conversationId for a room.
ChatModuleProps
| Prop | Required | Meaning |
| ------------------------ | -------- | ------------------------------------------------------------- |
| userId | Yes | ERP id of the signed-in user, as the chat backend knows them. |
| accessToken | Yes | Current ERP access token used by chat API/socket clients. |
| expireTime | Yes | Token expiry as epoch seconds for media upload checks. |
| apiUrl | Yes | Base URL for the Chat HTTP API. |
| socketUrl | Yes | Socket server URL. |
| hasAddGroupVerified | Yes | May the user create a verified ("tích xanh") group. |
| hasUpdateGroupVerified | Yes | May the user rename/re-avatar a verified group. |
| hasDeleteGroupVerified | Yes | May the user disband a verified group. |
| onUnauthorized | No | ERP callback for an unauthorized chat response. |
| onRefreshToken | No | Returns a fresh token before media upload when expiry is near. |
| onNavigateOutsideChat | No | ERP callback for navigation that leaves chat. |
accessToken, apiUrl and socketUrl are asserted non-empty at mount and
throw otherwise, so render chat only once the ERP session is resolved.
Pass a new accessToken prop whenever ERP refreshes its credential.
ChatModule updates its internal runtime context from the new prop and never
writes a token to localStorage or sessionStorage.
Anchored widget
ChatWidget is the second entry point: a rail the ERP anchors to the right
edge of the shell, plus a floating chat window. It takes every ChatModuleProps
field and does not use the router, so it can be mounted in the ERP layout
outside any <Routes>.
import { ChatWidget } from '@medipha/chat';
const [isChatOpen, setIsChatOpen] = useState(true);
const isChatFullPage = location.pathname.startsWith('/chat-erp');
<aside className="erp-chat-rail" hidden={!isChatOpen || isChatFullPage}>
<ChatWidget
{...chatRuntimeProps}
isOpen={isChatOpen && !isChatFullPage}
onRequestClose={() => setIsChatOpen(false)}
onUnreadTotalChange={setChatUnreadTotal}
onOpenFullPage={(conversationId) =>
navigate(conversationId ? `/chat-erp/${conversationId}` : '/chat-erp')
}
/>
</aside>;The ERP owns the rail's box — size it 72px wide and full height. Keep the
widget mounted while hidden and switch isOpen instead of unmounting: the
conversation list stays warm and onUnreadTotalChange keeps feeding the ERP's
own unread badge.
| Prop | Meaning |
| -------------------------- | --------------------------------------------------------------------------- |
| isOpen | Host is showing the widget. Defaults to true. Withholds the chat window. |
| onRequestClose | The rail's close button was pressed; hide the rail. Omit to hide the button. |
| onOpenFullPage | The window's expand button was pressed; route to the full-page chat. |
| onUnreadTotalChange | Total unread across conversations, for the ERP's launcher badge. |
| onActiveConversationChange | Which conversation the widget currently has open, or undefined. |
| onExpandChange | The rail widened to the full sidebar (320px) or collapsed back to 72px. |
The rail's expanded state anchors itself to the viewport (position: fixed),
so it works whether or not the ERP reacts to onExpandChange — but a host that
hides the rail by collapsing its width must hide it with visibility,
display or hidden rather than overflow, which does not clip fixed
children.
The floating window is portalled to document.body at z-index: 1000; ERP
chrome that must sit above chat needs a higher value.
Widget CSS variables
Set these on any ancestor to retune the widget without a rebuild:
| Variable | Default |
| ------------------------------------- | ------- |
| --med-chat-widget-window-width | 360px |
| --med-chat-widget-window-height | 480px |
| --med-chat-widget-window-right | 88px |
| --med-chat-widget-window-bottom | 24px |
| --med-chat-widget-window-z | 1000 |
| --med-chat-widget-rail-expanded-width | 320px |
| --med-chat-widget-image-width | 180px |
| --med-chat-widget-image-height | 240px |
Nginx SPA fallback
Configure the ERP server so deep links under /chat-erp/* return the ERP
application entry file:
location / {
try_files $uri $uri/ /index.html;
}Development and validation
Vite 8 requires Node.js 20.19+ or 22.12+.
pnpm install
pnpm devpnpm dev opens the root development playground. It loads ChatModule
directly from src, so library code and CSS changes use Vite HMR without a
library build after every edit. The playground's BrowserRouter is
development-only; it is not part of the published package.
To validate the publishable library:
pnpm typecheck
pnpm build
npm pack --dry-runTo verify the CRA host after building the library:
cd examples/erp-host
pnpm install
pnpm buildPublish
Publish @medipha/chat-core before @medipha/chat. GitLab CI uses a masked,
protected NPM_TOKEN variable with npm publish permission and runs only for a
version tag such as v0.1.0. Update versions using semantic versioning: patch
for fixes, minor for backward-compatible features, and major for breaking API
changes. Create and push the version tag through your normal release process
after reviewing the working tree.
