React và Next.js (@hatang/chat-react)
@hatang/chat-react nạp widget chat Hạ Tầng trong ứng dụng React hoặc Next.js. Gói có một component không render gì, chỉ chèn script widget đúng một lần, và một hook trả về JS API $hatang đã có kiểu. Widget vẫn là widget web, cùng giao diện và cùng cài đặt trong dashboard; gói này chỉ lo phần nạp và kiểu TypeScript.
@hatang/chat-react 0.1.2 · React 18+ · Next.js 14+ (App Router và Pages Router) · npm · changelog: tệp CHANGELOG.md trong gói
Trước khi bắt đầu
- Một kênh website trong Hạ Tầng. Chép Site ID (
site_...) ở Hỗ trợ → Kênh → chọn website. - Tên miền ứng dụng của bạn chạy phải là tên miền của website (hoặc tên miền phụ), hoặc nằm trong Origin được phép thêm ở cài đặt website. Thêm
http://localhost:3000vào đó khi phát triển trên máy.
Cài đặt
npm install @hatang/chat-react
Nạp widget
Render <HatangChat /> một lần trong root layout. Module bắt đầu bằng "use client" nên dùng thẳng được trong App Router.
import { HatangChat } from "@hatang/chat-react";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="vi">
<body>
{children}
<HatangChat siteId="site_123..." />
</body>
</html>
);
}
Ở lần render đầu, component đặt window.HATANG_SITE_ID, đặt window.HATANG_CONFIG nếu bạn truyền config, rồi chèn https://chat.hatang.dev/widget.js với async. Script không bao giờ bị chèn hai lần, kể cả khi component render lại hay được render nhiều chỗ. Sau khi trang đầu tiên tải xong, website trong dashboard hiện trạng thái đã cài.
Props
| Prop | Kiểu | Mặc định | Tác dụng |
|---|---|---|---|
siteId | string | bắt buộc | Site ID của website |
host | string | https://chat.hatang.dev | Nơi phục vụ widget.js. Chỉ đổi khi chạy gateway local |
config | HatangConfig | — | { position?: "left" | "right"; locale?: "vi" | "en"; hideLauncher?: boolean }, ghi vào window.HATANG_CONFIG trước khi script tải |
user | HatangIdentity | — | { userId?, email?, name?, phone?, hash?, data? }. Gọi identify khi widget sẵn sàng, và gọi lại mỗi khi userId, email, name hoặc hash đổi |
config chỉ được đọc một lần, lúc script được chèn; đổi sau đó không tới được widget. Muốn đổi ngôn ngữ lúc chạy, gọi setLocale. Tuỳ chọn offset và mobileOffset của HATANG_CONFIG chưa có trong kiểu HatangConfig; nếu cần, tự đặt window.HATANG_CONFIG trước khi component render (xem Widget web và JS API).
Gọi widget từ code
useHatang() (hook) và hatang() (hàm thường, dùng ngoài component) trả về cùng một object. Lệnh gọi trước khi widget tải xong được xếp hàng, nên gọi lúc nào trong trình duyệt cũng được. Ở server, các lệnh không làm gì.
"use client";
import { useHatang } from "@hatang/chat-react";
export function SupportButton() {
const chat = useHatang();
return <button onClick={() => chat.open()}>Chat với chúng tôi</button>;
}
| Hàm | Tác dụng |
|---|---|
open(), close(), toggle() | Mở, đóng hoặc bật/tắt cửa sổ chat |
hide(), show() | Ẩn, hiện nút chat (ví dụ trên trang thanh toán) |
identify(identity) | Gắn người dùng đã đăng nhập; hash tính ở server (Nhận diện khách) |
setData(data) | Thuộc tính tuỳ chỉnh hiển thị cho nhân viên |
setLocale("vi" | "en") | Đổi ngôn ngữ widget |
pushEvent(name, data?) | Ghi một sự kiện vào dòng thời gian của khách |
on(event, cb), off(event, cb) | Nghe ready, open, close, message:received, message:sent, conversation:status |
reset() | Quên khách hiện tại (khi đăng xuất). Widget tải lại trang để bắt đầu phiên mới |
"use client";
import { useEffect } from "react";
import { hatang } from "@hatang/chat-react";
export function ChatAnalytics() {
useEffect(() => {
const chat = hatang();
const onReply = (m?: unknown) => track("chat_reply", m);
chat.on("message:received", onReply);
return () => chat.off("message:received", onReply);
}, []);
return null;
}
Widget còn nhận những lệnh mà API có kiểu này chưa liệt kê, như lead. Tự đẩy chúng vào hàng đợi: window.$hatang.push(["lead", { email, name }]). Danh sách lệnh và sự kiện đầy đủ ở Widget web và JS API.
Nhận diện người dùng đã đăng nhập
Tính hash, tức HMAC-SHA256 của user id với identity secret của site, ở server rồi truyền xuống cùng người dùng. Đừng bao giờ đưa secret vào bundle phía client.
import { createHmac } from "node:crypto";
import { HatangChat } from "@hatang/chat-react";
import { currentUser } from "@/lib/auth";
export default async function RootLayout({ children }: { children: React.ReactNode }) {
const user = await currentUser();
const identity = user
? {
userId: user.id,
email: user.email,
name: user.name,
hash: createHmac("sha256", process.env.HATANG_IDENTITY_SECRET!).update(user.id).digest("hex"),
}
: undefined;
return (
<html lang="vi">
<body>
{children}
<HatangChat siteId="site_123..." user={identity} />
</body>
</html>
);
}
Với hash hợp lệ, hộp thư đánh dấu khách là đã xác minh và gộp hội thoại với các phiên trước của cùng người, kể cả phiên trong ứng dụng di động. Gọi hatang().reset() khi người dùng đăng xuất để người tiếp theo trên cùng trình duyệt không mang danh tính cũ.
Không dùng useEffect
Với Pages Router, framework khác hoặc template HTML thuần, hatangSnippet(siteId, host?, config?) trả về đúng đoạn script inline mà dashboard hiển thị, dưới dạng một chuỗi. Đặt nó vào thẻ <script> hoặc next/script:
import Script from "next/script";
import { hatangSnippet } from "@hatang/chat-react";
<Script id="hatang" strategy="afterInteractive">
{hatangSnippet("site_123...", undefined, { locale: "vi" })}
</Script>
Các export
HatangChat, HatangChatProps, hatang, useHatang, HatangApi, HatangIdentity, HatangEvent, HatangConfig, hatangSnippet, inlineSnippet và widgetUrl.
Changelog
0.1.1: trang npm mô tả widget và Hạ Tầng, dẫn tới hướng dẫn này; README có thêm phần About Ha Tang. Không đổi code.
0.1.0: bản đầu tiên. <HatangChat siteId host config user /> nạp widget một lần; useHatang() / hatang() với open, close, toggle, hide, show, identify, setData, setLocale, pushEvent, on, off và reset, được xếp hàng đến khi widget tải xong.
Bước tiếp theo
- Widget web và JS API: mọi tuỳ chọn
HATANG_CONFIG, lệnhlead, theo dõi chuyển đổi. - Nhận diện khách: danh tính có chữ ký và cách gộp phiên.
- Cài widget: Shopify, Google Tag Manager, Webflow và HTML thuần.