impact-ui-mcp-server 1.0.2 → 1.0.4

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,287 @@
1
+ /**
2
+ * TableChat scaffold tool – returns file contents and instructions for adding TableChat to a project.
3
+ * Templates: chat.service, useTopicWebSocket, chatCellRenderers, env, axios.
4
+ */
5
+
6
+ import { readFileSync, existsSync } from "fs";
7
+ import { join, dirname } from "path";
8
+ import { fileURLToPath } from "url";
9
+
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = dirname(__filename);
12
+
13
+ const TEMPLATES_DIR = join(__dirname, "..", "..", "templates", "table-chat");
14
+
15
+ const DEFAULT_PATHS = {
16
+ hooks: "src/customHooks",
17
+ services: "src/utils/services",
18
+ helpers: "src/utils/helpers/tableHelpers",
19
+ axios: "src/utils/axios",
20
+ constants: "src/constants",
21
+ };
22
+
23
+ /**
24
+ * Read a template file or return null if missing.
25
+ */
26
+ function readTemplate(name) {
27
+ const path = join(TEMPLATES_DIR, name);
28
+ if (!existsSync(path)) return null;
29
+ return readFileSync(path, "utf-8");
30
+ }
31
+
32
+ /**
33
+ * Replace placeholders in content with args.
34
+ */
35
+ function applyReplacements(content, args) {
36
+ const paths = { ...DEFAULT_PATHS, ...args.paths };
37
+ let out = content;
38
+
39
+ // Table config placeholders
40
+ out = out.replace(/\{\{OBJECT_TYPE\}\}/g, args.objectType || "promo");
41
+ out = out.replace(/\{\{UNIQUE_ROW_ID\}\}/g, args.uniqueRowId || "promo_id");
42
+ out = out.replace(/\{\{APP_CODE\}\}/g, args.appCode || "promosmart");
43
+ out = out.replace(/\{\{CHANNEL_NAME\}\}/g, args.channelName || "promo_name");
44
+ out = out.replace(/\{\{TABLE_NAME\}\}/g, args.tableName || "WORKBENCH_TABLE");
45
+
46
+ // Import path placeholders (use string for JS import)
47
+ const axiosImport = paths.axios ? `"${paths.axios}"` : '"../../utils/axios"';
48
+ out = out.replace(/\{\{IMPORT_AXIOS\}\}/g, axiosImport);
49
+ // Relative path from services folder to axios (e.g. chat.service.js in utils/services → ../axios)
50
+ const axiosFromServices = paths.axiosFromServices || "../axios";
51
+ out = out.replace(/\{\{IMPORT_AXIOS_FROM_SERVICES\}\}/g, axiosFromServices);
52
+
53
+ const stylesImport =
54
+ args.includeStyles !== false
55
+ ? 'import "./chatCellRenderers.scss";'
56
+ : "// import \"./chatCellRenderers.scss\"; if you add the scss file";
57
+ out = out.replace(/\{\{STYLES_IMPORT\}\}/g, stylesImport);
58
+
59
+ // useTableChat / useChat path placeholders (relative from src/customHooks)
60
+ const fromHooks = {
61
+ useChat: '"./useChat"',
62
+ useUrlChatFilter: '"./useUrlChatFilter"',
63
+ useTopicWebSocket: '"./useTopicWebSocket"',
64
+ useTableVisibleRows: '"./useTableVisibleRows"',
65
+ useWebSocket: '"./useWebSocket"',
66
+ constants: '"../constants"',
67
+ chatService: '"../../utils/services/chat.service"',
68
+ chatIcon: '"../../assets/imageAssets/chatMsgIcon.svg"',
69
+ chatCellRenderers: '"../../utils/helpers/tableHelpers/chatCellRenderers"',
70
+ toast: '"../../store/features/global/global"',
71
+ utilityHelpers: '"../../utils/helpers/utility_helpers"',
72
+ };
73
+ out = out.replace(/\{\{IMPORT_USE_CHAT\}\}/g, fromHooks.useChat);
74
+ out = out.replace(/\{\{IMPORT_USE_URL_CHAT_FILTER\}\}/g, fromHooks.useUrlChatFilter);
75
+ out = out.replace(/\{\{IMPORT_USE_TOPIC_WEBSOCKET\}\}/g, fromHooks.useTopicWebSocket);
76
+ out = out.replace(/\{\{IMPORT_USE_TABLE_VISIBLE_ROWS\}\}/g, fromHooks.useTableVisibleRows);
77
+ out = out.replace(/\{\{IMPORT_USE_WEBSOCKET\}\}/g, fromHooks.useWebSocket);
78
+ out = out.replace(/\{\{IMPORT_CONSTANTS\}\}/g, fromHooks.constants);
79
+ out = out.replace(/\{\{IMPORT_CHAT_SERVICE\}\}/g, fromHooks.chatService);
80
+ out = out.replace(/\{\{IMPORT_CHAT_ICON\}\}/g, fromHooks.chatIcon);
81
+ out = out.replace(/\{\{IMPORT_TABLE_CELL_RENDERERS\}\}/g, fromHooks.chatCellRenderers);
82
+ out = out.replace(/\{\{IMPORT_TOAST\}\}/g, fromHooks.toast);
83
+ out = out.replace(/\{\{IMPORT_UTILITY_HELPERS\}\}/g, fromHooks.utilityHelpers);
84
+ out = out.replace(/\{\{IMPORT_STORE_GLOBAL\}\}/g, fromHooks.toast);
85
+
86
+ const useSearchParamsImport = args.reactRouterPackage
87
+ ? `"${args.reactRouterPackage}"`
88
+ : '"react-router-dom-v5-compat"';
89
+ out = out.replace(/\{\{IMPORT_USE_SEARCH_PARAMS\}\}/g, useSearchParamsImport);
90
+
91
+ const lodashImport = args.lodashPackage ? `"${args.lodashPackage}"` : '"lodash"';
92
+ out = out.replace(/\{\{IMPORT_LODASH\}\}/g, lodashImport);
93
+
94
+ const chatServiceFromApp =
95
+ args.chatServiceFromApp || '"./utils/services/chat.service"';
96
+ out = out.replace(/\{\{IMPORT_CHAT_SERVICE_FROM_APP\}\}/g, chatServiceFromApp);
97
+
98
+ const tableChatConfigConstant =
99
+ "TABLE_CHAT_CONFIG_FOR_" + (args.tableName || "WORKBENCH_TABLE");
100
+ out = out.replace(/\{\{TABLE_CHAT_CONFIG_CONSTANT\}\}/g, tableChatConfigConstant);
101
+
102
+ const websocketBaseUrlDefault =
103
+ args.websocketBaseUrlDefault ||
104
+ "process.env.REACT_APP_TABLE_CHAT_WEBSOCKET_BASE_URL";
105
+ out = out.replace(
106
+ /\{\{WEBSOCKET_BASE_URL_DEFAULT\}\}/g,
107
+ websocketBaseUrlDefault
108
+ );
109
+
110
+ return out;
111
+ }
112
+
113
+ /**
114
+ * Build the scaffold payload and return markdown content for the AI.
115
+ */
116
+ export function getTableChatScaffold(args) {
117
+ const mode = args.mode || "first_time";
118
+ const objectType = args.objectType || "promo";
119
+ const uniqueRowId = args.uniqueRowId || "promo_id";
120
+ const paths = { ...DEFAULT_PATHS, ...args.paths };
121
+
122
+ const chatServiceTemplate = readTemplate("chat.service.js.template");
123
+ const useChatTemplate = readTemplate("useChat.js.template");
124
+ const useTableChatTemplate = readTemplate("useTableChat.js.template");
125
+ const useUrlChatFilterTemplate = readTemplate("useUrlChatFilter.js.template");
126
+ const useTableVisibleRowsTemplate = readTemplate("useTableVisibleRows.js.template");
127
+ const useTopicWebSocketTemplate = readTemplate("useTopicWebSocket.js.template");
128
+ const useWebSocketTemplate = readTemplate("useWebSocket.js.template");
129
+ const chatCellRenderersTemplate = readTemplate("chatCellRenderers.js.template");
130
+ const chatCellRenderersScssTemplate = readTemplate(
131
+ "chatCellRenderers.scss.template"
132
+ );
133
+ const envSnippet = readTemplate("env-snippet.txt");
134
+ const axiosSnippet = readTemplate("axios-snippet.txt");
135
+ const fetchUserIdAppInitSnippet = readTemplate(
136
+ "fetchUserIdAppInit.snippet.template"
137
+ );
138
+
139
+ const replaceArgs = {
140
+ mode,
141
+ objectType,
142
+ uniqueRowId,
143
+ tableName: args.tableName || "WORKBENCH_TABLE",
144
+ appCode: args.appCode || "promosmart",
145
+ channelName: args.channelName || "promo_name",
146
+ paths,
147
+ includeStyles: true,
148
+ reactRouterPackage: args.reactRouterPackage,
149
+ lodashPackage: args.lodashPackage,
150
+ chatServiceFromApp: args.chatServiceFromApp,
151
+ websocketBaseUrlDefault: args.websocketBaseUrlDefault,
152
+ };
153
+
154
+ let md = "# TableChat scaffold\n\n";
155
+
156
+ md += "## Instructions\n\n";
157
+ md +=
158
+ "1. **Env (required):** Add the env variables below to your `.env` file **before** TableChat will work. The URLs vary per project—replace the example values with your environment's base URL, WebSocket URL, and frontend URL.\n";
159
+ md +=
160
+ "2. Add the axios snippet to your existing axios file so `TABLE_CHAT_API` and `REACT_APP_TABLE_CHAT_WEBSOCKET_BASE_URL` are available.\n";
161
+ md += `3. Create the following files in your project. Paths assume \`${paths.hooks}\`, \`${paths.services}\`, \`${paths.helpers}\`; adjust if your structure differs.\n`;
162
+ md +=
163
+ "4. This scaffold provides **chat.service.js**, **useChat.js**, **useTableChat.js**, **useUrlChatFilter.js**, **useTableVisibleRows.js**, **useTopicWebSocket**, **useWebSocket.js**, and **chatCellRenderers**. You still need: **tableChatConstants.js** (with TABLE_CHAT_CONFIG_FOR_* for your table).\n";
164
+ md +=
165
+ "5. In your TableWrapper (or table container), use `useTableChat` with `chatConfig={{ objectType, uniqueRowId }}` and spread `getChatProps()` and apply `getChatColumnModifier(columnDefs)` to your column definitions.\n";
166
+ md +=
167
+ "6. **Run fetchUserId once in App**: Add the snippet below to your **App.js or App.tsx** so `userId` is fetched once on app load and stored in localStorage (used by useChat). This avoids calling the API again and again.\n\n";
168
+
169
+ if (envSnippet) {
170
+ md +=
171
+ "## Env (required – add to .env)\n\nTableChat will not work until these variables are set. Values vary per project; use your environment's URLs.\n\n```env\n";
172
+ md += envSnippet.trim();
173
+ md += "\n```\n\n";
174
+ }
175
+
176
+ if (axiosSnippet) {
177
+ md += "## Axios snippet (add to your axios file)\n\n```js\n";
178
+ md += axiosSnippet.trim();
179
+ md += "\n```\n\n";
180
+ }
181
+
182
+ if (fetchUserIdAppInitSnippet) {
183
+ const snippetContent = applyReplacements(
184
+ fetchUserIdAppInitSnippet,
185
+ replaceArgs
186
+ );
187
+ md +=
188
+ "## Add to App.js / App.tsx (run once on load)\n\n";
189
+ md +=
190
+ "Add the following so **userId** is fetched once and stored in localStorage (useChat reads it). Import at the top, then call `fetchUserIdInLocalStorage()` once in a `useEffect` with empty deps.\n\n";
191
+ md += "```js\n";
192
+ md += snippetContent.trim();
193
+ md += "\n```\n\n";
194
+ }
195
+
196
+ if (chatServiceTemplate) {
197
+ const content = applyReplacements(chatServiceTemplate, replaceArgs);
198
+ const filePath = `${paths.services}/chat.service.js`;
199
+ md += `## File: ${filePath}\n\n`;
200
+ md += "```js\n";
201
+ md += content;
202
+ md += "\n```\n\n";
203
+ }
204
+
205
+ if (useChatTemplate) {
206
+ const content = applyReplacements(useChatTemplate, replaceArgs);
207
+ const filePath = `${paths.hooks}/useChat.js`;
208
+ md += `## File: ${filePath}\n\n`;
209
+ md += "```js\n";
210
+ md += content;
211
+ md += "\n```\n\n";
212
+ }
213
+
214
+ if (useTableChatTemplate) {
215
+ const content = applyReplacements(useTableChatTemplate, replaceArgs);
216
+ const filePath = `${paths.hooks}/useTableChat.js`;
217
+ md += `## File: ${filePath}\n\n`;
218
+ md += "```js\n";
219
+ md += content;
220
+ md += "\n```\n\n";
221
+ }
222
+
223
+ if (useUrlChatFilterTemplate) {
224
+ const content = applyReplacements(useUrlChatFilterTemplate, replaceArgs);
225
+ const filePath = `${paths.hooks}/useUrlChatFilter.js`;
226
+ md += `## File: ${filePath}\n\n`;
227
+ md += "```js\n";
228
+ md += content;
229
+ md += "\n```\n\n";
230
+ }
231
+
232
+ if (useTableVisibleRowsTemplate) {
233
+ const content = applyReplacements(useTableVisibleRowsTemplate, replaceArgs);
234
+ const filePath = `${paths.hooks}/useTableVisibleRows.js`;
235
+ md += `## File: ${filePath}\n\n`;
236
+ md += "```js\n";
237
+ md += content;
238
+ md += "\n```\n\n";
239
+ }
240
+
241
+ if (useTopicWebSocketTemplate) {
242
+ const content = applyReplacements(useTopicWebSocketTemplate, replaceArgs);
243
+ const filePath = `${paths.hooks}/useTopicWebSocket.js`;
244
+ md += `## File: ${filePath}\n\n`;
245
+ md += "```js\n";
246
+ md += content;
247
+ md += "\n```\n\n";
248
+ }
249
+
250
+ if (useWebSocketTemplate) {
251
+ const content = applyReplacements(useWebSocketTemplate, replaceArgs);
252
+ const filePath = `${paths.hooks}/useWebSocket.js`;
253
+ md += `## File: ${filePath}\n\n`;
254
+ md += "```js\n";
255
+ md += content;
256
+ md += "\n```\n\n";
257
+ }
258
+
259
+ if (chatCellRenderersTemplate) {
260
+ const content = applyReplacements(chatCellRenderersTemplate, replaceArgs);
261
+ const filePath = `${paths.helpers}/chatCellRenderers.js`;
262
+ md += `## File: ${filePath}\n\n`;
263
+ md += "```js\n";
264
+ md += content;
265
+ md += "\n```\n\n";
266
+ }
267
+
268
+ if (chatCellRenderersScssTemplate) {
269
+ const filePath = `${paths.helpers}/chatCellRenderers.scss`;
270
+ md += `## File: ${filePath}\n\n`;
271
+ md += "```scss\n";
272
+ md += chatCellRenderersScssTemplate.trim();
273
+ md += "\n```\n\n";
274
+ }
275
+
276
+ md += "## Integration reminder\n\n";
277
+ md +=
278
+ "In your TableWrapper, pass `chatConfig={{ objectType: '" +
279
+ objectType +
280
+ "', uniqueRowId: '" +
281
+ uniqueRowId +
282
+ "' }}` (or from your table constants). ";
283
+ md +=
284
+ "Use `useTableChat({ chatConfig, tableRef, rowData, showChatIconOnColumn, visibleRowsIds, isEnabled })` and spread `getChatProps()` onto `<Table>`, and apply `getChatColumnModifier(columnDefs)` to get the chat column and inline chat icon.\n";
285
+
286
+ return md;
287
+ }
@@ -0,0 +1,72 @@
1
+ # TableChat scaffold templates
2
+
3
+ This folder holds the template files used by the MCP tool `scaffold_table_chat`. The tool reads these files, replaces placeholders with user-provided values (objectType, uniqueRowId, appCode, channelName, tableName, paths), and returns the result so the AI can create or update files in the consumer project.
4
+
5
+ ## Placeholders
6
+
7
+ Use these in the template files so the scaffold tool can substitute them:
8
+
9
+ | Placeholder | Description | Example |
10
+ |-------------|-------------|---------|
11
+ | `{{OBJECT_TYPE}}` | Table code (tableCode) – required per table | `promo`, `landing_workbench_table` |
12
+ | `{{UNIQUE_ROW_ID}}` | Row ID field name – required per table | `promo_id` |
13
+ | `{{APP_CODE}}` | Project-level app code – optional, default e.g. promosmart | `promosmart` |
14
+ | `{{CHANNEL_NAME}}` | Column field to show chat icon on – optional default | `promo_name` |
15
+ | `{{TABLE_NAME}}` | Constant key suffix for this table (add_table) | `WORKBENCH_TABLE` |
16
+ | `{{IMPORT_AXIOS}}` | Path to axios file (for chat.service and useTableChat) | `../axios` or `@/utils/axios` |
17
+ | `{{IMPORT_CONSTANTS}}` | Path to table-chat constants | `../constants` or `@/constants` |
18
+ | `{{IMPORT_CHAT_SERVICE}}` | Path to chat.service | `../../utils/services/chat.service` or `@/utils/services/chat.service` |
19
+ | `{{IMPORT_USE_CHAT}}` | Path to useChat hook | `./useChat` |
20
+ | `{{IMPORT_USE_URL_CHAT_FILTER}}` | Path to useUrlChatFilter | `./useUrlChatFilter` |
21
+ | `{{IMPORT_USE_TOPIC_WEBSOCKET}}` | Path to useTopicWebSocket | `./useTopicWebSocket` |
22
+ | `{{IMPORT_USE_TABLE_VISIBLE_ROWS}}` | Path to useTableVisibleRows | `./useTableVisibleRows` |
23
+ | `{{IMPORT_TABLE_CELL_RENDERERS}}` | Path to tableCellRendererHelpers | `@/utils/helpers/tableHelpers/tableCellRendererHelpers` |
24
+ | `{{IMPORT_TOAST}}` | Path to Redux toast (e.g. toastError) | `@/store/features/global/global` |
25
+ | `{{IMPORT_STORE_GLOBAL}}` | Path to store/global (e.g. requestFail) | Same as `{{IMPORT_TOAST}}` |
26
+ | `{{IMPORT_CHAT_ICON}}` | Path to chat icon asset | `@/assets/imageAssets/chatMsgIcon.svg?.url` |
27
+ | `{{WEBSOCKET_BASE_URL_DEFAULT}}` | Default WebSocket URL (useWebSocket) | `process.env.REACT_APP_TABLE_CHAT_WEBSOCKET_BASE_URL` |
28
+
29
+ ## Files included in this folder
30
+
31
+ - **chat.service.js.template** – TableChat API service. Placeholder: `{{IMPORT_AXIOS_FROM_SERVICES}}`.
32
+ - **useChat.js.template** – Core chat hook (WebSocket, messages, topics). Placeholders: `{{IMPORT_CHAT_SERVICE}}`, `{{IMPORT_STORE_GLOBAL}}`, `{{IMPORT_UTILITY_HELPERS}}`, `{{IMPORT_USE_WEBSOCKET}}`, `{{IMPORT_AXIOS}}`.
33
+ - **useTableChat.js.template** – Table + chat wiring hook. Placeholders: `{{IMPORT_AXIOS}}`, `{{IMPORT_USE_CHAT}}`, `{{IMPORT_USE_URL_CHAT_FILTER}}`, `{{IMPORT_USE_TOPIC_WEBSOCKET}}`, `{{IMPORT_CONSTANTS}}`, `{{IMPORT_CHAT_SERVICE}}`, `{{IMPORT_CHAT_ICON}}`, `{{IMPORT_TABLE_CELL_RENDERERS}}`, `{{IMPORT_USE_TABLE_VISIBLE_ROWS}}`, `{{IMPORT_TOAST}}`, `{{TABLE_CHAT_CONFIG_CONSTANT}}`.
34
+ - **useUrlChatFilter.js.template** – URL-based chat open/filter (topic_id, message_id, comment notification). Placeholder: `{{IMPORT_USE_SEARCH_PARAMS}}` (default `"react-router-dom-v5-compat"`; use `"react-router-dom"` for v6).
35
+ - **useTableVisibleRows.js.template** – Tracks visible table row IDs (shared by chat and comment). Placeholder: `{{IMPORT_LODASH}}` (default `"lodash"`).
36
+ - **useTopicWebSocket.js.template** – Hook for topic-level WebSocket (pulsing, visibility). Placeholder: `{{IMPORT_AXIOS}}`.
37
+ - **useWebSocket.js.template** – Generic WebSocket hook (connection, reconnect, sendMessage, joinTopic, etc.). Placeholders: `{{IMPORT_STORE_GLOBAL}}`, `{{WEBSOCKET_BASE_URL_DEFAULT}}`.
38
+ - **chatCellRenderers.js.template** – SpecialBadgeWrapperCellRenderer, ColorfulChatIcon, ChatColumnCellRenderer. Placeholder: `{{STYLES_IMPORT}}`.
39
+ - **chatCellRenderers.scss.template** – Styles for chat column and badge.
40
+ - **env-snippet.txt** – Env vars for TableChat API, WebSocket, and app URL.
41
+ - **axios-snippet.txt** – TABLE_CHAT_API, WebSocket URL, and REACT_APP_PROMO_FRONTEND_URL exports.
42
+
43
+ ## Additional templates (add your content)
44
+
45
+ 1. **chat.service.js.template**
46
+ Copy your `chat.service.js` content. Replace the axios import path with `{{IMPORT_AXIOS}}` (or leave a single placeholder at the top).
47
+
48
+ 2. **tableChatConstants.js.template**
49
+ Copy your table-chat constants. Replace the first table config block with placeholders:
50
+ - `objectType: "{{OBJECT_TYPE}}"`
51
+ - `channelName: "{{CHANNEL_NAME}}"`
52
+ - `uniqueRowId: "{{UNIQUE_ROW_ID}}"`
53
+ - For the constant name use `TABLE_CHAT_CONFIG_FOR_{{TABLE_NAME}}` or a fixed name for first_time (e.g. `TABLE_CHAT_CONFIG_FOR_WORKBENCH_TABLE`).
54
+
55
+ 3. **useTableChat.js.template**
56
+ Copy your `useTableChat.js` content. Replace all import paths with the placeholders above so the tool can adapt to the project’s path style (relative vs alias).
57
+
58
+ 4. **axios-snippet.txt** (optional)
59
+ Snippet to add to the project’s axios file: create `TABLE_CHAT_API` with `baseURL: process.env.REACT_APP_TABLE_CHAT_BASE_URL`.
60
+
61
+ 5. **env-snippet.txt** (optional)
62
+ Single line: `REACT_APP_TABLE_CHAT_BASE_URL='https://...'`
63
+
64
+ 6. **integration-snippet.jsx.template** (optional)
65
+ TableWrapper + actual table usage example with placeholders so the AI can paste a ready-to-use snippet.
66
+
67
+ ## First-time vs add-table
68
+
69
+ - **first_time**: The tool uses the full constants template (one table config). `{{TABLE_NAME}}` can default to e.g. `WORKBENCH_TABLE`.
70
+ - **add_table**: The tool returns only a new `TABLE_CHAT_CONFIG_FOR_{{TABLE_NAME}}` block (and instructions to merge it into the existing constants file); it does not return the full constants file again.
71
+
72
+ See **TABLECHAT_SCAFFOLD_PLAN.md** in the mcp-server root for the full design and tool contract.
@@ -0,0 +1,19 @@
1
+ // TableChat – add to your axios file (e.g. utils/axios.js)
2
+ // Ensure REACT_APP_TABLE_CHAT_BASE_URL and REACT_APP_TABLE_CHAT_WEBSOCKET_BASE_URL are in .env
3
+
4
+ const TABLE_CHAT_API = axios.create({
5
+ baseURL: process.env.REACT_APP_TABLE_CHAT_BASE_URL,
6
+ timeout: 9999999,
7
+ headers: {
8
+ "Content-Type": "application/json",
9
+ },
10
+ });
11
+
12
+ // Export WebSocket base URL for useTopicWebSocket and useChat
13
+ export const REACT_APP_TABLE_CHAT_WEBSOCKET_BASE_URL = process.env.REACT_APP_TABLE_CHAT_WEBSOCKET_BASE_URL;
14
+
15
+ // Export app base URL for useTableChat notificationMetaData (if not already exported)
16
+ export const REACT_APP_PROMO_FRONTEND_URL = process.env.REACT_APP_PROMO_FRONTEND_URL || window.location.origin;
17
+
18
+ // Add TABLE_CHAT_API to your exports
19
+ export { TABLE_CHAT_API };
@@ -0,0 +1,85 @@
1
+ import { TABLE_CHAT_API, CORE_API } from "{{IMPORT_AXIOS_FROM_SERVICES}}";
2
+
3
+ // Fetch all the channels
4
+ export const fetchAllTopics = async (payload) => {
5
+ return TABLE_CHAT_API.post("/topics", payload);
6
+ };
7
+
8
+ export const unpinTopic = async (topicId) => {
9
+ return TABLE_CHAT_API.put(`/topic-members/${topicId}`, { is_pinned: false });
10
+ };
11
+
12
+ // All all the chats of selected Row
13
+ export const fetchTopicsByObjectId = async (objectType, objectId) => {
14
+ return TABLE_CHAT_API.get(`/topic/${objectType}/${objectId}`);
15
+ };
16
+
17
+ export const createTopic = async (topic) => {
18
+ return TABLE_CHAT_API.post("/topic", topic);
19
+ };
20
+ export const resetUnreadCount = async (topicId) => {
21
+ return TABLE_CHAT_API.post(`/topic/${topicId}/reset-unread`);
22
+ };
23
+ export const fetchUserId = async () => {
24
+ return TABLE_CHAT_API.get("/details/me");
25
+ };
26
+
27
+ export const deleteTopic = async (topicId) => {
28
+ return TABLE_CHAT_API.delete(`/topic/${topicId}`);
29
+ };
30
+
31
+ export const pinTopic = async (topicId, isPinned) => {
32
+ return TABLE_CHAT_API.patch(`/topic/${topicId}/pin`, { is_pinned: isPinned });
33
+ };
34
+
35
+ // Message related APIs
36
+ export const createMessage = async (payload) => {
37
+ return TABLE_CHAT_API.post(`/message`, payload);
38
+ };
39
+
40
+ export const getTopicMessages = async (topicId, queryParams) => {
41
+ return TABLE_CHAT_API.get(`/messages/${topicId}/paginated`, {
42
+ params: queryParams,
43
+ });
44
+ };
45
+ export const clearTopicMessages = async (topicId) => {
46
+ return TABLE_CHAT_API.delete(`/messages/${topicId}/clear`);
47
+ };
48
+ export const updateMessage = async (messageId, message) => {
49
+ return TABLE_CHAT_API.patch(`/message/${messageId}`, message);
50
+ };
51
+ export const deleteMessage = async (messageId) => {
52
+ return TABLE_CHAT_API.delete(`/message/${messageId}`);
53
+ };
54
+ export const pinUnpinMessage = async (messageId, payload) => {
55
+ return TABLE_CHAT_API.patch(`/message/${messageId}`, payload);
56
+ };
57
+ export const searchMessages = async (topicId, queryParams) => {
58
+ return TABLE_CHAT_API.get(`/messages/${topicId}/paginated`, {
59
+ params: queryParams,
60
+ });
61
+ };
62
+
63
+ // Pinned message related APIs
64
+ export const getPinnedMessages = async (topicId, queryParams) => {
65
+ return TABLE_CHAT_API.get(`/messages/${topicId}/paginated`, {
66
+ params: queryParams,
67
+ });
68
+ };
69
+
70
+ // Member related APIs
71
+ export const fetchMembersForChat = async (topicId) => {
72
+ return TABLE_CHAT_API.get(`/topic-members/${topicId}`);
73
+ };
74
+
75
+ export const fetchNewMembersForChat = async (topicId) => {
76
+ return TABLE_CHAT_API.get(`/topic-members/${topicId}/remaining`);
77
+ };
78
+
79
+ export const addMembersToChat = async (topicId, userIds) => {
80
+ return TABLE_CHAT_API.post(`/topic-members/${topicId}`, userIds);
81
+ };
82
+
83
+ export const removeMembersFromChat = async (topicId, payload) => {
84
+ return TABLE_CHAT_API.delete(`/topic-members/${topicId}`, { data: payload });
85
+ };
@@ -0,0 +1,172 @@
1
+ import React, { useRef } from "react";
2
+ import { Tooltip } from "impact-ui";
3
+ {{STYLES_IMPORT}}
4
+
5
+ /**
6
+ * Wrapper cell renderer that shows original cell content plus an optional special badge (e.g. chat icon).
7
+ * Used by useTableChat to inject chat icon into a column.
8
+ */
9
+ export const SpecialBadgeWrapperCellRenderer = (props) => {
10
+ const {
11
+ originalCellRenderer: OriginalRenderer,
12
+ showSpecialBadge = false,
13
+ specialBadgeContent = null,
14
+ onSpecialBadgeClick,
15
+ ...restProps
16
+ } = props;
17
+
18
+ const renderOriginalContent = () => {
19
+ if (OriginalRenderer) {
20
+ return <OriginalRenderer {...restProps} />;
21
+ }
22
+ return <span>{props.value || ""}</span>;
23
+ };
24
+
25
+ const handleBadgeClick = (e) => {
26
+ e.preventDefault();
27
+ e.stopPropagation();
28
+ if (onSpecialBadgeClick && props.data) {
29
+ onSpecialBadgeClick(props.data);
30
+ }
31
+ };
32
+
33
+ return (
34
+ <div
35
+ className="special-badge-wrapper"
36
+ style={{
37
+ display: "flex",
38
+ alignItems: "center",
39
+ justifyContent: "space-between",
40
+ width: "100%",
41
+ gap: "8px",
42
+ }}
43
+ >
44
+ <div style={{ flex: 1, minWidth: 0 }}>{renderOriginalContent()}</div>
45
+ {showSpecialBadge && (
46
+ <div
47
+ style={{
48
+ cursor: "pointer",
49
+ display: "flex",
50
+ alignItems: "center",
51
+ borderRadius: "4px",
52
+ transition: "background-color 0.2s ease",
53
+ }}
54
+ onClick={handleBadgeClick}
55
+ title='Start New "Chat"'
56
+ >
57
+ {specialBadgeContent}
58
+ </div>
59
+ )}
60
+ </div>
61
+ );
62
+ };
63
+
64
+ /**
65
+ * Colorful chat icon (gradient speech bubble) for the dedicated Chat column.
66
+ */
67
+ export const ColorfulChatIcon = ({ size = 16, className = "", gradientId }) => {
68
+ const idRef = useRef(null);
69
+ if (!idRef.current) {
70
+ idRef.current =
71
+ gradientId ||
72
+ `colorful-chat-gradient-${Math.random().toString(36).slice(2, 11)}`;
73
+ }
74
+ const id = idRef.current;
75
+ return (
76
+ <svg
77
+ xmlns="http://www.w3.org/2000/svg"
78
+ width="17"
79
+ height="16"
80
+ viewBox="0 0 17 16"
81
+ fill="none"
82
+ >
83
+ <path
84
+ d="M1.88867 1H15.1113C15.3481 1.00006 15.5748 1.09396 15.7412 1.25977C15.9074 1.42549 16 1.65002 16 1.88281V11.2939C16 11.5268 15.9075 11.7513 15.7412 11.917C15.5748 12.0828 15.3481 12.1767 15.1113 12.1768H4.4082C3.78401 12.1768 3.17582 12.3782 2.67578 12.752L1 14.0039V1.88281C1 1.65002 1.09261 1.42549 1.25879 1.25977C1.42517 1.09396 1.65189 1.00006 1.88867 1Z"
85
+ stroke="url(#paint0_linear_1607_2520)"
86
+ strokeWidth="2"
87
+ />
88
+ <defs>
89
+ <linearGradient
90
+ id="paint0_linear_1607_2520"
91
+ x1="17"
92
+ y1="8"
93
+ x2="0"
94
+ y2="8"
95
+ gradientUnits="userSpaceOnUse"
96
+ >
97
+ <stop stopColor="#4259EE" />
98
+ <stop offset="1" stopColor="#F19579" />
99
+ </linearGradient>
100
+ </defs>
101
+ </svg>
102
+ );
103
+ };
104
+
105
+ /**
106
+ * Cell renderer for the dedicated fixed Chat column.
107
+ * Shows chat icon when the row has chat; supports unread count badge and pulsing indicator.
108
+ */
109
+ export const ChatColumnCellRenderer = (props) => {
110
+ const params = props?.colDef?.cellRendererParams || {};
111
+ const {
112
+ promoIdsWithChats = new Set(),
113
+ uniqueRowId = "promo_id",
114
+ onChatClick,
115
+ isTopicPulsing = () => false,
116
+ promoIdToUnreadCount = {},
117
+ } = params;
118
+
119
+ const rowData = props?.data;
120
+ if (!rowData || !uniqueRowId) return null;
121
+
122
+ const rowId = rowData[uniqueRowId];
123
+ if (rowId === undefined || rowId === null) return null;
124
+
125
+ const hasChat = promoIdsWithChats.has(rowId);
126
+ if (!hasChat) return null;
127
+
128
+ const isPulsing = typeof isTopicPulsing === "function" ? isTopicPulsing(rowId) : false;
129
+ const unreadCount = promoIdToUnreadCount[rowId] ?? promoIdToUnreadCount[String(rowId)] ?? 0;
130
+
131
+ const handleClick = (e) => {
132
+ e.preventDefault();
133
+ e.stopPropagation();
134
+ if (onChatClick && rowData) onChatClick(rowData);
135
+ };
136
+
137
+ return (
138
+ <div
139
+ className="chat-column-cell"
140
+ style={{
141
+ display: "flex",
142
+ alignItems: "center",
143
+ justifyContent: "center",
144
+ width: "100%",
145
+ minHeight: "100%",
146
+ }}
147
+ >
148
+ <Tooltip title='Start New "Chat"' placement="left" variant="tertiary">
149
+ <div
150
+ className="chat-column-icon-wrapper"
151
+ role="button"
152
+ tabIndex={0}
153
+ onClick={handleClick}
154
+ onKeyDown={(e) => {
155
+ if (e.key === "Enter" || e.key === " ") {
156
+ e.preventDefault();
157
+ handleClick(e);
158
+ }
159
+ }}
160
+ >
161
+ <ColorfulChatIcon size={16} />
162
+ {unreadCount > 0 && (
163
+ <span className="chat-column-unread-badge">
164
+ {unreadCount > 99 ? "99+" : unreadCount}
165
+ </span>
166
+ )}
167
+ {isPulsing && <div className="chat-notification-dot-pulse-slow" />}
168
+ </div>
169
+ </Tooltip>
170
+ </div>
171
+ );
172
+ };