@optifye/dashboard-core 4.2.1 → 4.2.2

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 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 directly to Slack webhook
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 directly to Slack webhook
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,9 @@ 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
- slackWebhookUrl: process.env.NEXT_PUBLIC_SLACK_WEBHOOK_URL || void 0
143
- // Slack webhook URL from environment
142
+ // Hard-coded Slack webhook so the Help page works out-of-the-box without any config
143
+ slackWebhookUrl: "https://hooks.slack.com/services/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",
144
+ slackProxyEndpoint: void 0
144
145
  };
145
146
  var DEFAULT_THEME_CONFIG = {
146
147
  // Sensible defaults for theme can be added here
@@ -23226,13 +23227,13 @@ var SideNavBar = React33.memo(({
23226
23227
  {
23227
23228
  onClick: handleKPIsClick,
23228
23229
  className: kpisButtonClasses,
23229
- "aria-label": "KPIs",
23230
+ "aria-label": "Lines",
23230
23231
  tabIndex: 0,
23231
23232
  role: "tab",
23232
23233
  "aria-selected": pathname === "/kpis" || pathname.startsWith("/kpis/"),
23233
23234
  children: [
23234
23235
  /* @__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: "KPIs" })
23236
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-medium leading-tight", children: "Lines" })
23236
23237
  ]
23237
23238
  }
23238
23239
  ),
@@ -25145,125 +25146,151 @@ var streamProxyConfig = {
25145
25146
  // src/lib/api/slackApi.ts
25146
25147
  var SlackAPI = class {
25147
25148
  /**
25148
- * Sends a support ticket notification directly to Slack webhook
25149
+ * Sends a support ticket notification to Slack.
25150
+ * 1. If slackWebhookUrl is configured, send directly (preferred)
25151
+ * 2. Otherwise, if slackProxyEndpoint is configured, send to the proxy
25152
+ * If neither is configured, logs a warning.
25149
25153
  */
25150
25154
  static async sendSupportTicketNotification(ticket) {
25151
25155
  try {
25152
25156
  const config = _getDashboardConfigInstance();
25153
25157
  const endpointsConfig = config.endpoints ?? DEFAULT_ENDPOINTS_CONFIG;
25154
- const slackWebhookUrl = endpointsConfig.slackWebhookUrl;
25155
- if (!slackWebhookUrl) {
25156
- console.log("Slack webhook URL not configured, skipping notification");
25158
+ const { slackWebhookUrl, slackProxyEndpoint } = endpointsConfig;
25159
+ if (slackWebhookUrl) {
25160
+ const slackMessage = this.formatSlackMessage(ticket);
25161
+ const response = await fetch(slackWebhookUrl, {
25162
+ method: "POST",
25163
+ headers: {
25164
+ // Use form-urlencoded + simple headers to avoid CORS preflight
25165
+ "Content-Type": "application/x-www-form-urlencoded"
25166
+ },
25167
+ body: `payload=${encodeURIComponent(JSON.stringify(slackMessage))}`
25168
+ });
25169
+ if (!response.ok) {
25170
+ const errorText = await response.text();
25171
+ console.error("Slack webhook error:", errorText);
25172
+ throw new Error(`Slack webhook failed with status: ${response.status}`);
25173
+ }
25174
+ console.log("Support ticket notification sent to Slack successfully via webhook");
25157
25175
  return;
25158
25176
  }
25159
- const timestamp = new Date(ticket.timestamp).toLocaleString("en-US", {
25160
- dateStyle: "medium",
25161
- timeStyle: "short",
25162
- timeZone: "UTC"
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
- }
25177
+ if (slackProxyEndpoint) {
25178
+ const response = await fetch(slackProxyEndpoint, {
25179
+ method: "POST",
25180
+ headers: {
25181
+ "Content-Type": "application/json"
25191
25182
  },
25192
- {
25193
- type: "section",
25194
- fields: [
25195
- {
25196
- type: "mrkdwn",
25197
- text: `*From:*
25183
+ body: JSON.stringify(ticket)
25184
+ });
25185
+ if (!response.ok) {
25186
+ const errorData = await response.json().catch(() => ({ message: "Unknown error" }));
25187
+ throw new Error(errorData.message || `Proxy failed with status: ${response.status}`);
25188
+ }
25189
+ console.log("Support ticket notification sent to Slack successfully via proxy");
25190
+ return;
25191
+ }
25192
+ console.warn("Slack notification skipped: No webhook or proxy endpoint configured");
25193
+ } catch (error) {
25194
+ console.error("Failed to send Slack notification:", error);
25195
+ throw error;
25196
+ }
25197
+ }
25198
+ /**
25199
+ * Formats a support ticket into a Slack message format
25200
+ * This can be used by server-side implementations
25201
+ */
25202
+ static formatSlackMessage(ticket) {
25203
+ const timestamp = new Date(ticket.timestamp).toLocaleString("en-US", {
25204
+ dateStyle: "medium",
25205
+ timeStyle: "short",
25206
+ timeZone: "UTC"
25207
+ });
25208
+ const maxDescriptionLength = 300;
25209
+ const description = ticket.description.length > maxDescriptionLength ? `${ticket.description.substring(0, maxDescriptionLength)}...` : ticket.description;
25210
+ const categoryConfig = {
25211
+ general: { emoji: "\u{1F4AC}", color: "#36a64f" },
25212
+ technical: { emoji: "\u{1F527}", color: "#ff6b6b" },
25213
+ feature: { emoji: "\u2728", color: "#4ecdc4" },
25214
+ billing: { emoji: "\u{1F4B3}", color: "#f7b731" }
25215
+ };
25216
+ const priorityConfig = {
25217
+ low: { emoji: "\u{1F7E2}", color: "#36a64f" },
25218
+ normal: { emoji: "\u{1F7E1}", color: "#ffc107" },
25219
+ high: { emoji: "\u{1F7E0}", color: "#ff8c00" },
25220
+ urgent: { emoji: "\u{1F534}", color: "#dc3545" }
25221
+ };
25222
+ const categoryInfo = categoryConfig[ticket.category] || categoryConfig.general;
25223
+ const priorityInfo = priorityConfig[ticket.priority] || priorityConfig.normal;
25224
+ const messageColor = ticket.priority === "high" || ticket.priority === "urgent" ? priorityInfo.color : categoryInfo.color;
25225
+ return {
25226
+ text: `New support ticket from ${ticket.email}`,
25227
+ blocks: [
25228
+ {
25229
+ type: "header",
25230
+ text: {
25231
+ type: "plain_text",
25232
+ text: "\u{1F3AB} New Support Ticket Submitted",
25233
+ emoji: true
25234
+ }
25235
+ },
25236
+ {
25237
+ type: "section",
25238
+ fields: [
25239
+ {
25240
+ type: "mrkdwn",
25241
+ text: `*From:*
25198
25242
  ${ticket.email}`
25199
- },
25200
- {
25201
- type: "mrkdwn",
25202
- text: `*Category:*
25243
+ },
25244
+ {
25245
+ type: "mrkdwn",
25246
+ text: `*Category:*
25203
25247
  ${categoryInfo.emoji} ${ticket.category.charAt(0).toUpperCase() + ticket.category.slice(1)}`
25204
- },
25205
- {
25206
- type: "mrkdwn",
25207
- text: `*Priority:*
25248
+ },
25249
+ {
25250
+ type: "mrkdwn",
25251
+ text: `*Priority:*
25208
25252
  ${priorityInfo.emoji} ${ticket.priority.charAt(0).toUpperCase() + ticket.priority.slice(1)}`
25209
- },
25210
- {
25211
- type: "mrkdwn",
25212
- text: `*Subject:*
25253
+ },
25254
+ {
25255
+ type: "mrkdwn",
25256
+ text: `*Subject:*
25213
25257
  ${ticket.subject}`
25214
- },
25215
- {
25216
- type: "mrkdwn",
25217
- text: `*Submitted:*
25218
- ${timestamp} UTC`
25219
- }
25220
- ]
25221
- },
25222
- {
25223
- type: "section",
25224
- text: {
25258
+ },
25259
+ {
25225
25260
  type: "mrkdwn",
25226
- text: `*Description:*
25227
- ${description}`
25261
+ text: `*Submitted:*
25262
+ ${timestamp} UTC`
25228
25263
  }
25229
- },
25230
- {
25231
- type: "divider"
25232
- },
25233
- {
25234
- type: "context",
25235
- elements: [
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: []
25264
+ ]
25265
+ },
25266
+ {
25267
+ type: "section",
25268
+ text: {
25269
+ type: "mrkdwn",
25270
+ text: `*Description:*
25271
+ ${description}`
25247
25272
  }
25248
- ]
25249
- };
25250
- const response = await fetch(slackWebhookUrl, {
25251
- method: "POST",
25252
- headers: {
25253
- "Content-Type": "application/json"
25254
25273
  },
25255
- body: JSON.stringify(slackMessage)
25256
- });
25257
- if (!response.ok) {
25258
- const errorText = await response.text();
25259
- console.error("Slack webhook error:", errorText);
25260
- throw new Error(`Slack webhook failed with status: ${response.status}`);
25261
- }
25262
- console.log("Support ticket notification sent to Slack successfully");
25263
- } catch (error) {
25264
- console.error("Failed to send Slack notification:", error);
25265
- throw error;
25266
- }
25274
+ {
25275
+ type: "divider"
25276
+ },
25277
+ {
25278
+ type: "context",
25279
+ elements: [
25280
+ {
25281
+ type: "mrkdwn",
25282
+ text: `Category: \`${ticket.category}\` | Priority: ${priorityInfo.emoji} \`${ticket.priority.toUpperCase()}\` | ${ticket.priority === "urgent" ? "\u26A0\uFE0F Requires immediate attention" : "Standard processing"}`
25283
+ }
25284
+ ]
25285
+ }
25286
+ ],
25287
+ attachments: [
25288
+ {
25289
+ color: messageColor,
25290
+ fields: []
25291
+ }
25292
+ ]
25293
+ };
25267
25294
  }
25268
25295
  };
25269
25296
  var HelpView = ({
@@ -26706,7 +26733,7 @@ var KPIsOverviewView = ({
26706
26733
  }
26707
26734
  ),
26708
26735
  /* @__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: "Production Line KPIs" }),
26736
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl font-bold text-gray-800 tracking-tight", children: "Shop-floor overview" }),
26710
26737
  /* @__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
26738
  ] }) })
26712
26739
  ] }) }) }),
@@ -26728,7 +26755,7 @@ var KPIsOverviewView = ({
26728
26755
  }
26729
26756
  ),
26730
26757
  /* @__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: "Production Line KPIs" }),
26758
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl font-bold text-gray-800 tracking-tight", children: "Shop-floor overview" }),
26732
26759
  /* @__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
26760
  ] }) })
26734
26761
  ] }) }) }),
@@ -26753,7 +26780,7 @@ var KPIsOverviewView = ({
26753
26780
  }
26754
26781
  ),
26755
26782
  /* @__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: "Production Line KPIs" }),
26783
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl font-bold text-gray-800 tracking-tight", children: "Shop-floor overview" }),
26757
26784
  /* @__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
26785
  ] }) })
26759
26786
  ] }),
package/dist/index.mjs CHANGED
@@ -110,8 +110,9 @@ 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
- slackWebhookUrl: process.env.NEXT_PUBLIC_SLACK_WEBHOOK_URL || void 0
114
- // Slack webhook URL from environment
113
+ // Hard-coded Slack webhook so the Help page works out-of-the-box without any config
114
+ slackWebhookUrl: "https://hooks.slack.com/services/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",
115
+ slackProxyEndpoint: void 0
115
116
  };
116
117
  var DEFAULT_THEME_CONFIG = {
117
118
  // Sensible defaults for theme can be added here
@@ -23197,13 +23198,13 @@ var SideNavBar = memo(({
23197
23198
  {
23198
23199
  onClick: handleKPIsClick,
23199
23200
  className: kpisButtonClasses,
23200
- "aria-label": "KPIs",
23201
+ "aria-label": "Lines",
23201
23202
  tabIndex: 0,
23202
23203
  role: "tab",
23203
23204
  "aria-selected": pathname === "/kpis" || pathname.startsWith("/kpis/"),
23204
23205
  children: [
23205
23206
  /* @__PURE__ */ jsx(ChartBarIcon, { className: "w-5 h-5 mb-1" }),
23206
- /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium leading-tight", children: "KPIs" })
23207
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium leading-tight", children: "Lines" })
23207
23208
  ]
23208
23209
  }
23209
23210
  ),
@@ -25116,125 +25117,151 @@ var streamProxyConfig = {
25116
25117
  // src/lib/api/slackApi.ts
25117
25118
  var SlackAPI = class {
25118
25119
  /**
25119
- * Sends a support ticket notification directly to Slack webhook
25120
+ * Sends a support ticket notification to Slack.
25121
+ * 1. If slackWebhookUrl is configured, send directly (preferred)
25122
+ * 2. Otherwise, if slackProxyEndpoint is configured, send to the proxy
25123
+ * If neither is configured, logs a warning.
25120
25124
  */
25121
25125
  static async sendSupportTicketNotification(ticket) {
25122
25126
  try {
25123
25127
  const config = _getDashboardConfigInstance();
25124
25128
  const endpointsConfig = config.endpoints ?? DEFAULT_ENDPOINTS_CONFIG;
25125
- const slackWebhookUrl = endpointsConfig.slackWebhookUrl;
25126
- if (!slackWebhookUrl) {
25127
- console.log("Slack webhook URL not configured, skipping notification");
25129
+ const { slackWebhookUrl, slackProxyEndpoint } = endpointsConfig;
25130
+ if (slackWebhookUrl) {
25131
+ const slackMessage = this.formatSlackMessage(ticket);
25132
+ const response = await fetch(slackWebhookUrl, {
25133
+ method: "POST",
25134
+ headers: {
25135
+ // Use form-urlencoded + simple headers to avoid CORS preflight
25136
+ "Content-Type": "application/x-www-form-urlencoded"
25137
+ },
25138
+ body: `payload=${encodeURIComponent(JSON.stringify(slackMessage))}`
25139
+ });
25140
+ if (!response.ok) {
25141
+ const errorText = await response.text();
25142
+ console.error("Slack webhook error:", errorText);
25143
+ throw new Error(`Slack webhook failed with status: ${response.status}`);
25144
+ }
25145
+ console.log("Support ticket notification sent to Slack successfully via webhook");
25128
25146
  return;
25129
25147
  }
25130
- const timestamp = new Date(ticket.timestamp).toLocaleString("en-US", {
25131
- dateStyle: "medium",
25132
- timeStyle: "short",
25133
- timeZone: "UTC"
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
- }
25148
+ if (slackProxyEndpoint) {
25149
+ const response = await fetch(slackProxyEndpoint, {
25150
+ method: "POST",
25151
+ headers: {
25152
+ "Content-Type": "application/json"
25162
25153
  },
25163
- {
25164
- type: "section",
25165
- fields: [
25166
- {
25167
- type: "mrkdwn",
25168
- text: `*From:*
25154
+ body: JSON.stringify(ticket)
25155
+ });
25156
+ if (!response.ok) {
25157
+ const errorData = await response.json().catch(() => ({ message: "Unknown error" }));
25158
+ throw new Error(errorData.message || `Proxy failed with status: ${response.status}`);
25159
+ }
25160
+ console.log("Support ticket notification sent to Slack successfully via proxy");
25161
+ return;
25162
+ }
25163
+ console.warn("Slack notification skipped: No webhook or proxy endpoint configured");
25164
+ } catch (error) {
25165
+ console.error("Failed to send Slack notification:", error);
25166
+ throw error;
25167
+ }
25168
+ }
25169
+ /**
25170
+ * Formats a support ticket into a Slack message format
25171
+ * This can be used by server-side implementations
25172
+ */
25173
+ static formatSlackMessage(ticket) {
25174
+ const timestamp = new Date(ticket.timestamp).toLocaleString("en-US", {
25175
+ dateStyle: "medium",
25176
+ timeStyle: "short",
25177
+ timeZone: "UTC"
25178
+ });
25179
+ const maxDescriptionLength = 300;
25180
+ const description = ticket.description.length > maxDescriptionLength ? `${ticket.description.substring(0, maxDescriptionLength)}...` : ticket.description;
25181
+ const categoryConfig = {
25182
+ general: { emoji: "\u{1F4AC}", color: "#36a64f" },
25183
+ technical: { emoji: "\u{1F527}", color: "#ff6b6b" },
25184
+ feature: { emoji: "\u2728", color: "#4ecdc4" },
25185
+ billing: { emoji: "\u{1F4B3}", color: "#f7b731" }
25186
+ };
25187
+ const priorityConfig = {
25188
+ low: { emoji: "\u{1F7E2}", color: "#36a64f" },
25189
+ normal: { emoji: "\u{1F7E1}", color: "#ffc107" },
25190
+ high: { emoji: "\u{1F7E0}", color: "#ff8c00" },
25191
+ urgent: { emoji: "\u{1F534}", color: "#dc3545" }
25192
+ };
25193
+ const categoryInfo = categoryConfig[ticket.category] || categoryConfig.general;
25194
+ const priorityInfo = priorityConfig[ticket.priority] || priorityConfig.normal;
25195
+ const messageColor = ticket.priority === "high" || ticket.priority === "urgent" ? priorityInfo.color : categoryInfo.color;
25196
+ return {
25197
+ text: `New support ticket from ${ticket.email}`,
25198
+ blocks: [
25199
+ {
25200
+ type: "header",
25201
+ text: {
25202
+ type: "plain_text",
25203
+ text: "\u{1F3AB} New Support Ticket Submitted",
25204
+ emoji: true
25205
+ }
25206
+ },
25207
+ {
25208
+ type: "section",
25209
+ fields: [
25210
+ {
25211
+ type: "mrkdwn",
25212
+ text: `*From:*
25169
25213
  ${ticket.email}`
25170
- },
25171
- {
25172
- type: "mrkdwn",
25173
- text: `*Category:*
25214
+ },
25215
+ {
25216
+ type: "mrkdwn",
25217
+ text: `*Category:*
25174
25218
  ${categoryInfo.emoji} ${ticket.category.charAt(0).toUpperCase() + ticket.category.slice(1)}`
25175
- },
25176
- {
25177
- type: "mrkdwn",
25178
- text: `*Priority:*
25219
+ },
25220
+ {
25221
+ type: "mrkdwn",
25222
+ text: `*Priority:*
25179
25223
  ${priorityInfo.emoji} ${ticket.priority.charAt(0).toUpperCase() + ticket.priority.slice(1)}`
25180
- },
25181
- {
25182
- type: "mrkdwn",
25183
- text: `*Subject:*
25224
+ },
25225
+ {
25226
+ type: "mrkdwn",
25227
+ text: `*Subject:*
25184
25228
  ${ticket.subject}`
25185
- },
25186
- {
25187
- type: "mrkdwn",
25188
- text: `*Submitted:*
25189
- ${timestamp} UTC`
25190
- }
25191
- ]
25192
- },
25193
- {
25194
- type: "section",
25195
- text: {
25229
+ },
25230
+ {
25196
25231
  type: "mrkdwn",
25197
- text: `*Description:*
25198
- ${description}`
25232
+ text: `*Submitted:*
25233
+ ${timestamp} UTC`
25199
25234
  }
25200
- },
25201
- {
25202
- type: "divider"
25203
- },
25204
- {
25205
- type: "context",
25206
- elements: [
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: []
25235
+ ]
25236
+ },
25237
+ {
25238
+ type: "section",
25239
+ text: {
25240
+ type: "mrkdwn",
25241
+ text: `*Description:*
25242
+ ${description}`
25218
25243
  }
25219
- ]
25220
- };
25221
- const response = await fetch(slackWebhookUrl, {
25222
- method: "POST",
25223
- headers: {
25224
- "Content-Type": "application/json"
25225
25244
  },
25226
- body: JSON.stringify(slackMessage)
25227
- });
25228
- if (!response.ok) {
25229
- const errorText = await response.text();
25230
- console.error("Slack webhook error:", errorText);
25231
- throw new Error(`Slack webhook failed with status: ${response.status}`);
25232
- }
25233
- console.log("Support ticket notification sent to Slack successfully");
25234
- } catch (error) {
25235
- console.error("Failed to send Slack notification:", error);
25236
- throw error;
25237
- }
25245
+ {
25246
+ type: "divider"
25247
+ },
25248
+ {
25249
+ type: "context",
25250
+ elements: [
25251
+ {
25252
+ type: "mrkdwn",
25253
+ text: `Category: \`${ticket.category}\` | Priority: ${priorityInfo.emoji} \`${ticket.priority.toUpperCase()}\` | ${ticket.priority === "urgent" ? "\u26A0\uFE0F Requires immediate attention" : "Standard processing"}`
25254
+ }
25255
+ ]
25256
+ }
25257
+ ],
25258
+ attachments: [
25259
+ {
25260
+ color: messageColor,
25261
+ fields: []
25262
+ }
25263
+ ]
25264
+ };
25238
25265
  }
25239
25266
  };
25240
25267
  var HelpView = ({
@@ -26677,7 +26704,7 @@ var KPIsOverviewView = ({
26677
26704
  }
26678
26705
  ),
26679
26706
  /* @__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: "Production Line KPIs" }),
26707
+ /* @__PURE__ */ jsx("h1", { className: "text-3xl font-bold text-gray-800 tracking-tight", children: "Shop-floor overview" }),
26681
26708
  /* @__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
26709
  ] }) })
26683
26710
  ] }) }) }),
@@ -26699,7 +26726,7 @@ var KPIsOverviewView = ({
26699
26726
  }
26700
26727
  ),
26701
26728
  /* @__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: "Production Line KPIs" }),
26729
+ /* @__PURE__ */ jsx("h1", { className: "text-3xl font-bold text-gray-800 tracking-tight", children: "Shop-floor overview" }),
26703
26730
  /* @__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
26731
  ] }) })
26705
26732
  ] }) }) }),
@@ -26724,7 +26751,7 @@ var KPIsOverviewView = ({
26724
26751
  }
26725
26752
  ),
26726
26753
  /* @__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: "Production Line KPIs" }),
26754
+ /* @__PURE__ */ jsx("h1", { className: "text-3xl font-bold text-gray-800 tracking-tight", children: "Shop-floor overview" }),
26728
26755
  /* @__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
26756
  ] }) })
26730
26757
  ] }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optifye/dashboard-core",
3
- "version": "4.2.1",
3
+ "version": "4.2.2",
4
4
  "description": "Reusable UI & logic for Optifye dashboard",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",