dp-widgets-framework 1.6.9 → 1.7.1

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.esm.js CHANGED
@@ -7,7 +7,7 @@ import * as LabelPrimitive from '@radix-ui/react-label';
7
7
  import { cva } from 'class-variance-authority';
8
8
  import * as SwitchPrimitives from '@radix-ui/react-switch';
9
9
  import * as SelectPrimitive from '@radix-ui/react-select';
10
- import { ChevronDown, ChevronUp, Check, AlertCircle, MoveUp, MoveDown, Trash2, Plus, Bot, Type, Layout, LayoutGrid, BarChart as BarChart$1, Filter, Search, ArrowUp, ArrowDown, ChevronRight, RefreshCw, Send, Loader2, X, AlignVerticalSpaceAround, LineChart as LineChart$1, PieChart as PieChart$1, Table, FileText, SlidersHorizontal, GripHorizontal, Edit, MessageCircleX, Edit2 } from 'lucide-react';
10
+ import { ChevronDown, ChevronUp, Check, AlertCircle, MoveUp, MoveDown, Trash2, Plus, Bot, Type, Layout, LayoutGrid, BarChart as BarChart$1, Filter, Search, ArrowUp, ArrowDown, ChevronRight, RefreshCw, Loader2, Download, Send, X, AlignVerticalSpaceAround, LineChart as LineChart$1, PieChart as PieChart$1, Table, FileText, SlidersHorizontal, GripHorizontal, Edit, MessageCircleX, ChevronLeft, Maximize2, Grid3X3, Edit2 } from 'lucide-react';
11
11
  import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
12
12
  import { Slot, createSlot, createSlottable } from '@radix-ui/react-slot';
13
13
  import { debounce as debounce$1 } from 'lodash';
@@ -17,6 +17,7 @@ import { v4 } from 'uuid';
17
17
  import * as ReactDOM from 'react-dom';
18
18
  import { TextMessage, Role } from '@copilotkit/runtime-client-gql';
19
19
  import { CopilotKit, useCopilotContext, useCoAgent, useCopilotChat } from '@copilotkit/react-core';
20
+ import jsPDF from 'jspdf';
20
21
  import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
21
22
  import * as DialogPrimitive from '@radix-ui/react-dialog';
22
23
  import { format } from 'date-fns';
@@ -44409,6 +44410,175 @@ function SpacerWidget({ widget }) {
44409
44410
  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full", style: styles });
44410
44411
  }
44411
44412
 
