@optifye/dashboard-core 4.2.1 → 4.2.3
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/index.d.mts +56 -1
- package/dist/index.d.ts +56 -1
- package/dist/index.js +153 -110
- package/dist/index.mjs +153 -110
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -212,6 +212,7 @@ interface EndpointsConfig {
|
|
|
212
212
|
worstPerformingWorkspaces?: string;
|
|
213
213
|
agnoApiUrl?: string;
|
|
214
214
|
slackWebhookUrl?: string;
|
|
215
|
+
slackProxyEndpoint?: string;
|
|
215
216
|
}
|
|
216
217
|
interface DateTimeConfig {
|
|
217
218
|
defaultTimezone?: string;
|
|
@@ -4344,9 +4345,63 @@ interface SupportTicket {
|
|
|
4344
4345
|
}
|
|
4345
4346
|
declare class SlackAPI {
|
|
4346
4347
|
/**
|
|
4347
|
-
* Sends a support ticket notification
|
|
4348
|
+
* Sends a support ticket notification to Slack.
|
|
4349
|
+
* 1. If slackWebhookUrl is configured, send directly (preferred)
|
|
4350
|
+
* 2. Otherwise, if slackProxyEndpoint is configured, send to the proxy
|
|
4351
|
+
* If neither is configured, logs a warning.
|
|
4348
4352
|
*/
|
|
4349
4353
|
static sendSupportTicketNotification(ticket: SupportTicket): Promise<void>;
|
|
4354
|
+
/**
|
|
4355
|
+
* Formats a support ticket into a Slack message format
|
|
4356
|
+
* This can be used by server-side implementations
|
|
4357
|
+
*/
|
|
4358
|
+
static formatSlackMessage(ticket: SupportTicket): {
|
|
4359
|
+
text: string;
|
|
4360
|
+
blocks: ({
|
|
4361
|
+
type: string;
|
|
4362
|
+
text: {
|
|
4363
|
+
type: string;
|
|
4364
|
+
text: string;
|
|
4365
|
+
emoji: boolean;
|
|
4366
|
+
};
|
|
4367
|
+
fields?: undefined;
|
|
4368
|
+
elements?: undefined;
|
|
4369
|
+
} | {
|
|
4370
|
+
type: string;
|
|
4371
|
+
fields: {
|
|
4372
|
+
type: string;
|
|
4373
|
+
text: string;
|
|
4374
|
+
}[];
|
|
4375
|
+
text?: undefined;
|
|
4376
|
+
elements?: undefined;
|
|
4377
|
+
} | {
|
|
4378
|
+
type: string;
|
|
4379
|
+
text: {
|
|
4380
|
+
type: string;
|
|
4381
|
+
text: string;
|
|
4382
|
+
emoji?: undefined;
|
|
4383
|
+
};
|
|
4384
|
+
fields?: undefined;
|
|
4385
|
+
elements?: undefined;
|
|
4386
|
+
} | {
|
|
4387
|
+
type: string;
|
|
4388
|
+
text?: undefined;
|
|
4389
|
+
fields?: undefined;
|
|
4390
|
+
elements?: undefined;
|
|
4391
|
+
} | {
|
|
4392
|
+
type: string;
|
|
4393
|
+
elements: {
|
|
4394
|
+
type: string;
|
|
4395
|
+
text: string;
|
|
4396
|
+
}[];
|
|
4397
|
+
text?: undefined;
|
|
4398
|
+
fields?: undefined;
|
|
4399
|
+
})[];
|
|
4400
|
+
attachments: {
|
|
4401
|
+
color: string;
|
|
4402
|
+
fields: never[];
|
|
4403
|
+
}[];
|
|
4404
|
+
};
|
|
4350
4405
|
}
|
|
4351
4406
|
|
|
4352
4407
|
interface ThreadSidebarProps {
|
package/dist/index.d.ts
CHANGED
|
@@ -212,6 +212,7 @@ interface EndpointsConfig {
|
|
|
212
212
|
worstPerformingWorkspaces?: string;
|
|
213
213
|
agnoApiUrl?: string;
|
|
214
214
|
slackWebhookUrl?: string;
|
|
215
|
+
slackProxyEndpoint?: string;
|
|
215
216
|
}
|
|
216
217
|
interface DateTimeConfig {
|
|
217
218
|
defaultTimezone?: string;
|
|
@@ -4344,9 +4345,63 @@ interface SupportTicket {
|
|
|
4344
4345
|
}
|
|
4345
4346
|
declare class SlackAPI {
|
|
4346
4347
|
/**
|
|
4347
|
-
* Sends a support ticket notification
|
|
4348
|
+
* Sends a support ticket notification to Slack.
|
|
4349
|
+
* 1. If slackWebhookUrl is configured, send directly (preferred)
|
|
4350
|
+
* 2. Otherwise, if slackProxyEndpoint is configured, send to the proxy
|
|
4351
|
+
* If neither is configured, logs a warning.
|
|
4348
4352
|
*/
|
|
4349
4353
|
static sendSupportTicketNotification(ticket: SupportTicket): Promise<void>;
|
|
4354
|
+
/**
|
|
4355
|
+
* Formats a support ticket into a Slack message format
|
|
4356
|
+
* This can be used by server-side implementations
|
|
4357
|
+
*/
|
|
4358
|
+
static formatSlackMessage(ticket: SupportTicket): {
|
|
4359
|
+
text: string;
|
|
4360
|
+
blocks: ({
|
|
4361
|
+
type: string;
|
|
4362
|
+
text: {
|
|
4363
|
+
type: string;
|
|
4364
|
+
text: string;
|
|
4365
|
+
emoji: boolean;
|
|
4366
|
+
};
|
|
4367
|
+
fields?: undefined;
|
|
4368
|
+
elements?: undefined;
|
|
4369
|
+
} | {
|
|
4370
|
+
type: string;
|
|
4371
|
+
fields: {
|
|
4372
|
+
type: string;
|
|
4373
|
+
text: string;
|
|
4374
|
+
}[];
|
|
4375
|
+
text?: undefined;
|
|
4376
|
+
elements?: undefined;
|
|
4377
|
+
} | {
|
|
4378
|
+
type: string;
|
|
4379
|
+
text: {
|
|
4380
|
+
type: string;
|
|
4381
|
+
text: string;
|
|
4382
|
+
emoji?: undefined;
|
|
4383
|
+
};
|
|
4384
|
+
fields?: undefined;
|
|
4385
|
+
elements?: undefined;
|
|
4386
|
+
} | {
|
|
4387
|
+
type: string;
|
|
4388
|
+
text?: undefined;
|
|
4389
|
+
fields?: undefined;
|
|
4390
|
+
elements?: undefined;
|
|
4391
|
+
} | {
|
|
4392
|
+
type: string;
|
|
4393
|
+
elements: {
|
|
4394
|
+
type: string;
|
|
4395
|
+
text: string;
|
|
4396
|
+
}[];
|
|
4397
|
+
text?: undefined;
|
|
4398
|
+
fields?: undefined;
|
|
4399
|
+
})[];
|
|
4400
|
+
attachments: {
|
|
4401
|
+
color: string;
|
|
4402
|
+
fields: never[];
|
|
4403
|
+
}[];
|
|
4404
|
+
};
|
|
4350
4405
|
}
|
|
4351
4406
|
|
|
4352
4407
|
interface ThreadSidebarProps {
|
package/dist/index.js
CHANGED
|
@@ -139,8 +139,10 @@ var DEFAULT_ENDPOINTS_CONFIG = {
|
|
|
139
139
|
whatsapp: "/api/send-whatsapp-direct",
|
|
140
140
|
agnoApiUrl: process.env.NEXT_PUBLIC_AGNO_URL || "https://optifye-agent-production.up.railway.app",
|
|
141
141
|
// Default AGNO API URL
|
|
142
|
-
|
|
143
|
-
//
|
|
142
|
+
// Use environment variable for Slack webhook URL for privacy/security
|
|
143
|
+
// Note: SLACK_WEBHOOK_URL is server-side only, NEXT_PUBLIC_SLACK_WEBHOOK_URL works client-side but is less secure
|
|
144
|
+
slackWebhookUrl: process.env.SLACK_WEBHOOK_URL || process.env.NEXT_PUBLIC_SLACK_WEBHOOK_URL || void 0,
|
|
145
|
+
slackProxyEndpoint: void 0
|
|
144
146
|
};
|
|
145
147
|
var DEFAULT_THEME_CONFIG = {
|
|
146
148
|
// Sensible defaults for theme can be added here
|
|
@@ -23226,13 +23228,13 @@ var SideNavBar = React33.memo(({
|
|
|
23226
23228
|
{
|
|
23227
23229
|
onClick: handleKPIsClick,
|
|
23228
23230
|
className: kpisButtonClasses,
|
|
23229
|
-
"aria-label": "
|
|
23231
|
+
"aria-label": "Lines",
|
|
23230
23232
|
tabIndex: 0,
|
|
23231
23233
|
role: "tab",
|
|
23232
23234
|
"aria-selected": pathname === "/kpis" || pathname.startsWith("/kpis/"),
|
|
23233
23235
|
children: [
|
|
23234
23236
|
/* @__PURE__ */ jsxRuntime.jsx(outline.ChartBarIcon, { className: "w-5 h-5 mb-1" }),
|
|
23235
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-medium leading-tight", children: "
|
|
23237
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-medium leading-tight", children: "Lines" })
|
|
23236
23238
|
]
|
|
23237
23239
|
}
|
|
23238
23240
|
),
|
|
@@ -25145,125 +25147,166 @@ var streamProxyConfig = {
|
|
|
25145
25147
|
// src/lib/api/slackApi.ts
|
|
25146
25148
|
var SlackAPI = class {
|
|
25147
25149
|
/**
|
|
25148
|
-
* Sends a support ticket notification
|
|
25150
|
+
* Sends a support ticket notification to Slack.
|
|
25151
|
+
* 1. If slackWebhookUrl is configured, send directly (preferred)
|
|
25152
|
+
* 2. Otherwise, if slackProxyEndpoint is configured, send to the proxy
|
|
25153
|
+
* If neither is configured, logs a warning.
|
|
25149
25154
|
*/
|
|
25150
25155
|
static async sendSupportTicketNotification(ticket) {
|
|
25151
25156
|
try {
|
|
25152
25157
|
const config = _getDashboardConfigInstance();
|
|
25153
25158
|
const endpointsConfig = config.endpoints ?? DEFAULT_ENDPOINTS_CONFIG;
|
|
25154
|
-
const slackWebhookUrl = endpointsConfig
|
|
25155
|
-
|
|
25156
|
-
|
|
25159
|
+
const { slackWebhookUrl, slackProxyEndpoint } = endpointsConfig;
|
|
25160
|
+
console.log("SlackAPI Debug - Configuration check:", {
|
|
25161
|
+
hasConfig: !!config,
|
|
25162
|
+
hasEndpoints: !!config.endpoints,
|
|
25163
|
+
slackWebhookUrl: slackWebhookUrl ? "configured" : "not configured",
|
|
25164
|
+
slackProxyEndpoint: slackProxyEndpoint ? "configured" : "not configured",
|
|
25165
|
+
envVariable: process.env.SLACK_WEBHOOK_URL ? "set" : "not set",
|
|
25166
|
+
publicEnvVariable: process.env.NEXT_PUBLIC_SLACK_WEBHOOK_URL ? "set" : "not set"
|
|
25167
|
+
});
|
|
25168
|
+
if (process.env.NEXT_PUBLIC_SLACK_WEBHOOK_URL && !process.env.SLACK_WEBHOOK_URL) {
|
|
25169
|
+
console.warn("\u26A0\uFE0F SECURITY WARNING: Using NEXT_PUBLIC_SLACK_WEBHOOK_URL exposes your webhook URL to the client. Consider using a server-side proxy instead.");
|
|
25170
|
+
}
|
|
25171
|
+
if (slackWebhookUrl) {
|
|
25172
|
+
const slackMessage = this.formatSlackMessage(ticket);
|
|
25173
|
+
const response = await fetch(slackWebhookUrl, {
|
|
25174
|
+
method: "POST",
|
|
25175
|
+
headers: {
|
|
25176
|
+
// Use form-urlencoded + simple headers to avoid CORS preflight
|
|
25177
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
25178
|
+
},
|
|
25179
|
+
body: `payload=${encodeURIComponent(JSON.stringify(slackMessage))}`
|
|
25180
|
+
});
|
|
25181
|
+
if (!response.ok) {
|
|
25182
|
+
const errorText = await response.text();
|
|
25183
|
+
console.error("Slack webhook error:", errorText);
|
|
25184
|
+
throw new Error(`Slack webhook failed with status: ${response.status}`);
|
|
25185
|
+
}
|
|
25186
|
+
console.log("Support ticket notification sent to Slack successfully via webhook");
|
|
25157
25187
|
return;
|
|
25158
25188
|
}
|
|
25159
|
-
|
|
25160
|
-
|
|
25161
|
-
|
|
25162
|
-
|
|
25163
|
-
|
|
25164
|
-
const maxDescriptionLength = 300;
|
|
25165
|
-
const description = ticket.description.length > maxDescriptionLength ? `${ticket.description.substring(0, maxDescriptionLength)}...` : ticket.description;
|
|
25166
|
-
const categoryConfig = {
|
|
25167
|
-
general: { emoji: "\u{1F4AC}", color: "#36a64f" },
|
|
25168
|
-
technical: { emoji: "\u{1F527}", color: "#ff6b6b" },
|
|
25169
|
-
feature: { emoji: "\u2728", color: "#4ecdc4" },
|
|
25170
|
-
billing: { emoji: "\u{1F4B3}", color: "#f7b731" }
|
|
25171
|
-
};
|
|
25172
|
-
const priorityConfig = {
|
|
25173
|
-
low: { emoji: "\u{1F7E2}", color: "#36a64f" },
|
|
25174
|
-
normal: { emoji: "\u{1F7E1}", color: "#ffc107" },
|
|
25175
|
-
high: { emoji: "\u{1F7E0}", color: "#ff8c00" },
|
|
25176
|
-
urgent: { emoji: "\u{1F534}", color: "#dc3545" }
|
|
25177
|
-
};
|
|
25178
|
-
const categoryInfo = categoryConfig[ticket.category] || categoryConfig.general;
|
|
25179
|
-
const priorityInfo = priorityConfig[ticket.priority] || priorityConfig.normal;
|
|
25180
|
-
const messageColor = ticket.priority === "high" || ticket.priority === "urgent" ? priorityInfo.color : categoryInfo.color;
|
|
25181
|
-
const slackMessage = {
|
|
25182
|
-
text: `New support ticket from ${ticket.email}`,
|
|
25183
|
-
blocks: [
|
|
25184
|
-
{
|
|
25185
|
-
type: "header",
|
|
25186
|
-
text: {
|
|
25187
|
-
type: "plain_text",
|
|
25188
|
-
text: "\u{1F3AB} New Support Ticket Submitted",
|
|
25189
|
-
emoji: true
|
|
25190
|
-
}
|
|
25189
|
+
if (slackProxyEndpoint) {
|
|
25190
|
+
const response = await fetch(slackProxyEndpoint, {
|
|
25191
|
+
method: "POST",
|
|
25192
|
+
headers: {
|
|
25193
|
+
"Content-Type": "application/json"
|
|
25191
25194
|
},
|
|
25192
|
-
|
|
25193
|
-
|
|
25194
|
-
|
|
25195
|
-
|
|
25196
|
-
|
|
25197
|
-
|
|
25195
|
+
body: JSON.stringify(ticket)
|
|
25196
|
+
});
|
|
25197
|
+
if (!response.ok) {
|
|
25198
|
+
const errorData = await response.json().catch(() => ({ message: "Unknown error" }));
|
|
25199
|
+
throw new Error(errorData.message || `Proxy failed with status: ${response.status}`);
|
|
25200
|
+
}
|
|
25201
|
+
console.log("Support ticket notification sent to Slack successfully via proxy");
|
|
25202
|
+
return;
|
|
25203
|
+
}
|
|
25204
|
+
console.warn("Slack notification skipped: No webhook or proxy endpoint configured");
|
|
25205
|
+
console.info("To fix this, either:");
|
|
25206
|
+
console.info("1. Set up a server-side proxy endpoint and configure slackProxyEndpoint in your dashboard config");
|
|
25207
|
+
console.info("2. For development only: use NEXT_PUBLIC_SLACK_WEBHOOK_URL (not recommended for production)");
|
|
25208
|
+
console.info("3. Configure the webhook URL directly in your dashboard config at runtime");
|
|
25209
|
+
} catch (error) {
|
|
25210
|
+
console.error("Failed to send Slack notification:", error);
|
|
25211
|
+
throw error;
|
|
25212
|
+
}
|
|
25213
|
+
}
|
|
25214
|
+
/**
|
|
25215
|
+
* Formats a support ticket into a Slack message format
|
|
25216
|
+
* This can be used by server-side implementations
|
|
25217
|
+
*/
|
|
25218
|
+
static formatSlackMessage(ticket) {
|
|
25219
|
+
const timestamp = new Date(ticket.timestamp).toLocaleString("en-US", {
|
|
25220
|
+
dateStyle: "medium",
|
|
25221
|
+
timeStyle: "short",
|
|
25222
|
+
timeZone: "UTC"
|
|
25223
|
+
});
|
|
25224
|
+
const maxDescriptionLength = 300;
|
|
25225
|
+
const description = ticket.description.length > maxDescriptionLength ? `${ticket.description.substring(0, maxDescriptionLength)}...` : ticket.description;
|
|
25226
|
+
const categoryConfig = {
|
|
25227
|
+
general: { emoji: "\u{1F4AC}", color: "#36a64f" },
|
|
25228
|
+
technical: { emoji: "\u{1F527}", color: "#ff6b6b" },
|
|
25229
|
+
feature: { emoji: "\u2728", color: "#4ecdc4" },
|
|
25230
|
+
billing: { emoji: "\u{1F4B3}", color: "#f7b731" }
|
|
25231
|
+
};
|
|
25232
|
+
const priorityConfig = {
|
|
25233
|
+
low: { emoji: "\u{1F7E2}", color: "#36a64f" },
|
|
25234
|
+
normal: { emoji: "\u{1F7E1}", color: "#ffc107" },
|
|
25235
|
+
high: { emoji: "\u{1F7E0}", color: "#ff8c00" },
|
|
25236
|
+
urgent: { emoji: "\u{1F534}", color: "#dc3545" }
|
|
25237
|
+
};
|
|
25238
|
+
const categoryInfo = categoryConfig[ticket.category] || categoryConfig.general;
|
|
25239
|
+
const priorityInfo = priorityConfig[ticket.priority] || priorityConfig.normal;
|
|
25240
|
+
const messageColor = ticket.priority === "high" || ticket.priority === "urgent" ? priorityInfo.color : categoryInfo.color;
|
|
25241
|
+
return {
|
|
25242
|
+
text: `New support ticket from ${ticket.email}`,
|
|
25243
|
+
blocks: [
|
|
25244
|
+
{
|
|
25245
|
+
type: "header",
|
|
25246
|
+
text: {
|
|
25247
|
+
type: "plain_text",
|
|
25248
|
+
text: "\u{1F3AB} New Support Ticket Submitted",
|
|
25249
|
+
emoji: true
|
|
25250
|
+
}
|
|
25251
|
+
},
|
|
25252
|
+
{
|
|
25253
|
+
type: "section",
|
|
25254
|
+
fields: [
|
|
25255
|
+
{
|
|
25256
|
+
type: "mrkdwn",
|
|
25257
|
+
text: `*From:*
|
|
25198
25258
|
${ticket.email}`
|
|
25199
|
-
|
|
25200
|
-
|
|
25201
|
-
|
|
25202
|
-
|
|
25259
|
+
},
|
|
25260
|
+
{
|
|
25261
|
+
type: "mrkdwn",
|
|
25262
|
+
text: `*Category:*
|
|
25203
25263
|
${categoryInfo.emoji} ${ticket.category.charAt(0).toUpperCase() + ticket.category.slice(1)}`
|
|
25204
|
-
|
|
25205
|
-
|
|
25206
|
-
|
|
25207
|
-
|
|
25264
|
+
},
|
|
25265
|
+
{
|
|
25266
|
+
type: "mrkdwn",
|
|
25267
|
+
text: `*Priority:*
|
|
25208
25268
|
${priorityInfo.emoji} ${ticket.priority.charAt(0).toUpperCase() + ticket.priority.slice(1)}`
|
|
25209
|
-
|
|
25210
|
-
|
|
25211
|
-
|
|
25212
|
-
|
|
25269
|
+
},
|
|
25270
|
+
{
|
|
25271
|
+
type: "mrkdwn",
|
|
25272
|
+
text: `*Subject:*
|
|
25213
25273
|
${ticket.subject}`
|
|
25214
|
-
|
|
25215
|
-
|
|
25216
|
-
type: "mrkdwn",
|
|
25217
|
-
text: `*Submitted:*
|
|
25218
|
-
${timestamp} UTC`
|
|
25219
|
-
}
|
|
25220
|
-
]
|
|
25221
|
-
},
|
|
25222
|
-
{
|
|
25223
|
-
type: "section",
|
|
25224
|
-
text: {
|
|
25274
|
+
},
|
|
25275
|
+
{
|
|
25225
25276
|
type: "mrkdwn",
|
|
25226
|
-
text: `*
|
|
25227
|
-
${
|
|
25277
|
+
text: `*Submitted:*
|
|
25278
|
+
${timestamp} UTC`
|
|
25228
25279
|
}
|
|
25229
|
-
|
|
25230
|
-
|
|
25231
|
-
|
|
25232
|
-
|
|
25233
|
-
{
|
|
25234
|
-
type: "
|
|
25235
|
-
|
|
25236
|
-
|
|
25237
|
-
type: "mrkdwn",
|
|
25238
|
-
text: `Category: \`${ticket.category}\` | Priority: ${priorityInfo.emoji} \`${ticket.priority.toUpperCase()}\` | ${ticket.priority === "urgent" ? "\u26A0\uFE0F Requires immediate attention" : "Standard processing"}`
|
|
25239
|
-
}
|
|
25240
|
-
]
|
|
25241
|
-
}
|
|
25242
|
-
],
|
|
25243
|
-
attachments: [
|
|
25244
|
-
{
|
|
25245
|
-
color: messageColor,
|
|
25246
|
-
fields: []
|
|
25280
|
+
]
|
|
25281
|
+
},
|
|
25282
|
+
{
|
|
25283
|
+
type: "section",
|
|
25284
|
+
text: {
|
|
25285
|
+
type: "mrkdwn",
|
|
25286
|
+
text: `*Description:*
|
|
25287
|
+
${description}`
|
|
25247
25288
|
}
|
|
25248
|
-
]
|
|
25249
|
-
};
|
|
25250
|
-
const response = await fetch(slackWebhookUrl, {
|
|
25251
|
-
method: "POST",
|
|
25252
|
-
headers: {
|
|
25253
|
-
"Content-Type": "application/json"
|
|
25254
25289
|
},
|
|
25255
|
-
|
|
25256
|
-
|
|
25257
|
-
|
|
25258
|
-
|
|
25259
|
-
|
|
25260
|
-
|
|
25261
|
-
|
|
25262
|
-
|
|
25263
|
-
|
|
25264
|
-
|
|
25265
|
-
|
|
25266
|
-
|
|
25290
|
+
{
|
|
25291
|
+
type: "divider"
|
|
25292
|
+
},
|
|
25293
|
+
{
|
|
25294
|
+
type: "context",
|
|
25295
|
+
elements: [
|
|
25296
|
+
{
|
|
25297
|
+
type: "mrkdwn",
|
|
25298
|
+
text: `Category: \`${ticket.category}\` | Priority: ${priorityInfo.emoji} \`${ticket.priority.toUpperCase()}\` | ${ticket.priority === "urgent" ? "\u26A0\uFE0F Requires immediate attention" : "Standard processing"}`
|
|
25299
|
+
}
|
|
25300
|
+
]
|
|
25301
|
+
}
|
|
25302
|
+
],
|
|
25303
|
+
attachments: [
|
|
25304
|
+
{
|
|
25305
|
+
color: messageColor,
|
|
25306
|
+
fields: []
|
|
25307
|
+
}
|
|
25308
|
+
]
|
|
25309
|
+
};
|
|
25267
25310
|
}
|
|
25268
25311
|
};
|
|
25269
25312
|
var HelpView = ({
|
|
@@ -26706,7 +26749,7 @@ var KPIsOverviewView = ({
|
|
|
26706
26749
|
}
|
|
26707
26750
|
),
|
|
26708
26751
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
26709
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl font-bold text-gray-800 tracking-tight", children: "
|
|
26752
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl font-bold text-gray-800 tracking-tight", children: "Shop-floor overview" }),
|
|
26710
26753
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2 w-2 rounded-full bg-green-500 animate-pulse ring-2 ring-green-500/30 ring-offset-1" })
|
|
26711
26754
|
] }) })
|
|
26712
26755
|
] }) }) }),
|
|
@@ -26728,7 +26771,7 @@ var KPIsOverviewView = ({
|
|
|
26728
26771
|
}
|
|
26729
26772
|
),
|
|
26730
26773
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
26731
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl font-bold text-gray-800 tracking-tight", children: "
|
|
26774
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl font-bold text-gray-800 tracking-tight", children: "Shop-floor overview" }),
|
|
26732
26775
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2 w-2 rounded-full bg-green-500 animate-pulse ring-2 ring-green-500/30 ring-offset-1" })
|
|
26733
26776
|
] }) })
|
|
26734
26777
|
] }) }) }),
|
|
@@ -26753,7 +26796,7 @@ var KPIsOverviewView = ({
|
|
|
26753
26796
|
}
|
|
26754
26797
|
),
|
|
26755
26798
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
26756
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl font-bold text-gray-800 tracking-tight", children: "
|
|
26799
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl font-bold text-gray-800 tracking-tight", children: "Shop-floor overview" }),
|
|
26757
26800
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2 w-2 rounded-full bg-green-500 animate-pulse ring-2 ring-green-500/30 ring-offset-1" })
|
|
26758
26801
|
] }) })
|
|
26759
26802
|
] }),
|
package/dist/index.mjs
CHANGED
|
@@ -110,8 +110,10 @@ var DEFAULT_ENDPOINTS_CONFIG = {
|
|
|
110
110
|
whatsapp: "/api/send-whatsapp-direct",
|
|
111
111
|
agnoApiUrl: process.env.NEXT_PUBLIC_AGNO_URL || "https://optifye-agent-production.up.railway.app",
|
|
112
112
|
// Default AGNO API URL
|
|
113
|
-
|
|
114
|
-
//
|
|
113
|
+
// Use environment variable for Slack webhook URL for privacy/security
|
|
114
|
+
// Note: SLACK_WEBHOOK_URL is server-side only, NEXT_PUBLIC_SLACK_WEBHOOK_URL works client-side but is less secure
|
|
115
|
+
slackWebhookUrl: process.env.SLACK_WEBHOOK_URL || process.env.NEXT_PUBLIC_SLACK_WEBHOOK_URL || void 0,
|
|
116
|
+
slackProxyEndpoint: void 0
|
|
115
117
|
};
|
|
116
118
|
var DEFAULT_THEME_CONFIG = {
|
|
117
119
|
// Sensible defaults for theme can be added here
|
|
@@ -23197,13 +23199,13 @@ var SideNavBar = memo(({
|
|
|
23197
23199
|
{
|
|
23198
23200
|
onClick: handleKPIsClick,
|
|
23199
23201
|
className: kpisButtonClasses,
|
|
23200
|
-
"aria-label": "
|
|
23202
|
+
"aria-label": "Lines",
|
|
23201
23203
|
tabIndex: 0,
|
|
23202
23204
|
role: "tab",
|
|
23203
23205
|
"aria-selected": pathname === "/kpis" || pathname.startsWith("/kpis/"),
|
|
23204
23206
|
children: [
|
|
23205
23207
|
/* @__PURE__ */ jsx(ChartBarIcon, { className: "w-5 h-5 mb-1" }),
|
|
23206
|
-
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium leading-tight", children: "
|
|
23208
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium leading-tight", children: "Lines" })
|
|
23207
23209
|
]
|
|
23208
23210
|
}
|
|
23209
23211
|
),
|
|
@@ -25116,125 +25118,166 @@ var streamProxyConfig = {
|
|
|
25116
25118
|
// src/lib/api/slackApi.ts
|
|
25117
25119
|
var SlackAPI = class {
|
|
25118
25120
|
/**
|
|
25119
|
-
* Sends a support ticket notification
|
|
25121
|
+
* Sends a support ticket notification to Slack.
|
|
25122
|
+
* 1. If slackWebhookUrl is configured, send directly (preferred)
|
|
25123
|
+
* 2. Otherwise, if slackProxyEndpoint is configured, send to the proxy
|
|
25124
|
+
* If neither is configured, logs a warning.
|
|
25120
25125
|
*/
|
|
25121
25126
|
static async sendSupportTicketNotification(ticket) {
|
|
25122
25127
|
try {
|
|
25123
25128
|
const config = _getDashboardConfigInstance();
|
|
25124
25129
|
const endpointsConfig = config.endpoints ?? DEFAULT_ENDPOINTS_CONFIG;
|
|
25125
|
-
const slackWebhookUrl = endpointsConfig
|
|
25126
|
-
|
|
25127
|
-
|
|
25130
|
+
const { slackWebhookUrl, slackProxyEndpoint } = endpointsConfig;
|
|
25131
|
+
console.log("SlackAPI Debug - Configuration check:", {
|
|
25132
|
+
hasConfig: !!config,
|
|
25133
|
+
hasEndpoints: !!config.endpoints,
|
|
25134
|
+
slackWebhookUrl: slackWebhookUrl ? "configured" : "not configured",
|
|
25135
|
+
slackProxyEndpoint: slackProxyEndpoint ? "configured" : "not configured",
|
|
25136
|
+
envVariable: process.env.SLACK_WEBHOOK_URL ? "set" : "not set",
|
|
25137
|
+
publicEnvVariable: process.env.NEXT_PUBLIC_SLACK_WEBHOOK_URL ? "set" : "not set"
|
|
25138
|
+
});
|
|
25139
|
+
if (process.env.NEXT_PUBLIC_SLACK_WEBHOOK_URL && !process.env.SLACK_WEBHOOK_URL) {
|
|
25140
|
+
console.warn("\u26A0\uFE0F SECURITY WARNING: Using NEXT_PUBLIC_SLACK_WEBHOOK_URL exposes your webhook URL to the client. Consider using a server-side proxy instead.");
|
|
25141
|
+
}
|
|
25142
|
+
if (slackWebhookUrl) {
|
|
25143
|
+
const slackMessage = this.formatSlackMessage(ticket);
|
|
25144
|
+
const response = await fetch(slackWebhookUrl, {
|
|
25145
|
+
method: "POST",
|
|
25146
|
+
headers: {
|
|
25147
|
+
// Use form-urlencoded + simple headers to avoid CORS preflight
|
|
25148
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
25149
|
+
},
|
|
25150
|
+
body: `payload=${encodeURIComponent(JSON.stringify(slackMessage))}`
|
|
25151
|
+
});
|
|
25152
|
+
if (!response.ok) {
|
|
25153
|
+
const errorText = await response.text();
|
|
25154
|
+
console.error("Slack webhook error:", errorText);
|
|
25155
|
+
throw new Error(`Slack webhook failed with status: ${response.status}`);
|
|
25156
|
+
}
|
|
25157
|
+
console.log("Support ticket notification sent to Slack successfully via webhook");
|
|
25128
25158
|
return;
|
|
25129
25159
|
}
|
|
25130
|
-
|
|
25131
|
-
|
|
25132
|
-
|
|
25133
|
-
|
|
25134
|
-
|
|
25135
|
-
const maxDescriptionLength = 300;
|
|
25136
|
-
const description = ticket.description.length > maxDescriptionLength ? `${ticket.description.substring(0, maxDescriptionLength)}...` : ticket.description;
|
|
25137
|
-
const categoryConfig = {
|
|
25138
|
-
general: { emoji: "\u{1F4AC}", color: "#36a64f" },
|
|
25139
|
-
technical: { emoji: "\u{1F527}", color: "#ff6b6b" },
|
|
25140
|
-
feature: { emoji: "\u2728", color: "#4ecdc4" },
|
|
25141
|
-
billing: { emoji: "\u{1F4B3}", color: "#f7b731" }
|
|
25142
|
-
};
|
|
25143
|
-
const priorityConfig = {
|
|
25144
|
-
low: { emoji: "\u{1F7E2}", color: "#36a64f" },
|
|
25145
|
-
normal: { emoji: "\u{1F7E1}", color: "#ffc107" },
|
|
25146
|
-
high: { emoji: "\u{1F7E0}", color: "#ff8c00" },
|
|
25147
|
-
urgent: { emoji: "\u{1F534}", color: "#dc3545" }
|
|
25148
|
-
};
|
|
25149
|
-
const categoryInfo = categoryConfig[ticket.category] || categoryConfig.general;
|
|
25150
|
-
const priorityInfo = priorityConfig[ticket.priority] || priorityConfig.normal;
|
|
25151
|
-
const messageColor = ticket.priority === "high" || ticket.priority === "urgent" ? priorityInfo.color : categoryInfo.color;
|
|
25152
|
-
const slackMessage = {
|
|
25153
|
-
text: `New support ticket from ${ticket.email}`,
|
|
25154
|
-
blocks: [
|
|
25155
|
-
{
|
|
25156
|
-
type: "header",
|
|
25157
|
-
text: {
|
|
25158
|
-
type: "plain_text",
|
|
25159
|
-
text: "\u{1F3AB} New Support Ticket Submitted",
|
|
25160
|
-
emoji: true
|
|
25161
|
-
}
|
|
25160
|
+
if (slackProxyEndpoint) {
|
|
25161
|
+
const response = await fetch(slackProxyEndpoint, {
|
|
25162
|
+
method: "POST",
|
|
25163
|
+
headers: {
|
|
25164
|
+
"Content-Type": "application/json"
|
|
25162
25165
|
},
|
|
25163
|
-
|
|
25164
|
-
|
|
25165
|
-
|
|
25166
|
-
|
|
25167
|
-
|
|
25168
|
-
|
|
25166
|
+
body: JSON.stringify(ticket)
|
|
25167
|
+
});
|
|
25168
|
+
if (!response.ok) {
|
|
25169
|
+
const errorData = await response.json().catch(() => ({ message: "Unknown error" }));
|
|
25170
|
+
throw new Error(errorData.message || `Proxy failed with status: ${response.status}`);
|
|
25171
|
+
}
|
|
25172
|
+
console.log("Support ticket notification sent to Slack successfully via proxy");
|
|
25173
|
+
return;
|
|
25174
|
+
}
|
|
25175
|
+
console.warn("Slack notification skipped: No webhook or proxy endpoint configured");
|
|
25176
|
+
console.info("To fix this, either:");
|
|
25177
|
+
console.info("1. Set up a server-side proxy endpoint and configure slackProxyEndpoint in your dashboard config");
|
|
25178
|
+
console.info("2. For development only: use NEXT_PUBLIC_SLACK_WEBHOOK_URL (not recommended for production)");
|
|
25179
|
+
console.info("3. Configure the webhook URL directly in your dashboard config at runtime");
|
|
25180
|
+
} catch (error) {
|
|
25181
|
+
console.error("Failed to send Slack notification:", error);
|
|
25182
|
+
throw error;
|
|
25183
|
+
}
|
|
25184
|
+
}
|
|
25185
|
+
/**
|
|
25186
|
+
* Formats a support ticket into a Slack message format
|
|
25187
|
+
* This can be used by server-side implementations
|
|
25188
|
+
*/
|
|
25189
|
+
static formatSlackMessage(ticket) {
|
|
25190
|
+
const timestamp = new Date(ticket.timestamp).toLocaleString("en-US", {
|
|
25191
|
+
dateStyle: "medium",
|
|
25192
|
+
timeStyle: "short",
|
|
25193
|
+
timeZone: "UTC"
|
|
25194
|
+
});
|
|
25195
|
+
const maxDescriptionLength = 300;
|
|
25196
|
+
const description = ticket.description.length > maxDescriptionLength ? `${ticket.description.substring(0, maxDescriptionLength)}...` : ticket.description;
|
|
25197
|
+
const categoryConfig = {
|
|
25198
|
+
general: { emoji: "\u{1F4AC}", color: "#36a64f" },
|
|
25199
|
+
technical: { emoji: "\u{1F527}", color: "#ff6b6b" },
|
|
25200
|
+
feature: { emoji: "\u2728", color: "#4ecdc4" },
|
|
25201
|
+
billing: { emoji: "\u{1F4B3}", color: "#f7b731" }
|
|
25202
|
+
};
|
|
25203
|
+
const priorityConfig = {
|
|
25204
|
+
low: { emoji: "\u{1F7E2}", color: "#36a64f" },
|
|
25205
|
+
normal: { emoji: "\u{1F7E1}", color: "#ffc107" },
|
|
25206
|
+
high: { emoji: "\u{1F7E0}", color: "#ff8c00" },
|
|
25207
|
+
urgent: { emoji: "\u{1F534}", color: "#dc3545" }
|
|
25208
|
+
};
|
|
25209
|
+
const categoryInfo = categoryConfig[ticket.category] || categoryConfig.general;
|
|
25210
|
+
const priorityInfo = priorityConfig[ticket.priority] || priorityConfig.normal;
|
|
25211
|
+
const messageColor = ticket.priority === "high" || ticket.priority === "urgent" ? priorityInfo.color : categoryInfo.color;
|
|
25212
|
+
return {
|
|
25213
|
+
text: `New support ticket from ${ticket.email}`,
|
|
25214
|
+
blocks: [
|
|
25215
|
+
{
|
|
25216
|
+
type: "header",
|
|
25217
|
+
text: {
|
|
25218
|
+
type: "plain_text",
|
|
25219
|
+
text: "\u{1F3AB} New Support Ticket Submitted",
|
|
25220
|
+
emoji: true
|
|
25221
|
+
}
|
|
25222
|
+
},
|
|
25223
|
+
{
|
|
25224
|
+
type: "section",
|
|
25225
|
+
fields: [
|
|
25226
|
+
{
|
|
25227
|
+
type: "mrkdwn",
|
|
25228
|
+
text: `*From:*
|
|
25169
25229
|
${ticket.email}`
|
|
25170
|
-
|
|
25171
|
-
|
|
25172
|
-
|
|
25173
|
-
|
|
25230
|
+
},
|
|
25231
|
+
{
|
|
25232
|
+
type: "mrkdwn",
|
|
25233
|
+
text: `*Category:*
|
|
25174
25234
|
${categoryInfo.emoji} ${ticket.category.charAt(0).toUpperCase() + ticket.category.slice(1)}`
|
|
25175
|
-
|
|
25176
|
-
|
|
25177
|
-
|
|
25178
|
-
|
|
25235
|
+
},
|
|
25236
|
+
{
|
|
25237
|
+
type: "mrkdwn",
|
|
25238
|
+
text: `*Priority:*
|
|
25179
25239
|
${priorityInfo.emoji} ${ticket.priority.charAt(0).toUpperCase() + ticket.priority.slice(1)}`
|
|
25180
|
-
|
|
25181
|
-
|
|
25182
|
-
|
|
25183
|
-
|
|
25240
|
+
},
|
|
25241
|
+
{
|
|
25242
|
+
type: "mrkdwn",
|
|
25243
|
+
text: `*Subject:*
|
|
25184
25244
|
${ticket.subject}`
|
|
25185
|
-
|
|
25186
|
-
|
|
25187
|
-
type: "mrkdwn",
|
|
25188
|
-
text: `*Submitted:*
|
|
25189
|
-
${timestamp} UTC`
|
|
25190
|
-
}
|
|
25191
|
-
]
|
|
25192
|
-
},
|
|
25193
|
-
{
|
|
25194
|
-
type: "section",
|
|
25195
|
-
text: {
|
|
25245
|
+
},
|
|
25246
|
+
{
|
|
25196
25247
|
type: "mrkdwn",
|
|
25197
|
-
text: `*
|
|
25198
|
-
${
|
|
25248
|
+
text: `*Submitted:*
|
|
25249
|
+
${timestamp} UTC`
|
|
25199
25250
|
}
|
|
25200
|
-
|
|
25201
|
-
|
|
25202
|
-
|
|
25203
|
-
|
|
25204
|
-
{
|
|
25205
|
-
type: "
|
|
25206
|
-
|
|
25207
|
-
|
|
25208
|
-
type: "mrkdwn",
|
|
25209
|
-
text: `Category: \`${ticket.category}\` | Priority: ${priorityInfo.emoji} \`${ticket.priority.toUpperCase()}\` | ${ticket.priority === "urgent" ? "\u26A0\uFE0F Requires immediate attention" : "Standard processing"}`
|
|
25210
|
-
}
|
|
25211
|
-
]
|
|
25212
|
-
}
|
|
25213
|
-
],
|
|
25214
|
-
attachments: [
|
|
25215
|
-
{
|
|
25216
|
-
color: messageColor,
|
|
25217
|
-
fields: []
|
|
25251
|
+
]
|
|
25252
|
+
},
|
|
25253
|
+
{
|
|
25254
|
+
type: "section",
|
|
25255
|
+
text: {
|
|
25256
|
+
type: "mrkdwn",
|
|
25257
|
+
text: `*Description:*
|
|
25258
|
+
${description}`
|
|
25218
25259
|
}
|
|
25219
|
-
]
|
|
25220
|
-
};
|
|
25221
|
-
const response = await fetch(slackWebhookUrl, {
|
|
25222
|
-
method: "POST",
|
|
25223
|
-
headers: {
|
|
25224
|
-
"Content-Type": "application/json"
|
|
25225
25260
|
},
|
|
25226
|
-
|
|
25227
|
-
|
|
25228
|
-
|
|
25229
|
-
|
|
25230
|
-
|
|
25231
|
-
|
|
25232
|
-
|
|
25233
|
-
|
|
25234
|
-
|
|
25235
|
-
|
|
25236
|
-
|
|
25237
|
-
|
|
25261
|
+
{
|
|
25262
|
+
type: "divider"
|
|
25263
|
+
},
|
|
25264
|
+
{
|
|
25265
|
+
type: "context",
|
|
25266
|
+
elements: [
|
|
25267
|
+
{
|
|
25268
|
+
type: "mrkdwn",
|
|
25269
|
+
text: `Category: \`${ticket.category}\` | Priority: ${priorityInfo.emoji} \`${ticket.priority.toUpperCase()}\` | ${ticket.priority === "urgent" ? "\u26A0\uFE0F Requires immediate attention" : "Standard processing"}`
|
|
25270
|
+
}
|
|
25271
|
+
]
|
|
25272
|
+
}
|
|
25273
|
+
],
|
|
25274
|
+
attachments: [
|
|
25275
|
+
{
|
|
25276
|
+
color: messageColor,
|
|
25277
|
+
fields: []
|
|
25278
|
+
}
|
|
25279
|
+
]
|
|
25280
|
+
};
|
|
25238
25281
|
}
|
|
25239
25282
|
};
|
|
25240
25283
|
var HelpView = ({
|
|
@@ -26677,7 +26720,7 @@ var KPIsOverviewView = ({
|
|
|
26677
26720
|
}
|
|
26678
26721
|
),
|
|
26679
26722
|
/* @__PURE__ */ jsx("div", { className: "flex-1 flex justify-center", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
26680
|
-
/* @__PURE__ */ jsx("h1", { className: "text-3xl font-bold text-gray-800 tracking-tight", children: "
|
|
26723
|
+
/* @__PURE__ */ jsx("h1", { className: "text-3xl font-bold text-gray-800 tracking-tight", children: "Shop-floor overview" }),
|
|
26681
26724
|
/* @__PURE__ */ jsx("div", { className: "h-2 w-2 rounded-full bg-green-500 animate-pulse ring-2 ring-green-500/30 ring-offset-1" })
|
|
26682
26725
|
] }) })
|
|
26683
26726
|
] }) }) }),
|
|
@@ -26699,7 +26742,7 @@ var KPIsOverviewView = ({
|
|
|
26699
26742
|
}
|
|
26700
26743
|
),
|
|
26701
26744
|
/* @__PURE__ */ jsx("div", { className: "flex-1 flex justify-center", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
26702
|
-
/* @__PURE__ */ jsx("h1", { className: "text-3xl font-bold text-gray-800 tracking-tight", children: "
|
|
26745
|
+
/* @__PURE__ */ jsx("h1", { className: "text-3xl font-bold text-gray-800 tracking-tight", children: "Shop-floor overview" }),
|
|
26703
26746
|
/* @__PURE__ */ jsx("div", { className: "h-2 w-2 rounded-full bg-green-500 animate-pulse ring-2 ring-green-500/30 ring-offset-1" })
|
|
26704
26747
|
] }) })
|
|
26705
26748
|
] }) }) }),
|
|
@@ -26724,7 +26767,7 @@ var KPIsOverviewView = ({
|
|
|
26724
26767
|
}
|
|
26725
26768
|
),
|
|
26726
26769
|
/* @__PURE__ */ jsx("div", { className: "flex-1 flex justify-center", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
26727
|
-
/* @__PURE__ */ jsx("h1", { className: "text-3xl font-bold text-gray-800 tracking-tight", children: "
|
|
26770
|
+
/* @__PURE__ */ jsx("h1", { className: "text-3xl font-bold text-gray-800 tracking-tight", children: "Shop-floor overview" }),
|
|
26728
26771
|
/* @__PURE__ */ jsx("div", { className: "h-2 w-2 rounded-full bg-green-500 animate-pulse ring-2 ring-green-500/30 ring-offset-1" })
|
|
26729
26772
|
] }) })
|
|
26730
26773
|
] }),
|