44413
+ const WISENBAKER_PRIMARY = "#8f002d";
44414
+ const WISENBAKER_GRAY = "#475569";
44415
+ function parseMarkdown(text) {
44416
+ const segments = [];
44417
+ const regex = /\*\*(.+?)\*\*/g;
44418
+ let lastIndex = 0;
44419
+ let match;
44420
+ while ((match = regex.exec(text)) !== null) {
44421
+ if (match.index > lastIndex) {
44422
+ segments.push({ text: text.slice(lastIndex, match.index), bold: false });
44423
+ }
44424
+ segments.push({ text: match[1], bold: true });
44425
+ lastIndex = regex.lastIndex;
44426
+ }
44427
+ if (lastIndex < text.length) {
44428
+ segments.push({ text: text.slice(lastIndex), bold: false });
44429
+ }
44430
+ return segments.length > 0 ? segments : [{ text, bold: false }];
44431
+ }
44432
+ function formatLine(line) {
44433
+ const bulletMatch = line.match(/^(\s*)[\*\-•]\s+(.*)$/);
44434
+ if (bulletMatch) {
44435
+ const indent = bulletMatch[1].length;
44436
+ return { text: bulletMatch[2], isBullet: true, indent };
44437
+ }
44438
+ const numberedMatch = line.match(/^(\s*)\d+\.\s+(.*)$/);
44439
+ if (numberedMatch) {
44440
+ const indent = numberedMatch[1].length;
44441
+ return { text: numberedMatch[2], isBullet: true, indent };
44442
+ }
44443
+ return { text: line, isBullet: false, indent: 0 };
44444
+ }
44445
+ function generateExportFilename(widgetId) {
44446
+ const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
44447
+ return `NSight_Chat_Export_${widgetId}_${date}.pdf`;
44448
+ }
44449
+ function exportChatToPDF(options) {
44450
+ const { widgetId, messages, dashboardName } = options;
44451
+ const doc = new jsPDF();
44452
+ let yPosition = 25;
44453
+ const pageHeight = doc.internal.pageSize.height;
44454
+ const pageWidth = doc.internal.pageSize.width;
44455
+ const margin = 20;
44456
+ const contentWidth = pageWidth - margin * 2;
44457
+ const maxWidth = contentWidth - 10;
44458
+ const checkPageBreak = (requiredSpace = 20) => {
44459
+ if (yPosition > pageHeight - requiredSpace) {
44460
+ doc.addPage();
44461
+ yPosition = 25;
44462
+ }
44463
+ };
44464
+ const renderFormattedText = (text, x, maxW, baseIndent = 0) => {
44465
+ const lines = text.split("\n");
44466
+ lines.forEach((line) => {
44467
+ const { text: lineText, isBullet, indent } = formatLine(line);
44468
+ const lineIndent = x + baseIndent + indent * 2;
44469
+ if (isBullet) {
44470
+ checkPageBreak(10);
44471
+ doc.setFont("helvetica", "normal");
44472
+ doc.text("\u2022", lineIndent, yPosition);
44473
+ }
44474
+ const textX = isBullet ? lineIndent + 5 : lineIndent;
44475
+ const textMaxWidth = maxW - (textX - x);
44476
+ const segments = parseMarkdown(lineText);
44477
+ let currentLine = "";
44478
+ let currentX = textX;
44479
+ segments.forEach((segment) => {
44480
+ doc.setFont("helvetica", segment.bold ? "bold" : "normal");
44481
+ const words = segment.text.split(" ");
44482
+ words.forEach((word, wordIndex) => {
44483
+ const testLine = currentLine + (currentLine ? " " : "") + word;
44484
+ const testWidth = doc.getTextWidth(testLine);
44485
+ if (testWidth > textMaxWidth && currentLine) {
44486
+ checkPageBreak(10);
44487
+ doc.text(currentLine, currentX, yPosition);
44488
+ yPosition += 5;
44489
+ currentLine = word;
44490
+ currentX = isBullet ? lineIndent + 5 : x + baseIndent;
44491
+ } else {
44492
+ currentLine = testLine;
44493
+ }
44494
+ });
44495
+ });
44496
+ if (currentLine) {
44497
+ checkPageBreak(10);
44498
+ const lastSegment = segments[segments.length - 1];
44499
+ doc.setFont("helvetica", (lastSegment == null ? void 0 : lastSegment.bold) ? "bold" : "normal");
44500
+ doc.text(currentLine, currentX, yPosition);
44501
+ yPosition += 5;
44502
+ }
44503
+ });
44504
+ };
44505
+ doc.setFontSize(18);
44506
+ doc.setFont("helvetica", "bold");
44507
+ doc.setTextColor(WISENBAKER_PRIMARY);
44508
+ doc.text("Wisenbaker AI Analytics", margin, yPosition);
44509
+ yPosition += 8;
44510
+ doc.setFontSize(11);
44511
+ doc.setFont("helvetica", "normal");
44512
+ doc.setTextColor(WISENBAKER_GRAY);
44513
+ doc.text("Chat Export", margin, yPosition);
44514
+ yPosition += 12;
44515
+ doc.setFontSize(9);
44516
+ doc.setTextColor("#666666");
44517
+ if (dashboardName) {
44518
+ doc.setFont("helvetica", "bold");
44519
+ doc.text("Dashboard:", margin, yPosition);
44520
+ doc.setFont("helvetica", "normal");
44521
+ doc.text(dashboardName, margin + 38, yPosition);
44522
+ yPosition += 5;
44523
+ }
44524
+ doc.setFont("helvetica", "bold");
44525
+ doc.text("Exported:", margin, yPosition);
44526
+ doc.setFont("helvetica", "normal");
44527
+ doc.text((/* @__PURE__ */ new Date()).toLocaleString(), margin + 38, yPosition);
44528
+ doc.setFont("helvetica", "bold");
44529
+ doc.text("Messages:", margin + 100, yPosition);
44530
+ doc.setFont("helvetica", "normal");
44531
+ doc.text(String(messages.length), margin + 130, yPosition);
44532
+ yPosition += 12;
44533
+ doc.setDrawColor(200, 200, 200);
44534
+ doc.setLineWidth(0.5);
44535
+ doc.line(margin, yPosition, pageWidth - margin, yPosition);
44536
+ yPosition += 8;
44537
+ messages.forEach((message, index) => {
44538
+ checkPageBreak(30);
44539
+ const isUser = message.role === "user";
44540
+ const roleLabel = isUser ? "You" : "AI Assistant";
44541
+ const timestamp = message.createdAt ? new Date(message.createdAt).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) : "";
44542
+ const bgColor = isUser ? [143, 0, 45] : [241, 243, 245];
44543
+ doc.setFillColor(bgColor[0], bgColor[1], bgColor[2]);
44544
+ doc.setFontSize(9);
44545
+ doc.setFont("helvetica", "bold");
44546
+ doc.setTextColor(isUser ? WISENBAKER_PRIMARY : WISENBAKER_GRAY);
44547
+ doc.text(roleLabel, margin, yPosition);
44548
+ if (timestamp) {
44549
+ doc.setFont("helvetica", "normal");
44550
+ doc.setTextColor("#888888");
44551
+ doc.text(timestamp, margin + doc.getTextWidth(roleLabel) + 5, yPosition);
44552
+ }
44553
+ yPosition += 6;
44554
+ doc.setFontSize(10);
44555
+ doc.setTextColor("#333333");
44556
+ doc.setFont("helvetica", "normal");
44557
+ renderFormattedText(message.content, margin + 5, maxWidth - 5, 0);
44558
+ yPosition += 8;
44559
+ if (index < messages.length - 1) {
44560
+ doc.setDrawColor(230, 230, 230);
44561
+ doc.setLineWidth(0.3);
44562
+ doc.line(margin + 10, yPosition - 4, pageWidth - margin - 10, yPosition - 4);
44563
+ }
44564
+ });
44565
+ const totalPages = doc.getNumberOfPages();
44566
+ for (let i = 1; i <= totalPages; i++) {
44567
+ doc.setPage(i);
44568
+ doc.setFontSize(8);
44569
+ doc.setTextColor("#999999");
44570
+ doc.setFont("helvetica", "normal");
44571
+ doc.text(
44572
+ `Wisenbaker AI Analytics \u2022 Page ${i} of ${totalPages}`,
44573
+ pageWidth / 2,
44574
+ pageHeight - 10,
44575
+ { align: "center" }
44576
+ );
44577
+ }
44578
+ const filename = generateExportFilename(widgetId);
44579
+ doc.save(filename);
44580
+ }
44581
+
44412
44582
  var __defProp$b = Object.defineProperty;
44413
44583
  var __defProps$9 = Object.defineProperties;
44414
44584
  var __getOwnPropDescs$9 = Object.getOwnPropertyDescriptors;
@@ -44457,7 +44627,8 @@ function CopilotKitChatbot({
44457
44627
  styles,
44458
44628
  onResetReady,
44459
44629
  widgetIds,
44460
- datasetId
44630
+ datasetId,
44631
+ dashboardName
44461
44632
  }) {
44462
44633
  var _a, _b, _c, _d;
44463
44634
  const { threadId, setThreadId } = useCopilotContext();
@@ -44474,7 +44645,24 @@ function CopilotKitChatbot({
44474
44645
  const { reset, visibleMessages, appendMessage, isLoading } = useCopilotChat();
44475
44646
  const [inputValue, setInputValue] = useState("");
44476
44647
  const [chatMessages, setChatMessages] = useState([]);
44648
+ const [isExporting, setIsExporting] = useState(false);
44477
44649
  const messagesEndRef = React__default.useRef(null);
44650
+ const handleExport = async () => {
44651
+ if (chatMessages.length === 0) return;
44652
+ setIsExporting(true);
44653
+ try {
44654
+ exportChatToPDF({
44655
+ widgetId: widget.id,
44656
+ widgetTitle: widget.title,
44657
+ messages: chatMessages,
44658
+ dashboardName
44659
+ });
44660
+ } catch (error) {
44661
+ console.error("Export failed:", error);
44662
+ } finally {
44663
+ setIsExporting(false);
44664
+ }
44665
+ };
44478
44666
  const scrollToBottom = () => {
44479
44667
  var _a2;
44480
44668
  const el = messagesEndRef.current;
@@ -44549,6 +44737,25 @@ function CopilotKitChatbot({
44549
44737
  /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-sm font-medium", children: widget.title })
44550
44738
  ] }) }),
44551
44739
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 h-full flex flex-col", children: [
44740
+ chatMessages.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-start px-4 py-2 border-b border-gray-100", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
44741
+ "button",
44742
+ {
44743
+ onClick: handleExport,
44744
+ disabled: isExporting,
44745
+ className: cn(
44746
+ "flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md",
44747
+ "bg-gray-100 border border-gray-200",
44748
+ "text-gray-700 hover:bg-gray-200 hover:border-gray-300",
44749
+ "disabled:opacity-50 disabled:cursor-not-allowed",
44750
+ "transition-colors duration-150"
44751
+ ),
44752
+ title: "Export conversation as PDF",
44753
+ children: [
44754
+ isExporting ? /* @__PURE__ */ jsxRuntimeExports.jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Download, { className: "h-3.5 w-3.5" }),
44755
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Export PDF" })
44756
+ ]
44757
+ }
44758
+ ) }),
44552
44759
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-y-auto p-4 space-y-4", children: chatMessages.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-center text-gray-500 text-xs mt-8", children: ((_b = widget.config) == null ? void 0 : _b.copilotInitialMessage) || ((_c = widget.config) == null ? void 0 : _c.placeholder) || "How can I help you today?" }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
44553
44760
  chatMessages.map((message) => /* @__PURE__ */ jsxRuntimeExports.jsx(
44554
44761
  "div",
@@ -44624,7 +44831,8 @@ function ChatbotWidget({
44624
44831
  widgetBackendUrl,
44625
44832
  onResetReady,
44626
44833
  widgetIds,
44627
- datasetId
44834
+ datasetId,
44835
+ dashboardName
44628
44836
  }) {
44629
44837
  var _a, _b;
44630
44838
  const styles = getStyleValues$4((_a = widget.config) == null ? void 0 : _a.styles);
@@ -44648,7 +44856,8 @@ function ChatbotWidget({
44648
44856
  styles,
44649
44857
  onResetReady,
44650
44858
  widgetIds,
44651
- datasetId
44859
+ datasetId,
44860
+ dashboardName
44652
44861
  }
44653
44862
  )
44654
44863
  }
@@ -47165,7 +47374,8 @@ function WidgetRenderer({
47165
47374
  datasetId,
47166
47375
  pageId,
47167
47376
  onApplyFilters,
47168
- isEditing = false
47377
+ isEditing = false,
47378
+ dashboardName
47169
47379
  }) {
47170
47380
  const handleConfigUpdate = (config) => {
47171
47381
  if (onConfigUpdate) {
@@ -47197,7 +47407,7 @@ function WidgetRenderer({
47197
47407
  }
47198
47408
  );
47199
47409
  case "chatbot":
47200
- return /* @__PURE__ */ jsxRuntimeExports.jsx(ChatbotWidget, { widget, showHeader: false, widgetBackendUrl, onResetReady, widgetIds, datasetId });
47410
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(ChatbotWidget, { widget, showHeader: false, widgetBackendUrl, onResetReady, widgetIds, datasetId, dashboardName });
47201
47411
  case "filters":
47202
47412
  return /* @__PURE__ */ jsxRuntimeExports.jsx(
47203
47413
  FiltersWidget,
@@ -48077,7 +48287,8 @@ function WidgetDashboard({
48077
48287
  userId,
48078
48288
  onApplyFilters,
48079
48289
  filterResults,
48080
- isApplyingFilters = false
48290
+ isApplyingFilters = false,
48291
+ focusWidgetId
48081
48292
  }) {
48082
48293
  const [widgets, setWidgets] = useState([]);
48083
48294
  const [datasetId, setDatasetId] = useState("");
@@ -48094,6 +48305,12 @@ function WidgetDashboard({
48094
48305
  const [editInitialQuery, setEditInitialQuery] = useState("");
48095
48306
  const [editingWidget, setEditingWidget] = useState(null);
48096
48307
  const [widgetResetFunctions, setWidgetResetFunctions] = useState(/* @__PURE__ */ new Map());
48308
+ const displayWidgets = React__default.useMemo(() => {
48309
+ if (focusWidgetId) {
48310
+ return widgets.filter((w) => w.id === focusWidgetId);
48311
+ }
48312
+ return widgets;
48313
+ }, [widgets, focusWidgetId]);
48097
48314
  const getWidgetFilterStatus = useCallback((widgetId) => {
48098
48315
  if (!filterResults) return null;
48099
48316
  const result = filterResults.results.find((r) => r.widgetId === widgetId);
@@ -48692,7 +48909,7 @@ function WidgetDashboard({
48692
48909
  };
48693
48910
  const getLayoutFromWidgets = () => {
48694
48911
  const processedIds = /* @__PURE__ */ new Set();
48695
- return widgets.filter((widget) => {
48912
+ return displayWidgets.filter((widget) => {
48696
48913
  if (processedIds.has(widget.id)) {
48697
48914
  return false;
48698
48915
  }
@@ -48700,6 +48917,22 @@ function WidgetDashboard({
48700
48917
  return true;
48701
48918
  }).map((widget) => {
48702
48919
  widget.type === "text";
48920
+ if (focusWidgetId && widget.id === focusWidgetId) {
48921
+ return {
48922
+ i: widget.id,
48923
+ x: 0,
48924
+ y: 0,
48925
+ w: 12,
48926
+ // Full width
48927
+ h: 10,
48928
+ // Increased height for presentation
48929
+ minW: 0,
48930
+ minH: 1,
48931
+ isResizable: false,
48932
+ static: true
48933
+ // Prevent any movement in focus mode
48934
+ };
48935
+ }
48703
48936
  return {
48704
48937
  i: widget.id,
48705
48938
  x: widget.position_x,
@@ -48782,11 +49015,15 @@ function WidgetDashboard({
48782
49015
  compactType: "vertical",
48783
49016
  containerPadding: [0, 0],
48784
49017
  margin: [16, 16],
48785
- children: widgets.map((w) => {
48786
- var _a, _b;
49018
+ children: displayWidgets.map((w) => {
49019
+ var _a, _b, _c;
48787
49020
  const filterStatus = w.type === "agent" ? getWidgetFilterStatus(w.id) : null;
48788
49021
  const badgeInfo = filterStatus ? getFilterStatusBadge(filterStatus.status) : null;
48789
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `${w.type === "text" || w.type === "spacer" ? `${((_b = (_a = w == null ? void 0 : w.config) == null ? void 0 : _a.content) == null ? void 0 : _b.divider) === "yes" && "border-b border-gray-300"} ${isEditing ? "shadow-lg rounded-xl border border-primary-300" : "flex items-center"}` : `shadow-lg rounded-xl border border-primary-300 p-4 ${isEditing ? "pb-14" : "pb-5"}`} relative`, children: [
49022
+ const isFocusMode = focusWidgetId && w.id === focusWidgetId;
49023
+ const widgetBaseStyles = "rounded-xl border border-gray-200 !bg-white";
49024
+ const widgetHoverStyles = isEditing ? "hover:border-primary-500 hover:shadow-lg transition-all duration-200" : "";
49025
+ const widgetShadow = "shadow-[0_2px_8px_-2px_rgba(0,0,0,0.15)]";
49026
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { "data-widget-id": w.id, className: `${w.type === "text" || w.type === "spacer" ? `${((_b = (_a = w == null ? void 0 : w.config) == null ? void 0 : _a.content) == null ? void 0 : _b.divider) === "yes" && "border-b border-gray-300"} ${isEditing ? `${widgetBaseStyles} ${widgetShadow} ${widgetHoverStyles}` : "flex items-center"}` : `${widgetBaseStyles} ${widgetShadow} ${widgetHoverStyles} p-4 ${isEditing ? "pb-14" : "pb-5"}`} ${isFocusMode ? "h-full" : ""} relative`, children: [
48790
49027
  w.type === "agent" && badgeInfo && !isApplyingFilters && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-2 right-2 z-10", title: (filterStatus == null ? void 0 : filterStatus.reason) || (filterStatus == null ? void 0 : filterStatus.error) || "", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Badge, { variant: badgeInfo.variant, className: "text-[10px] px-2 py-0.5 gap-1", children: [
48791
49028
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: badgeInfo.icon }),
48792
49029
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: badgeInfo.label })
@@ -48818,7 +49055,7 @@ function WidgetDashboard({
48818
49055
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { onClick: () => handleClearChat(w == null ? void 0 : w.id), onMouseOver: () => setVisibleClearButton(w == null ? void 0 : w.id), onMouseLeave: () => setVisibleClearButton(""), className: "absolute top-[12px] right-0 z-40 flex align-middle justify-center gap-2 text-sm px-4 py-2 border-primary-300 rounded-l-sm w-fit bg-primary-700 text-white cursor-pointer shadow-md transition-all", children: /* @__PURE__ */ jsxRuntimeExports.jsx(MessageCircleX, { className: "w-5 h-5" }) }),
48819
49056
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `absolute top-[56px] right-[16px] z-50 w-max py-1 text-xs px-2 rounded-sm text-white bg-gray-950 ${visibleClearButton === (w == null ? void 0 : w.id) ? "block" : "hidden"}`, children: "Clear Chat" })
48820
49057
  ] }),
48821
- /* @__PURE__ */ jsxRuntimeExports.jsx(WidgetRenderer, { widget: w, widgetBackendUrl, onResetReady: handleResetReady, widgetIds: widgets.filter((widget) => widget.type !== "chatbot").map((widget) => widget.id), datasetId, pageId, onApplyFilters, isEditing })
49058
+ /* @__PURE__ */ jsxRuntimeExports.jsx(WidgetRenderer, { widget: w, widgetBackendUrl, onResetReady: handleResetReady, widgetIds: widgets.filter((widget) => widget.type !== "chatbot").map((widget) => widget.id), datasetId, pageId, onApplyFilters, isEditing, dashboardName: ((_c = pageData == null ? void 0 : pageData.basic) == null ? void 0 : _c.title) || (pageData == null ? void 0 : pageData.name) || (pageData == null ? void 0 : pageData.title) })
48822
49059
  ] })
48823
49060
  ] }, w.id);
48824
49061
  })
@@ -48829,6 +49066,366 @@ function WidgetDashboard({
48829
49066
  ] });
48830
49067
  }
48831
49068
 
49069
+ const EXCLUDED_WIDGET_TYPES = ["text", "spacer", "filter", "filters", "Text", "Spacer", "Filter", "Filters"];
49070
+ const LaserPointerIcon = ({ className }) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
49071
+ "svg",
49072
+ {
49073
+ viewBox: "0 0 24 24",
49074
+ fill: "none",
49075
+ stroke: "currentColor",
49076
+ strokeWidth: "2",
49077
+ strokeLinecap: "round",
49078
+ strokeLinejoin: "round",
49079
+ className,
49080
+ children: [
49081
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "3", fill: "currentColor" }),
49082
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "12", y1: "2", x2: "12", y2: "6" }),
49083
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "12", y1: "18", x2: "12", y2: "22" }),
49084
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "2", y1: "12", x2: "6", y2: "12" }),
49085
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "18", y1: "12", x2: "22", y2: "12" })
49086
+ ]
49087
+ }
49088
+ );
49089
+ function PresentationMode({
49090
+ isOpen,
49091
+ onClose,
49092
+ pageId,
49093
+ dashboardTitle,
49094
+ widgetBackendUrl = "http://localhost:3001",
49095
+ branding = { title: "DASHBOARD", subtitle: "COMPOSER" }
49096
+ }) {
49097
+ const [viewMode, setViewMode] = useState("grid");
49098
+ const [widgets, setWidgets] = useState([]);
49099
+ const [focusableWidgets, setFocusableWidgets] = useState([]);
49100
+ const [currentFocusIndex, setCurrentFocusIndex] = useState(0);
49101
+ const [loading, setLoading] = useState(true);
49102
+ const [laserPointerActive, setLaserPointerActive] = useState(false);
49103
+ const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
49104
+ const focusContainerRef = useRef(null);
49105
+ const gridContainerRef = useRef(null);
49106
+ useEffect(() => {
49107
+ if (isOpen && pageId) {
49108
+ setLoading(true);
49109
+ setCurrentFocusIndex(0);
49110
+ fetch(`${widgetBackendUrl}/api/widgets?page_id=${pageId}`).then((res) => {
49111
+ if (!res.ok) throw new Error("Failed to fetch widgets");
49112
+ return res.json();
49113
+ }).then((data) => {
49114
+ const widgetsArray = Array.isArray(data) ? data : [];
49115
+ setWidgets(widgetsArray);
49116
+ const focusable = widgetsArray.filter((widget) => !EXCLUDED_WIDGET_TYPES.includes(widget.type)).sort((a, b) => {
49117
+ if (a.position_y !== b.position_y) return a.position_y - b.position_y;
49118
+ return a.position_x - b.position_x;
49119
+ });
49120
+ setFocusableWidgets(focusable);
49121
+ console.log(`[PresentationMode] Loaded ${widgetsArray.length} widgets, ${focusable.length} focusable`);
49122
+ }).catch((error) => {
49123
+ console.error("Failed to fetch widgets:", error);
49124
+ setWidgets([]);
49125
+ setFocusableWidgets([]);
49126
+ }).finally(() => {
49127
+ setLoading(false);
49128
+ });
49129
+ }
49130
+ }, [isOpen, pageId, widgetBackendUrl]);
49131
+ useEffect(() => {
49132
+ if (focusableWidgets.length > 0 && currentFocusIndex >= focusableWidgets.length) {
49133
+ setCurrentFocusIndex(focusableWidgets.length - 1);
49134
+ }
49135
+ }, [focusableWidgets.length, currentFocusIndex]);
49136
+ useEffect(() => {
49137
+ if (!laserPointerActive) return;
49138
+ const handleMouseMove = (e) => {
49139
+ setMousePosition({ x: e.clientX, y: e.clientY });
49140
+ };
49141
+ window.addEventListener("mousemove", handleMouseMove);
49142
+ return () => window.removeEventListener("mousemove", handleMouseMove);
49143
+ }, [laserPointerActive]);
49144
+ useEffect(() => {
49145
+ if (!isOpen || viewMode !== "grid" || !gridContainerRef.current) return;
49146
+ const setupWidgetInteractions = () => {
49147
+ var _a;
49148
+ const gridItems = (_a = gridContainerRef.current) == null ? void 0 : _a.querySelectorAll(".react-grid-item");
49149
+ if (!gridItems) return;
49150
+ const handleMouseEnter = (e) => {
49151
+ var _a2;
49152
+ const item = e.currentTarget;
49153
+ const widgetId = item.getAttribute("data-widget-id") || ((_a2 = item.querySelector("[data-widget-id]")) == null ? void 0 : _a2.getAttribute("data-widget-id"));
49154
+ const widget = widgets.find((w) => w.id === widgetId);
49155
+ if (widget && EXCLUDED_WIDGET_TYPES.includes(widget.type)) {
49156
+ return;
49157
+ }
49158
+ item.style.cursor = "pointer";
49159
+ item.style.boxShadow = "0 0 0 3px var(--primary-500, #3b82f6), 0 10px 30px -5px rgba(0, 0, 0, 0.2)";
49160
+ item.style.transition = "box-shadow 0.2s ease-out";
49161
+ item.style.zIndex = "10";
49162
+ };
49163
+ const handleMouseLeave = (e) => {
49164
+ const item = e.currentTarget;
49165
+ item.style.cursor = "";
49166
+ item.style.boxShadow = "";
49167
+ item.style.zIndex = "";
49168
+ };
49169
+ const handleClick = (e) => {
49170
+ var _a2;
49171
+ const item = e.currentTarget;
49172
+ const widgetId = item.getAttribute("data-widget-id") || ((_a2 = item.querySelector("[data-widget-id]")) == null ? void 0 : _a2.getAttribute("data-widget-id"));
49173
+ const widget = widgets.find((w) => w.id === widgetId);
49174
+ if (widget && EXCLUDED_WIDGET_TYPES.includes(widget.type)) {
49175
+ return;
49176
+ }
49177
+ if (widgetId) {
49178
+ const index = focusableWidgets.findIndex((w) => w.id === widgetId);
49179
+ if (index !== -1) {
49180
+ setCurrentFocusIndex(index);
49181
+ setViewMode("focus");
49182
+ }
49183
+ }
49184
+ };
49185
+ gridItems.forEach((item) => {
49186
+ item.addEventListener("mouseenter", handleMouseEnter);
49187
+ item.addEventListener("mouseleave", handleMouseLeave);
49188
+ item.addEventListener("click", handleClick);
49189
+ });
49190
+ return () => {
49191
+ gridItems.forEach((item) => {
49192
+ item.removeEventListener("mouseenter", handleMouseEnter);
49193
+ item.removeEventListener("mouseleave", handleMouseLeave);
49194
+ item.removeEventListener("click", handleClick);
49195
+ item.style.cursor = "";
49196
+ item.style.transform = "";
49197
+ item.style.boxShadow = "";
49198
+ });
49199
+ };
49200
+ };
49201
+ const timeoutId = setTimeout(setupWidgetInteractions, 500);
49202
+ const observer = new MutationObserver(() => {
49203
+ setupWidgetInteractions();
49204
+ });
49205
+ if (gridContainerRef.current) {
49206
+ observer.observe(gridContainerRef.current, {
49207
+ childList: true,
49208
+ subtree: true
49209
+ });
49210
+ }
49211
+ return () => {
49212
+ clearTimeout(timeoutId);
49213
+ observer.disconnect();
49214
+ };
49215
+ }, [isOpen, viewMode, widgets, focusableWidgets, loading]);
49216
+ const handleKeyDown = useCallback(
49217
+ (e) => {
49218
+ if (!isOpen) return;
49219
+ if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) {
49220
+ return;
49221
+ }
49222
+ switch (e.key) {
49223
+ case "Escape":
49224
+ onClose();
49225
+ break;
49226
+ case "f":
49227
+ case "F":
49228
+ setViewMode((prev) => prev === "grid" ? "focus" : "grid");
49229
+ break;
49230
+ case "l":
49231
+ case "L":
49232
+ setLaserPointerActive((prev) => !prev);
49233
+ break;
49234
+ case "ArrowLeft":
49235
+ if (viewMode === "focus") {
49236
+ e.preventDefault();
49237
+ setCurrentFocusIndex(
49238
+ (prev) => prev > 0 ? prev - 1 : focusableWidgets.length - 1
49239
+ );
49240
+ }
49241
+ break;
49242
+ case "ArrowRight":
49243
+ if (viewMode === "focus") {
49244
+ e.preventDefault();
49245
+ setCurrentFocusIndex(
49246
+ (prev) => prev < focusableWidgets.length - 1 ? prev + 1 : 0
49247
+ );
49248
+ }
49249
+ break;
49250
+ }
49251
+ },
49252
+ [isOpen, onClose, viewMode, focusableWidgets.length]
49253
+ );
49254
+ useEffect(() => {
49255
+ window.addEventListener("keydown", handleKeyDown);
49256
+ return () => window.removeEventListener("keydown", handleKeyDown);
49257
+ }, [handleKeyDown]);
49258
+ useEffect(() => {
49259
+ if (isOpen) {
49260
+ document.body.style.overflow = "hidden";
49261
+ } else {
49262
+ document.body.style.overflow = "";
49263
+ }
49264
+ return () => {
49265
+ document.body.style.overflow = "";
49266
+ };
49267
+ }, [isOpen]);
49268
+ useEffect(() => {
49269
+ if (!isOpen) {
49270
+ setViewMode("grid");
49271
+ setCurrentFocusIndex(0);
49272
+ setLaserPointerActive(false);
49273
+ }
49274
+ }, [isOpen]);
49275
+ if (!isOpen) return null;
49276
+ const currentFocusWidget = focusableWidgets[currentFocusIndex];
49277
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "fixed inset-0 z-50 bg-white flex flex-col", children: [
49278
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("header", { className: "flex items-center justify-between px-6 py-4 border-b border-gray-200 bg-white shrink-0", children: [
49279
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col", children: [
49280
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xl font-bold text-primary-700 tracking-wide", children: branding.title }),
49281
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-primary-500 tracking-[0.2em] -mt-1", children: branding.subtitle })
49282
+ ] }) }),
49283
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute left-1/2 transform -translate-x-1/2 text-center", children: [
49284
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { className: "text-lg font-bold text-gray-900 uppercase tracking-wide", children: dashboardTitle }),
49285
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-12 h-1 bg-primary-600 mx-auto mt-1 rounded" })
49286
+ ] }),
49287
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
49288
+ "button",
49289
+ {
49290
+ onClick: onClose,
49291
+ className: "p-2 hover:bg-gray-100 rounded-lg transition-colors",
49292
+ "aria-label": "Close presentation",
49293
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "w-6 h-6 text-gray-600" })
49294
+ }
49295
+ )
49296
+ ] }),
49297
+ /* @__PURE__ */ jsxRuntimeExports.jsx("main", { className: `flex-1 min-h-0 ${viewMode === "grid" ? "overflow-auto bg-gray-50 p-6" : "overflow-hidden bg-gray-100"}`, children: loading ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-center h-full", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center gap-3", children: [
49298
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-8 h-8 border-4 border-primary-600 border-t-transparent rounded-full animate-spin" }),
49299
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-gray-600", children: "Loading widgets..." })
49300
+ ] }) }) : viewMode === "grid" ? (
49301
+ /* Grid View - Show all widgets using WidgetDashboard */
49302
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-full relative", ref: gridContainerRef, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
49303
+ WidgetDashboard,
49304
+ {
49305
+ pageId,
49306
+ isEditing: false,
49307
+ selectedWidget: null,
49308
+ onWidgetSelect: () => {
49309
+ },
49310
+ openWidgetPallete: false,
49311
+ onCloseWidgetPallete: () => {
49312
+ },
49313
+ onApplyFilters: () => {
49314
+ },
49315
+ isApplyingFilters: false,
49316
+ onSaveLayoutReady: () => {
49317
+ },
49318
+ widgetBackendUrl
49319
+ },
49320
+ `presentation-grid-${pageId}`
49321
+ ) })
49322
+ ) : (
49323
+ /* Focus View - Show one widget at a time */
49324
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-full flex flex-col", ref: focusContainerRef, children: focusableWidgets.length > 0 && currentFocusWidget ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex flex-col min-h-0", children: [
49325
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "shrink-0 bg-white border-b border-gray-200 px-6 py-3 flex items-center justify-between", children: [
49326
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
49327
+ "button",
49328
+ {
49329
+ onClick: () => setCurrentFocusIndex((prev) => prev > 0 ? prev - 1 : focusableWidgets.length - 1),
49330
+ className: "p-2 rounded-full hover:bg-gray-100 transition-colors disabled:opacity-30",
49331
+ disabled: focusableWidgets.length <= 1,
49332
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronLeft, { className: "w-5 h-5 text-gray-600" })
49333
+ }
49334
+ ),
49335
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-center", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-sm text-gray-500", children: [
49336
+ currentFocusIndex + 1,
49337
+ " of ",
49338
+ focusableWidgets.length
49339
+ ] }) }),
49340
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
49341
+ "button",
49342
+ {
49343
+ onClick: () => setCurrentFocusIndex((prev) => prev < focusableWidgets.length - 1 ? prev + 1 : 0),
49344
+ className: "p-2 rounded-full hover:bg-gray-100 transition-colors disabled:opacity-30",
49345
+ disabled: focusableWidgets.length <= 1,
49346
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "w-5 h-5 text-gray-600" })
49347
+ }
49348
+ )
49349
+ ] }),
49350
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 bg-white p-6 min-h-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-full w-full rounded-xl border border-gray-200 bg-white shadow-sm overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
49351
+ WidgetRenderer,
49352
+ {
49353
+ widget: currentFocusWidget,
49354
+ widgetBackendUrl,
49355
+ pageId,
49356
+ isEditing: false
49357
+ },
49358
+ `focus-widget-${currentFocusWidget.id}`
49359
+ ) }) })
49360
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center text-gray-500", children: [
49361
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Maximize2, { className: "w-12 h-12 mx-auto mb-4 text-gray-300" }),
49362
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-lg font-medium", children: "No widgets available for focus view" }),
49363
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm mt-2", children: "Text, Spacer, and Filter widgets are excluded." })
49364
+ ] }) }) })
49365
+ ) }),
49366
+ /* @__PURE__ */ jsxRuntimeExports.jsx("footer", { className: "bg-white border-t border-gray-200 py-4 shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center gap-3", children: [
49367
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 bg-gray-100 rounded-full p-1", children: [
49368
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
49369
+ "button",
49370
+ {
49371
+ onClick: () => setViewMode("grid"),
49372
+ className: `flex items-center gap-2 px-4 py-2 rounded-full text-sm font-medium transition-all ${viewMode === "grid" ? "bg-primary-700 text-white shadow-md" : "text-gray-600 hover:text-gray-900"}`,
49373
+ children: [
49374
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Grid3X3, { className: "w-4 h-4" }),
49375
+ "Grid"
49376
+ ]
49377
+ }
49378
+ ),
49379
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
49380
+ "button",
49381
+ {
49382
+ onClick: () => setViewMode("focus"),
49383
+ className: `flex items-center gap-2 px-4 py-2 rounded-full text-sm font-medium transition-all ${viewMode === "focus" ? "bg-primary-700 text-white shadow-md" : "text-gray-600 hover:text-gray-900"}`,
49384
+ children: [
49385
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Maximize2, { className: "w-4 h-4" }),
49386
+ "Focus"
49387
+ ]
49388
+ }
49389
+ ),
49390
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-px h-6 bg-gray-300 mx-2" }),
49391
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
49392
+ "button",
49393
+ {
49394
+ onClick: () => setLaserPointerActive((prev) => !prev),
49395
+ className: `p-2 transition-colors rounded-full ${laserPointerActive ? "bg-primary-600 text-white" : "text-gray-600 hover:text-gray-900 hover:bg-gray-200"}`,
49396
+ "aria-label": "Toggle laser pointer (L)",
49397
+ title: "Toggle laser pointer (L)",
49398
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(LaserPointerIcon, { className: "w-4 h-4" })
49399
+ }
49400
+ ),
49401
+ viewMode === "focus" && focusableWidgets.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
49402
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-px h-6 bg-gray-300 mx-2" }),
49403
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-sm text-gray-600 px-2", children: [
49404
+ currentFocusIndex + 1,
49405
+ " / ",
49406
+ focusableWidgets.length
49407
+ ] })
49408
+ ] })
49409
+ ] }),
49410
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-gray-400 uppercase tracking-wider", children: "ESC TO EXIT \u2022 F TO TOGGLE \u2022 L FOR LASER \u2022 ARROWS TO NAVIGATE" })
49411
+ ] }) }),
49412
+ laserPointerActive && /* @__PURE__ */ jsxRuntimeExports.jsxs(
49413
+ "div",
49414
+ {
49415
+ className: "fixed pointer-events-none z-[100] transform -translate-x-1/2 -translate-y-1/2",
49416
+ style: {
49417
+ left: mousePosition.x,
49418
+ top: mousePosition.y
49419
+ },
49420
+ children: [
49421
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 w-8 h-8 -translate-x-1/2 -translate-y-1/2 bg-red-500/20 rounded-full blur-md" }),
49422
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute w-4 h-4 -translate-x-1/2 -translate-y-1/2 bg-red-500 rounded-full shadow-lg shadow-red-500/50" })
49423
+ ]
49424
+ }
49425
+ )
49426
+ ] });
49427
+ }
49428
+
48832
49429
  var __defProp = Object.defineProperty;
48833
49430
  var __defProps = Object.defineProperties;
48834
49431
  var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
@@ -49290,5 +49887,5 @@ function DashboardPages({ widgetBackendUrl }) {
49290
49887
  ] });
49291
49888
  }
49292
49889
 
49293
- export { Button, DashboardPages, Input, SavedPages, WidgetDashboard, WidgetPalette, WidgetSettingsPanel, cn };
49890
+ export { Button, DashboardPages, Input, PresentationMode, SavedPages, WidgetDashboard, WidgetPalette, WidgetSettingsPanel, cn };
49294
49891
  //# sourceMappingURL=index.esm.js.map