dp-widgets-framework 1.5.8 → 1.6.0

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, X, AlignVerticalSpaceAround, LineChart as LineChart$1, PieChart as PieChart$1, Table, FileText, SlidersHorizontal, Loader2, 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, Send, Loader2, X, AlignVerticalSpaceAround, LineChart as LineChart$1, PieChart as PieChart$1, Table, FileText, SlidersHorizontal, GripHorizontal, Edit, MessageCircleX, Edit2 } from 'lucide-react';
11
11
  import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
12
12
  import { Slot } from '@radix-ui/react-slot';
13
13
  import { debounce as debounce$1 } from 'lodash';
@@ -1986,6 +1986,29 @@ function WidgetPalette({ widgetBackendUrl } = {}) {
1986
1986
  }, className: "cursor-move hover:bg-accent transition-colors", children: jsxRuntimeExports.jsx(CardContent, { className: "p-3", children: jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [React__default.createElement(widget.icon, {}), jsxRuntimeExports.jsxs("div", { children: [jsxRuntimeExports.jsx("h4", { className: "font-medium text-sm", children: widget.name }), jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: widget.description })] })] }) }) }, widget.type))) }) })] }));
1987
1987
  }
1988
1988
 
1989
+ const badgeVariants = cva("inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", {
1990
+ variants: {
1991
+ variant: {
1992
+ default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
1993
+ secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
1994
+ destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
1995
+ outline: "text-foreground",
1996
+ // Filter status variants
1997
+ success: "border-transparent bg-green-100 text-green-800 hover:bg-green-200",
1998
+ warning: "border-transparent bg-amber-100 text-amber-800 hover:bg-amber-200",
1999
+ muted: "border-transparent bg-gray-100 text-gray-600 hover:bg-gray-200",
2000
+ error: "border-transparent bg-red-100 text-red-800 hover:bg-red-200",
2001
+ },
2002
+ },
2003
+ defaultVariants: {
2004
+ variant: "default",
2005
+ },
2006
+ });
2007
+ function Badge(_a) {
2008
+ var { className, variant } = _a, props = __rest(_a, ["className", "variant"]);
2009
+ return (jsxRuntimeExports.jsx("div", Object.assign({ className: cn(badgeVariants({ variant }), className) }, props)));
2010
+ }
2011
+
1989
2012
  const borderRadiusMap$2 = {
1990
2013
  none: "0px",
1991
2014
  rounded: "0.375rem",
@@ -39327,6 +39350,10 @@ const parseAndUpdateFilterState = (apiResponse, setFilterState) => {
39327
39350
  if (filterData.agent_message) {
39328
39351
  newFilterState.agent_message = filterData.agent_message;
39329
39352
  }
39353
+ // Handle error key as a fallback for agent_message
39354
+ if (filterData.error && !newFilterState.agent_message) {
39355
+ newFilterState.agent_message = filterData.error;
39356
+ }
39330
39357
  // Handle confirmation state
39331
39358
  if (filterData.status) {
39332
39359
  newFilterState.status = filterData.status;
@@ -39382,15 +39409,17 @@ function TableSelectionConfirmation({ ambiguousColumns, resolvedColumns, message
39382
39409
  onConfirm(selectedTables);
39383
39410
  };
39384
39411
  const allSelected = ambiguousColumns.every((col) => selectedTables[col.column]);
39385
- return (jsxRuntimeExports.jsxs("div", { className: "flex flex-col h-full p-4 bg-white", children: [jsxRuntimeExports.jsxs("div", { className: "mb-4", children: [jsxRuntimeExports.jsx("h3", { className: "text-lg font-semibold text-gray-800 mb-2", children: "Table Selection Required" }), message && (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600 mb-4 whitespace-pre-line", children: message }))] }), resolvedColumns && resolvedColumns.length > 0 && (jsxRuntimeExports.jsxs("div", { className: "mb-4 p-3 bg-green-50 border border-green-200 rounded-lg", children: [jsxRuntimeExports.jsx("p", { className: "text-sm font-medium text-green-800 mb-2", children: "Already Resolved Columns:" }), jsxRuntimeExports.jsx("div", { className: "space-y-1", children: resolvedColumns.map((col) => (jsxRuntimeExports.jsxs("div", { className: "text-sm text-green-700", children: [jsxRuntimeExports.jsx("span", { className: "font-medium", children: col.column }), " \u2192 ", jsxRuntimeExports.jsx("span", { className: "text-green-600", children: col.table_name })] }, col.column))) })] })), jsxRuntimeExports.jsx(ScrollArea, { className: "flex-1 mb-4", children: jsxRuntimeExports.jsx("div", { className: "space-y-4", children: ambiguousColumns.map((col) => (jsxRuntimeExports.jsxs("div", { className: "border border-gray-200 rounded-lg p-3", children: [jsxRuntimeExports.jsx("label", { className: "block mb-2", children: jsxRuntimeExports.jsxs("span", { className: "text-sm font-medium text-gray-700", children: ["Column: ", jsxRuntimeExports.jsx("span", { className: "text-primary-600", children: col.column })] }) }), jsxRuntimeExports.jsxs("select", { value: selectedTables[col.column] || '', onChange: (e) => {
39412
+ return (jsxRuntimeExports.jsxs("div", { className: "flex flex-col h-full p-4 bg-white overflow-hidden", children: [jsxRuntimeExports.jsxs("div", { className: "mb-4 flex-shrink-0", children: [jsxRuntimeExports.jsx("h3", { className: "text-lg font-semibold text-gray-800 mb-2", children: "Table Selection Required" }), message && (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600 mb-4 whitespace-pre-line", children: message }))] }), resolvedColumns && resolvedColumns.length > 0 && (jsxRuntimeExports.jsxs("div", { className: "mb-4 p-3 bg-green-50 border border-green-200 rounded-lg flex-shrink-0", children: [jsxRuntimeExports.jsx("p", { className: "text-sm font-medium text-green-800 mb-2", children: "Already Resolved Columns:" }), jsxRuntimeExports.jsx("div", { className: "space-y-1", children: resolvedColumns.map((col) => (jsxRuntimeExports.jsxs("div", { className: "text-sm text-green-700", children: [jsxRuntimeExports.jsx("span", { className: "font-medium", children: col.column }), " \u2192 ", jsxRuntimeExports.jsx("span", { className: "text-green-600", children: col.table_name })] }, col.column))) })] })), jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-y-auto mb-4 min-h-0 scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-transparent", children: jsxRuntimeExports.jsx("div", { className: "space-y-4", children: ambiguousColumns.map((col) => (jsxRuntimeExports.jsxs("div", { className: "border border-gray-200 rounded-lg p-3", children: [jsxRuntimeExports.jsx("label", { className: "block mb-2", children: jsxRuntimeExports.jsxs("span", { className: "text-sm font-medium text-gray-700", children: ["Column: ", jsxRuntimeExports.jsx("span", { className: "text-primary-600", children: col.column })] }) }), jsxRuntimeExports.jsxs("select", { value: selectedTables[col.column] || '', onChange: (e) => {
39386
39413
  setSelectedTables((prev) => (Object.assign(Object.assign({}, prev), { [col.column]: e.target.value })));
39387
- }, className: "w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500", children: [jsxRuntimeExports.jsx("option", { value: "", children: "Select a table..." }), col.tables.map((table) => (jsxRuntimeExports.jsx("option", { value: table, children: table }, table)))] })] }, col.column))) }) }), jsxRuntimeExports.jsxs("div", { className: "flex gap-2 pt-3 border-t border-gray-200", children: [jsxRuntimeExports.jsx(Button, { onClick: onCancel, variant: "outline", className: "flex-1", children: "Cancel" }), jsxRuntimeExports.jsx(Button, { onClick: handleSubmit, disabled: !allSelected, className: "flex-1 bg-primary-600 hover:bg-primary-700 text-white disabled:bg-gray-400", children: "Confirm Selection" })] })] }));
39414
+ }, className: "w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500", children: [jsxRuntimeExports.jsx("option", { value: "", children: "Select a table..." }), col.tables.map((table) => (jsxRuntimeExports.jsx("option", { value: table, children: table }, table)))] })] }, col.column))) }) }), jsxRuntimeExports.jsxs("div", { className: "flex gap-2 pt-3 border-t border-gray-200 flex-shrink-0", children: [jsxRuntimeExports.jsx(Button, { onClick: onCancel, variant: "outline", className: "flex-1", children: "Cancel" }), jsxRuntimeExports.jsx(Button, { onClick: handleSubmit, disabled: !allSelected, className: "flex-1 bg-primary-600 hover:bg-primary-700 text-white disabled:bg-gray-400", children: "Confirm Selection" })] })] }));
39388
39415
  }
39389
39416
  function FiltersContent({ filterGroups, showHeader, onFilterChange, widget, appendMessage, query, isFirstLoad, widgetBackendUrl, widgetId, startLoadingTimeout, clearLoadingTimeout, filterState, onApplyFilters, isEditing = false, }) {
39417
+ var _a;
39390
39418
  const hasCalledRef = useRef(false);
39391
39419
  const [expandedGroups, setExpandedGroups] = useState({});
39392
39420
  const [selectedFilters, setSelectedFilters] = useState({});
39393
39421
  const [searchQueries, setSearchQueries] = useState({});
39422
+ const [isSubmitting, setIsSubmitting] = useState(false);
39394
39423
  const isEmpty = filterGroups.length === 0;
39395
39424
  const handleRefresh = async () => {
39396
39425
  if (query) {
@@ -39428,6 +39457,29 @@ function FiltersContent({ filterGroups, showHeader, onFilterChange, widget, appe
39428
39457
  });
39429
39458
  setExpandedGroups(initialExpanded);
39430
39459
  }, [filterGroups]);
39460
+ // Initialize selected filters from widget.config.filters
39461
+ useEffect(() => {
39462
+ var _a;
39463
+ if (((_a = widget.config) === null || _a === void 0 ? void 0 : _a.filters) && filterGroups.length > 0) {
39464
+ const initialSelectedFilters = {};
39465
+ Object.entries(widget.config.filters).forEach(([groupId, labels]) => {
39466
+ const group = filterGroups.find(g => g.id === groupId);
39467
+ if (group && Array.isArray(labels)) {
39468
+ // Convert labels back to option IDs
39469
+ const optionIds = labels
39470
+ .map(label => {
39471
+ const option = group.options.find(opt => opt.label === label);
39472
+ return option === null || option === void 0 ? void 0 : option.id;
39473
+ })
39474
+ .filter((id) => id !== undefined);
39475
+ if (optionIds.length > 0) {
39476
+ initialSelectedFilters[groupId] = optionIds;
39477
+ }
39478
+ }
39479
+ });
39480
+ setSelectedFilters(initialSelectedFilters);
39481
+ }
39482
+ }, [(_a = widget.config) === null || _a === void 0 ? void 0 : _a.filters, filterGroups]);
39431
39483
  const toggleGroup = (groupId) => {
39432
39484
  setExpandedGroups((prev) => (Object.assign(Object.assign({}, prev), { [groupId]: !prev[groupId] })));
39433
39485
  };
@@ -39474,24 +39526,24 @@ function FiltersContent({ filterGroups, showHeader, onFilterChange, widget, appe
39474
39526
  if (isEmpty) {
39475
39527
  return createLoadingComponent$4();
39476
39528
  }
39477
- return (jsxRuntimeExports.jsxs("div", { className: "h-full flex flex-col bg-white rounded-lg", children: [showHeader && (jsxRuntimeExports.jsx("div", { className: "px-4 py-3 border-b border-gray-100", children: jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntimeExports.jsx(Bot, { className: "h-4 w-4" }), jsxRuntimeExports.jsx("h3", { className: "font-semibold text-gray-800", children: "Filters" })] }) })), jsxRuntimeExports.jsx(ScrollArea, { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: "p-2", children: filterGroups.map((group) => {
39529
+ return (jsxRuntimeExports.jsxs("div", { className: "h-full flex flex-col bg-white rounded-lg overflow-hidden", children: [showHeader && (jsxRuntimeExports.jsx("div", { className: "px-4 py-3 border-b border-gray-100 flex-shrink-0", children: jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntimeExports.jsx(Bot, { className: "h-4 w-4" }), jsxRuntimeExports.jsx("h3", { className: "font-semibold text-gray-800", children: "Filters" })] }) })), jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-y-auto min-h-0 scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-transparent", children: jsxRuntimeExports.jsx("div", { className: "p-2", children: filterGroups.map((group) => {
39478
39530
  const isExpanded = expandedGroups[group.id];
39479
39531
  const selectedCount = getSelectedCount(group.id);
39480
39532
  const filteredOptions = getFilteredOptions(group);
39481
39533
  return (jsxRuntimeExports.jsxs("div", { className: "mb-2 border border-gray-100 rounded-lg overflow-hidden", children: [jsxRuntimeExports.jsxs("button", { onClick: () => toggleGroup(group.id), className: cn("w-full flex items-center justify-between px-3 py-2.5", "bg-gray-50 hover:bg-gray-100 transition-colors", "focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-inset"), children: [jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [isExpanded ? (jsxRuntimeExports.jsx(ChevronDown, { className: "w-4 h-4 text-primary-600" })) : (jsxRuntimeExports.jsx(ChevronRight, { className: "w-4 h-4 text-gray-500" })), jsxRuntimeExports.jsx("span", { className: "font-medium text-sm text-gray-700", children: group.name }), selectedCount > 0 && (jsxRuntimeExports.jsx("span", { className: "ml-1 px-1.5 py-0.5 text-xs font-medium bg-primary-100 text-primary-700 rounded-full", children: selectedCount }))] }), selectedCount > 0 && (jsxRuntimeExports.jsx("button", { onClick: (e) => {
39482
39534
  e.stopPropagation();
39483
39535
  clearGroupFilters(group.id);
39484
- }, className: "text-xs text-primary-600 hover:text-primary-800 hover:underline", children: "Clear" }))] }), jsxRuntimeExports.jsx("div", { className: cn("overflow-hidden transition-all duration-200 ease-in-out", isExpanded ? "max-h-[400px]" : "max-h-0"), children: jsxRuntimeExports.jsxs("div", { className: "px-3 py-2 bg-white", children: [group.options.length > 5 && (jsxRuntimeExports.jsxs("div", { className: "relative mb-2", children: [jsxRuntimeExports.jsx(Search, { className: "absolute left-2.5 top-1/2 transform -translate-y-1/2 w-3.5 h-3.5 text-gray-400" }), jsxRuntimeExports.jsx(Input, { type: "text", placeholder: `Search ${group.name.toLowerCase()}...`, value: searchQueries[group.id] || "", onChange: (e) => handleSearchChange(group.id, e.target.value), className: "pl-8 h-8 text-sm border-gray-200 focus:border-primary-400 focus:ring-primary-400" })] })), jsxRuntimeExports.jsx("div", { className: "space-y-1 max-h-[250px] overflow-y-auto pr-1", children: filteredOptions.length === 0 ? (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500 py-2 text-center", children: "No options found" })) : (filteredOptions.map((option) => {
39536
+ }, className: "text-xs text-primary-600 hover:text-primary-800 hover:underline", children: "Clear" }))] }), jsxRuntimeExports.jsx("div", { className: cn("overflow-hidden transition-all duration-200 ease-in-out", isExpanded ? "max-h-[500px]" : "max-h-0"), children: jsxRuntimeExports.jsxs("div", { className: "px-3 py-2 bg-white", children: [group.options.length > 5 && (jsxRuntimeExports.jsxs("div", { className: "relative mb-2", children: [jsxRuntimeExports.jsx(Search, { className: "absolute left-2.5 top-1/2 transform -translate-y-1/2 w-3.5 h-3.5 text-gray-400" }), jsxRuntimeExports.jsx(Input, { type: "text", placeholder: `Search ${group.name.toLowerCase()}...`, value: searchQueries[group.id] || "", onChange: (e) => handleSearchChange(group.id, e.target.value), className: "pl-8 h-8 text-sm border-gray-200 focus:border-primary-400 focus:ring-primary-400" })] })), jsxRuntimeExports.jsx("div", { className: "space-y-1 max-h-[350px] overflow-y-auto pr-1 scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-transparent", children: filteredOptions.length === 0 ? (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500 py-2 text-center", children: "No options found" })) : (filteredOptions.map((option) => {
39485
39537
  var _a;
39486
39538
  const isChecked = ((_a = selectedFilters[group.id]) === null || _a === void 0 ? void 0 : _a.includes(option.id)) || false;
39487
39539
  return (jsxRuntimeExports.jsxs("label", { className: cn("flex items-center gap-2.5 px-2 py-1.5 rounded-md cursor-pointer", "hover:bg-gray-50 transition-colors", isChecked && "bg-primary-50"), children: [jsxRuntimeExports.jsx(Checkbox, { checked: isChecked, onCheckedChange: (checked) => handleCheckboxChange(group.id, option.id, checked), className: cn("h-4 w-4 rounded border-gray-300", "data-[state=checked]:bg-primary-600 data-[state=checked]:border-primary-600") }), jsxRuntimeExports.jsx("span", { className: cn("text-sm flex-1 truncate", isChecked ? "text-primary-700 font-medium" : "text-gray-600"), title: option.label, children: option.label }), option.count !== undefined && (jsxRuntimeExports.jsxs("span", { className: "text-xs text-gray-400", children: ["(", option.count, ")"] }))] }, option.id));
39488
39540
  })) })] }) })] }, group.id));
39489
- }) }) }), jsxRuntimeExports.jsx("div", { className: "px-4 py-2 border-t border-gray-100 bg-gray-50", children: jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [Object.values(selectedFilters).some((arr) => arr.length > 0) ? (jsxRuntimeExports.jsx("button", { onClick: () => {
39541
+ }) }) }), jsxRuntimeExports.jsx("div", { className: "px-4 py-2 border-t border-gray-100 bg-gray-50 flex-shrink-0", children: jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [Object.values(selectedFilters).some((arr) => arr.length > 0) ? (jsxRuntimeExports.jsx("button", { onClick: () => {
39490
39542
  setSelectedFilters({});
39491
39543
  if (onFilterChange) {
39492
39544
  onFilterChange({});
39493
39545
  }
39494
- }, className: "py-1.5 text-sm font-medium text-primary-600 hover:text-primary-800 hover:bg-primary-50 rounded-md transition-colors px-2", children: "Clear All Filters" })) : (jsxRuntimeExports.jsx("div", {})), jsxRuntimeExports.jsx(Button, { onClick: () => {
39546
+ }, className: "py-1.5 text-sm font-medium text-primary-600 hover:text-primary-800 hover:bg-primary-50 rounded-md transition-colors px-2", children: "Clear All Filters" })) : (jsxRuntimeExports.jsx("div", {})), jsxRuntimeExports.jsx(Button, { onClick: async () => {
39495
39547
  // Convert option IDs back to original label values
39496
39548
  const filtersWithLabels = {};
39497
39549
  filterGroups.forEach((group) => {
@@ -39501,8 +39553,37 @@ function FiltersContent({ filterGroups, showHeader, onFilterChange, widget, appe
39501
39553
  return (option === null || option === void 0 ? void 0 : option.label) || optionId;
39502
39554
  });
39503
39555
  });
39504
- onApplyFilters === null || onApplyFilters === void 0 ? void 0 : onApplyFilters(filtersWithLabels);
39505
- }, disabled: isEditing, className: `${isEditing ? 'bg-gray-400 cursor-not-allowed' : 'bg-primary-600 hover:bg-primary-700'} text-white`, title: isEditing ? 'Save the layout first to apply filters' : '', children: "Apply Filters" })] }) })] }));
39556
+ // Call API to update widget config with filters
39557
+ if (widgetBackendUrl && widgetId) {
39558
+ setIsSubmitting(true);
39559
+ try {
39560
+ const response = await fetch(`${widgetBackendUrl}/api/widgets/${widgetId}/config`, {
39561
+ method: 'PATCH',
39562
+ headers: {
39563
+ 'Content-Type': 'application/json',
39564
+ },
39565
+ body: JSON.stringify({
39566
+ config: Object.assign(Object.assign({}, widget.config), { filters: filtersWithLabels }),
39567
+ }),
39568
+ });
39569
+ if (!response.ok) {
39570
+ throw new Error('Failed to update widget config');
39571
+ }
39572
+ // Call the onApplyFilters callback after successful API call
39573
+ onApplyFilters === null || onApplyFilters === void 0 ? void 0 : onApplyFilters(filtersWithLabels);
39574
+ }
39575
+ catch (error) {
39576
+ console.error('Error updating widget config:', error);
39577
+ }
39578
+ finally {
39579
+ setIsSubmitting(false);
39580
+ }
39581
+ }
39582
+ else {
39583
+ // Fallback if no backend URL - just call the callback
39584
+ onApplyFilters === null || onApplyFilters === void 0 ? void 0 : onApplyFilters(filtersWithLabels);
39585
+ }
39586
+ }, disabled: isEditing || isSubmitting, className: `${isEditing || isSubmitting ? 'bg-gray-400 cursor-not-allowed' : 'bg-primary-600 hover:bg-primary-700'} text-white`, title: isEditing ? 'Save the layout first to apply filters' : '', children: isSubmitting ? (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Applying..."] })) : ('Apply Filters') })] }) })] }));
39506
39587
  }
39507
39588
  function CopilotKitFilters({ widget, showHeader, onFilterChange, onResetReady, widgetBackendUrl, datasetId, onApplyFilters, isEditing = false, }) {
39508
39589
  var _a, _b, _c, _d, _e, _f, _g, _h;
@@ -41356,7 +41437,7 @@ const IconMap = {
41356
41437
  'pie-chart': PieChart$1,
41357
41438
  'chatbot': Bot,
41358
41439
  };
41359
- function WidgetDashboard({ pageId, isEditing, selectedWidget = null, onWidgetSelect, refreshKey, widgetBackendUrl, onSaveLayoutReady, openWidgetPallete = false, onCloseWidgetPallete, defaultAgentName = "adk-construction-project-agent", userId, onApplyFilters, activeFilters = {} }) {
41440
+ function WidgetDashboard({ pageId, isEditing, selectedWidget = null, onWidgetSelect, refreshKey, widgetBackendUrl, onSaveLayoutReady, openWidgetPallete = false, onCloseWidgetPallete, defaultAgentName = "adk-construction-project-agent", userId, onApplyFilters, filterResults, isApplyingFilters = false }) {
41360
41441
  const [widgets, setWidgets] = useState([]);
41361
41442
  const [datasetId, setDatasetId] = useState('');
41362
41443
  const [availableWidgets, setAvailableWidgets] = useState([]);
@@ -41371,6 +41452,31 @@ function WidgetDashboard({ pageId, isEditing, selectedWidget = null, onWidgetSel
41371
41452
  const [editInitialQuery, setEditInitialQuery] = useState("");
41372
41453
  const [editingWidget, setEditingWidget] = useState(null);
41373
41454
  const [widgetResetFunctions, setWidgetResetFunctions] = useState(new Map());
41455
+ // Helper to get filter status for a widget
41456
+ const getWidgetFilterStatus = useCallback((widgetId) => {
41457
+ if (!filterResults)
41458
+ return null;
41459
+ // Only show status for agent widgets
41460
+ const result = filterResults.results.find(r => r.widgetId === widgetId);
41461
+ if (!result)
41462
+ return null;
41463
+ return result;
41464
+ }, [filterResults]);
41465
+ // Helper to get badge variant based on filter status
41466
+ const getFilterStatusBadge = (status) => {
41467
+ switch (status) {
41468
+ case "updated":
41469
+ return { variant: "success", label: "Filtered", icon: "✓" };
41470
+ case "no_data":
41471
+ return { variant: "warning", label: "No Data", icon: "⚠" };
41472
+ case "skipped":
41473
+ return { variant: "muted", label: "Skipped", icon: "−" };
41474
+ case "failed":
41475
+ return { variant: "error", label: "Failed", icon: "✕" };
41476
+ default:
41477
+ return { variant: "muted", label: status, icon: "?" };
41478
+ }
41479
+ };
41374
41480
  // Use external selectedWidget if provided, otherwise use internal state
41375
41481
  const currentSelectedWidget = selectedWidget !== undefined ? selectedWidget : internalSelectedWidget;
41376
41482
  const setSelectedWidget = onWidgetSelect || setInternalSelectedWidget;
@@ -41435,6 +41541,19 @@ function WidgetDashboard({ pageId, isEditing, selectedWidget = null, onWidgetSel
41435
41541
  }
41436
41542
  });
41437
41543
  window.dispatchEvent(event);
41544
+ // If filters have been applied (filter widget exists), regenerate filtered_sql_query for this widget
41545
+ // This handles the case where a widget's query is changed after filters have been applied
41546
+ const hasFilterWidget = widgets.some(w => w.type === 'filters');
41547
+ if (hasFilterWidget && editingWidget.type === 'agent' && widgetBackendUrl) {
41548
+ // Dispatch event to notify that filtered query needs regeneration for this widget
41549
+ const regenerateEvent = new CustomEvent('regenerateFilteredQuery', {
41550
+ detail: {
41551
+ widgetId: editingWidget.id,
41552
+ dashboardId: pageId
41553
+ }
41554
+ });
41555
+ window.dispatchEvent(regenerateEvent);
41556
+ }
41438
41557
  }
41439
41558
  };
41440
41559
  const handleResetReady = useCallback((widgetId, resetFn) => {
@@ -41526,6 +41645,15 @@ function WidgetDashboard({ pageId, isEditing, selectedWidget = null, onWidgetSel
41526
41645
  };
41527
41646
  loadWidgetTypes();
41528
41647
  }, []);
41648
+ // Track original widget IDs from initial load
41649
+ const originalWidgetIdsRef = useRef(new Set());
41650
+ // Update original widget IDs when widgets are loaded from server
41651
+ useEffect(() => {
41652
+ if (pageData === null || pageData === void 0 ? void 0 : pageData.widgets) {
41653
+ const ids = new Set(pageData.widgets.map((w) => w.id));
41654
+ originalWidgetIdsRef.current = ids;
41655
+ }
41656
+ }, [pageData]);
41529
41657
  // Use ref to store the latest save function without causing re-renders
41530
41658
  const saveLayoutRef = useRef();
41531
41659
  // Update the ref whenever dependencies change
@@ -41533,6 +41661,13 @@ function WidgetDashboard({ pageId, isEditing, selectedWidget = null, onWidgetSel
41533
41661
  if (!pageData) {
41534
41662
  throw new Error("No page data available");
41535
41663
  }
41664
+ // Identify new agent widgets (added since last load/save)
41665
+ const currentWidgetIds = new Set(widgets.map(w => w.id));
41666
+ const newAgentWidgetIds = widgets
41667
+ .filter(w => w.type === 'agent' && !originalWidgetIdsRef.current.has(w.id))
41668
+ .map(w => w.id);
41669
+ // Check if there's a filter widget on this dashboard
41670
+ const hasFilterWidget = widgets.some(w => w.type === 'filters');
41536
41671
  try {
41537
41672
  const response = await fetch(getApiUrl(`/api/pages/${pageId}`), {
41538
41673
  method: "PUT",
@@ -41550,16 +41685,42 @@ function WidgetDashboard({ pageId, isEditing, selectedWidget = null, onWidgetSel
41550
41685
  throw new Error("Failed to save layout");
41551
41686
  }
41552
41687
  const savedData = await response.json();
41553
- // setPageData(savedData);
41554
- // // Update local widgets state to clear isFirstLoad flag
41555
- // setWidgets(prevWidgets => prevWidgets.map(widget => ({
41556
- // ...widget,
41557
- // config: {
41558
- // ...widget.config,
41559
- // isFirstLoad: false
41560
- // }
41561
- // })));
41688
+ // Update original widget IDs to include newly saved widgets
41689
+ originalWidgetIdsRef.current = currentWidgetIds;
41562
41690
  console.log('Layout saved successfully');
41691
+ // If there are new agent widgets and a filter widget exists,
41692
+ // schedule generation of filtered_sql_query for new widgets
41693
+ // Note: This is done asynchronously after save to not block the save operation
41694
+ // The actual generation will happen after the widget agents have run and created sql_query
41695
+ if (newAgentWidgetIds.length > 0 && hasFilterWidget) {
41696
+ console.log(`[WidgetDashboard] New agent widgets detected: ${newAgentWidgetIds.join(', ')}`);
41697
+ console.log('[WidgetDashboard] Filter widget exists - filtered_sql_query will be generated when filters are applied');
41698
+ // Optionally, we could trigger generation after a delay to allow widget agents to initialize
41699
+ // For now, we rely on the auto-generation in /api/filters/apply when filters are applied
41700
+ // Uncomment below to proactively generate after a delay:
41701
+ /*
41702
+ setTimeout(async () => {
41703
+ try {
41704
+ console.log('[WidgetDashboard] Generating filtered_sql_query for new widgets...');
41705
+ const generateResponse = await fetch(getApiUrl('/api/filters/generate-queries'), {
41706
+ method: 'POST',
41707
+ headers: { 'Content-Type': 'application/json' },
41708
+ body: JSON.stringify({
41709
+ dashboard_id: pageId,
41710
+ widget_ids: newAgentWidgetIds,
41711
+ regenerate: false,
41712
+ }),
41713
+ });
41714
+ if (generateResponse.ok) {
41715
+ const result = await generateResponse.json();
41716
+ console.log('[WidgetDashboard] Generate queries result:', result);
41717
+ }
41718
+ } catch (err) {
41719
+ console.warn('[WidgetDashboard] Could not generate filtered queries for new widgets:', err);
41720
+ }
41721
+ }, 5000); // 5 second delay to allow widget agents to initialize
41722
+ */
41723
+ }
41563
41724
  }
41564
41725
  catch (err) {
41565
41726
  console.error("Save error:", err);
@@ -41839,10 +42000,10 @@ function WidgetDashboard({ pageId, isEditing, selectedWidget = null, onWidgetSel
41839
42000
  onCloseWidgetPallete && onCloseWidgetPallete();
41840
42001
  }, defaultAgentName: defaultAgentName, hasFiltersWidget: widgets.some(w => w.type === 'filters') }), jsxRuntimeExports.jsx(EditWidgetDialog, { editingWidget: editingWidget, setWidgets: setWidgets, initialText: editInitialQuery, isOpen: showEditModal, onClose: () => setShowEditModal(false), onSubmit: handleEditSubmit }), jsxRuntimeExports.jsx("div", { className: "min-h-full", onDragOver: (e) => e.preventDefault(), onDrop: handleDrop, onClick: () => setSelectedWidget(null), children: isLoading ? (jsxRuntimeExports.jsx("div", { className: "flex items-center justify-center h-full", children: jsxRuntimeExports.jsx(Loader2, { className: "h-8 w-8 animate-spin" }) })) : (jsxRuntimeExports.jsx(RGL, { className: "layout m-0 p-0 gap-2", layouts: { lg: getLayoutFromWidgets() }, breakpoints: { lg: 1200, md: 996, sm: 768, xs: 480 }, cols: { lg: 12, md: 8, sm: 6, xs: 2 }, rowHeight: 60, isDraggable: isEditing, isResizable: isEditing, draggableHandle: ".drag-icon", onLayoutChange: handleLayoutChange, compactType: "vertical", containerPadding: [0, 0], margin: [16, 16], children: widgets.map((w) => {
41841
42002
  var _a, _b;
41842
- const hasActiveFilters = Object.keys(activeFilters).length > 0 && Object.values(activeFilters).some(arr => arr.length > 0);
41843
- const shouldShowFilterBadge = hasActiveFilters && w.type !== "filters" && w.type !== "text" && w.type !== "spacer" && w.type !== "divider" && w.type !== "header" && w.type !== "footer";
41844
- return (jsxRuntimeExports.jsxs("div", { className: `${(w.type === "text" || w.type === "spacer") ? `${((_b = (_a = w === null || w === void 0 ? void 0 : w.config) === null || _a === void 0 ? void 0 : _a.content) === null || _b === void 0 ? 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'}`}`, children: [isEditing &&
41845
- jsxRuntimeExports.jsxs("div", { className: `flex items-center justify-end mb-4 relative ${(w.type === "text" || w.type === "spacer") ? "pl-4 pr-4 pt-4" : ""}`, children: [jsxRuntimeExports.jsxs("div", { className: "flex items-center drag-icon cursor-grab absolute left-1/2 -translate-x-1/2", children: [jsxRuntimeExports.jsx(GripHorizontal, { className: "" }), jsxRuntimeExports.jsx(GripHorizontal, { className: "-ml-[3px]" }), jsxRuntimeExports.jsx(GripHorizontal, { className: "-ml-[3px]" })] }), jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 cursor-pointer justify-end", children: [jsxRuntimeExports.jsx(Trash2, { onClick: () => removeWidget(w.id), className: "w-5 h-5 text-red-700" }), (w.type !== "spacer" && w.type !== "chatbot") && jsxRuntimeExports.jsx(Edit, { onClick: () => onClickSettings && onClickSettings(w), className: "w-5 h-5 text-gray-600" })] })] }), !isEditing && shouldShowFilterBadge && (jsxRuntimeExports.jsxs("div", { className: "absolute top-2 right-2 z-10 bg-primary-600 text-white px-2 py-1 rounded-md shadow-md flex items-center gap-1.5 text-xs font-medium", children: [jsxRuntimeExports.jsx(Filter, { className: "w-3 h-3" }), jsxRuntimeExports.jsx("span", { children: "Filtered" })] })), jsxRuntimeExports.jsxs("div", { className: `${((w === null || w === void 0 ? void 0 : w.type) === 'text' || (w === null || w === void 0 ? void 0 : w.type) === 'spacer') ? `${isEditing ? 'px-4' : ''}` : "h-full"} w-full relative`, children: [(w === null || w === void 0 ? void 0 : w.type) === "chatbot" &&
42003
+ const filterStatus = w.type === 'agent' ? getWidgetFilterStatus(w.id) : null;
42004
+ const badgeInfo = filterStatus ? getFilterStatusBadge(filterStatus.status) : null;
42005
+ return (jsxRuntimeExports.jsxs("div", { className: `${(w.type === "text" || w.type === "spacer") ? `${((_b = (_a = w === null || w === void 0 ? void 0 : w.config) === null || _a === void 0 ? void 0 : _a.content) === null || _b === void 0 ? 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: [w.type === 'agent' && badgeInfo && !isApplyingFilters && (jsxRuntimeExports.jsx("div", { className: "absolute top-2 right-2 z-10", title: (filterStatus === null || filterStatus === void 0 ? void 0 : filterStatus.reason) || (filterStatus === null || filterStatus === void 0 ? void 0 : filterStatus.error) || '', children: jsxRuntimeExports.jsxs(Badge, { variant: badgeInfo.variant, className: "text-[10px] px-2 py-0.5 gap-1", children: [jsxRuntimeExports.jsx("span", { children: badgeInfo.icon }), jsxRuntimeExports.jsx("span", { children: badgeInfo.label })] }) })), w.type === 'agent' && isApplyingFilters && (jsxRuntimeExports.jsx("div", { className: "absolute top-2 right-2 z-10", children: jsxRuntimeExports.jsxs(Badge, { variant: "secondary", className: "text-[10px] px-2 py-0.5 gap-1 animate-pulse", children: [jsxRuntimeExports.jsx(Loader2, { className: "w-3 h-3 animate-spin" }), jsxRuntimeExports.jsx("span", { children: "Filtering..." })] }) })), isEditing &&
42006
+ jsxRuntimeExports.jsxs("div", { className: `flex items-center justify-end mb-4 relative ${(w.type === "text" || w.type === "spacer") ? "pl-4 pr-4 pt-4" : ""}`, children: [jsxRuntimeExports.jsxs("div", { className: "flex items-center drag-icon cursor-grab absolute left-1/2 -translate-x-1/2", children: [jsxRuntimeExports.jsx(GripHorizontal, { className: "" }), jsxRuntimeExports.jsx(GripHorizontal, { className: "-ml-[3px]" }), jsxRuntimeExports.jsx(GripHorizontal, { className: "-ml-[3px]" })] }), jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 cursor-pointer justify-end", children: [jsxRuntimeExports.jsx(Trash2, { onClick: () => removeWidget(w.id), className: "w-5 h-5 text-red-700" }), (w.type !== "spacer" && w.type !== "chatbot") && jsxRuntimeExports.jsx(Edit, { onClick: () => onClickSettings && onClickSettings(w), className: "w-5 h-5 text-gray-600" })] })] }), jsxRuntimeExports.jsxs("div", { className: `${((w === null || w === void 0 ? void 0 : w.type) === 'text' || (w === null || w === void 0 ? void 0 : w.type) === 'spacer') ? `${isEditing ? 'px-4' : ''}` : "h-full"} w-full relative`, children: [(w === null || w === void 0 ? void 0 : w.type) === "chatbot" &&
41846
42007
  jsxRuntimeExports.jsxs("div", { className: "relative z-50", children: [jsxRuntimeExports.jsx("div", { onClick: () => handleClearChat(w === null || w === void 0 ? void 0 : w.id), onMouseOver: () => setVisibleClearButton(w === null || w === void 0 ? 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: jsxRuntimeExports.jsx(MessageCircleX, { className: "w-5 h-5" }) }), 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 || w === void 0 ? void 0 : w.id) ? "block" : "hidden"}`, children: "Clear Chat" })] }), jsxRuntimeExports.jsx(WidgetRenderer, { widget: w, widgetBackendUrl: widgetBackendUrl, onResetReady: handleResetReady, widgetIds: widgets.filter(widget => widget.type !== 'chatbot').map(widget => widget.id), datasetId: datasetId, pageId: pageId, onApplyFilters: onApplyFilters, isEditing: isEditing })] })] }, w.id));
41847
42008
  }) })) })] }));
41848
42009
  }
@@ -44740,7 +44901,6 @@ function DashboardPages({ widgetBackendUrl }) {
44740
44901
  const [pages, setPages] = useState([]);
44741
44902
  const [isLoading, setIsLoading] = useState(true);
44742
44903
  const [error, setError] = useState(null);
44743
- const [activeFilters, setActiveFilters] = useState({});
44744
44904
  // Helper function to get API URL
44745
44905
  const getApiUrl = (endpoint) => {
44746
44906
  const baseUrl = widgetBackendUrl || '';
@@ -44749,10 +44909,6 @@ function DashboardPages({ widgetBackendUrl }) {
44749
44909
  useEffect(() => {
44750
44910
  loadPages();
44751
44911
  }, []);
44752
- const handleApplyFilters = (pageId) => (filters) => {
44753
- console.log('Filters applied for page:', pageId, filters);
44754
- setActiveFilters(prev => (Object.assign(Object.assign({}, prev), { [pageId]: filters })));
44755
- };
44756
44912
  const loadPages = async () => {
44757
44913
  try {
44758
44914
  setIsLoading(true);
@@ -44797,7 +44953,7 @@ function DashboardPages({ widgetBackendUrl }) {
44797
44953
  }
44798
44954
  return (jsxRuntimeExports.jsxs("div", { className: "container mx-auto p-6 h-screen flex flex-col", children: [jsxRuntimeExports.jsxs("div", { className: "mb-6", children: [jsxRuntimeExports.jsx("h1", { className: "text-2xl font-bold", children: "Dashboard Pages" }), jsxRuntimeExports.jsx("p", { className: "text-muted-foreground", children: "View all your saved pages in dashboard mode" })] }), jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-hidden", children: jsxRuntimeExports.jsxs(Tabs, { defaultValue: (_a = pages[0]) === null || _a === void 0 ? void 0 : _a.id, className: "h-full flex flex-col", children: [jsxRuntimeExports.jsx(TabsList, { className: "grid w-full grid-cols-auto gap-1 mb-4", style: {
44799
44955
  gridTemplateColumns: `repeat(${pages.length}, minmax(120px, 1fr))`
44800
- }, children: pages.map((page) => (jsxRuntimeExports.jsx(TabsTrigger, { value: page.id, className: "truncate px-3 py-2 data-[state=active]:bg-primary data-[state=active]:text-primary-foreground data-[state=active]:shadow-lg border-2 data-[state=active]:border-primary data-[state=inactive]:border-transparent hover:bg-accent hover:text-accent-foreground transition-all duration-200", title: page.title, children: page.title }, page.id))) }), pages.map((page) => (jsxRuntimeExports.jsx(TabsContent, { value: page.id, className: "flex-1 overflow-hidden m-0", children: jsxRuntimeExports.jsx("div", { className: "h-full border rounded-lg overflow-hidden", children: jsxRuntimeExports.jsx(WidgetDashboard, { pageId: page.id, isEditing: false, widgetBackendUrl: widgetBackendUrl, onApplyFilters: handleApplyFilters(page.id), activeFilters: activeFilters[page.id] || {} }) }) }, page.id)))] }) })] }));
44956
+ }, children: pages.map((page) => (jsxRuntimeExports.jsx(TabsTrigger, { value: page.id, className: "truncate px-3 py-2 data-[state=active]:bg-primary data-[state=active]:text-primary-foreground data-[state=active]:shadow-lg border-2 data-[state=active]:border-primary data-[state=inactive]:border-transparent hover:bg-accent hover:text-accent-foreground transition-all duration-200", title: page.title, children: page.title }, page.id))) }), pages.map((page) => (jsxRuntimeExports.jsx(TabsContent, { value: page.id, className: "flex-1 overflow-hidden m-0", children: jsxRuntimeExports.jsx("div", { className: "h-full border rounded-lg overflow-hidden", children: jsxRuntimeExports.jsx(WidgetDashboard, { pageId: page.id, isEditing: false, widgetBackendUrl: widgetBackendUrl }) }) }, page.id)))] }) })] }));
44801
44957
  }
44802
44958
 
44803
44959
  export { Button, DashboardPages, Input, SavedPages, WidgetDashboard, WidgetPalette, WidgetSettingsPanel, cn };
package/dist/index.js CHANGED
@@ -2013,6 +2013,29 @@ function WidgetPalette({ widgetBackendUrl } = {}) {
2013
2013
  }, className: "cursor-move hover:bg-accent transition-colors", children: jsxRuntimeExports.jsx(CardContent, { className: "p-3", children: jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [React.createElement(widget.icon, {}), jsxRuntimeExports.jsxs("div", { children: [jsxRuntimeExports.jsx("h4", { className: "font-medium text-sm", children: widget.name }), jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: widget.description })] })] }) }) }, widget.type))) }) })] }));
2014
2014
  }
2015
2015
 
2016
+ const badgeVariants = classVarianceAuthority.cva("inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", {
2017
+ variants: {
2018
+ variant: {
2019
+ default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
2020
+ secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
2021
+ destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
2022
+ outline: "text-foreground",
2023
+ // Filter status variants
2024
+ success: "border-transparent bg-green-100 text-green-800 hover:bg-green-200",
2025
+ warning: "border-transparent bg-amber-100 text-amber-800 hover:bg-amber-200",
2026
+ muted: "border-transparent bg-gray-100 text-gray-600 hover:bg-gray-200",
2027
+ error: "border-transparent bg-red-100 text-red-800 hover:bg-red-200",
2028
+ },
2029
+ },
2030
+ defaultVariants: {
2031
+ variant: "default",
2032
+ },
2033
+ });
2034
+ function Badge(_a) {
2035
+ var { className, variant } = _a, props = __rest(_a, ["className", "variant"]);
2036
+ return (jsxRuntimeExports.jsx("div", Object.assign({ className: cn(badgeVariants({ variant }), className) }, props)));
2037
+ }
2038
+
2016
2039
  const borderRadiusMap$2 = {
2017
2040
  none: "0px",
2018
2041
  rounded: "0.375rem",
@@ -39354,6 +39377,10 @@ const parseAndUpdateFilterState = (apiResponse, setFilterState) => {
39354
39377
  if (filterData.agent_message) {
39355
39378
  newFilterState.agent_message = filterData.agent_message;
39356
39379
  }
39380
+ // Handle error key as a fallback for agent_message
39381
+ if (filterData.error && !newFilterState.agent_message) {
39382
+ newFilterState.agent_message = filterData.error;
39383
+ }
39357
39384
  // Handle confirmation state
39358
39385
  if (filterData.status) {
39359
39386
  newFilterState.status = filterData.status;
@@ -39409,15 +39436,17 @@ function TableSelectionConfirmation({ ambiguousColumns, resolvedColumns, message
39409
39436
  onConfirm(selectedTables);
39410
39437
  };
39411
39438
  const allSelected = ambiguousColumns.every((col) => selectedTables[col.column]);
39412
- return (jsxRuntimeExports.jsxs("div", { className: "flex flex-col h-full p-4 bg-white", children: [jsxRuntimeExports.jsxs("div", { className: "mb-4", children: [jsxRuntimeExports.jsx("h3", { className: "text-lg font-semibold text-gray-800 mb-2", children: "Table Selection Required" }), message && (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600 mb-4 whitespace-pre-line", children: message }))] }), resolvedColumns && resolvedColumns.length > 0 && (jsxRuntimeExports.jsxs("div", { className: "mb-4 p-3 bg-green-50 border border-green-200 rounded-lg", children: [jsxRuntimeExports.jsx("p", { className: "text-sm font-medium text-green-800 mb-2", children: "Already Resolved Columns:" }), jsxRuntimeExports.jsx("div", { className: "space-y-1", children: resolvedColumns.map((col) => (jsxRuntimeExports.jsxs("div", { className: "text-sm text-green-700", children: [jsxRuntimeExports.jsx("span", { className: "font-medium", children: col.column }), " \u2192 ", jsxRuntimeExports.jsx("span", { className: "text-green-600", children: col.table_name })] }, col.column))) })] })), jsxRuntimeExports.jsx(ScrollArea, { className: "flex-1 mb-4", children: jsxRuntimeExports.jsx("div", { className: "space-y-4", children: ambiguousColumns.map((col) => (jsxRuntimeExports.jsxs("div", { className: "border border-gray-200 rounded-lg p-3", children: [jsxRuntimeExports.jsx("label", { className: "block mb-2", children: jsxRuntimeExports.jsxs("span", { className: "text-sm font-medium text-gray-700", children: ["Column: ", jsxRuntimeExports.jsx("span", { className: "text-primary-600", children: col.column })] }) }), jsxRuntimeExports.jsxs("select", { value: selectedTables[col.column] || '', onChange: (e) => {
39439
+ return (jsxRuntimeExports.jsxs("div", { className: "flex flex-col h-full p-4 bg-white overflow-hidden", children: [jsxRuntimeExports.jsxs("div", { className: "mb-4 flex-shrink-0", children: [jsxRuntimeExports.jsx("h3", { className: "text-lg font-semibold text-gray-800 mb-2", children: "Table Selection Required" }), message && (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-600 mb-4 whitespace-pre-line", children: message }))] }), resolvedColumns && resolvedColumns.length > 0 && (jsxRuntimeExports.jsxs("div", { className: "mb-4 p-3 bg-green-50 border border-green-200 rounded-lg flex-shrink-0", children: [jsxRuntimeExports.jsx("p", { className: "text-sm font-medium text-green-800 mb-2", children: "Already Resolved Columns:" }), jsxRuntimeExports.jsx("div", { className: "space-y-1", children: resolvedColumns.map((col) => (jsxRuntimeExports.jsxs("div", { className: "text-sm text-green-700", children: [jsxRuntimeExports.jsx("span", { className: "font-medium", children: col.column }), " \u2192 ", jsxRuntimeExports.jsx("span", { className: "text-green-600", children: col.table_name })] }, col.column))) })] })), jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-y-auto mb-4 min-h-0 scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-transparent", children: jsxRuntimeExports.jsx("div", { className: "space-y-4", children: ambiguousColumns.map((col) => (jsxRuntimeExports.jsxs("div", { className: "border border-gray-200 rounded-lg p-3", children: [jsxRuntimeExports.jsx("label", { className: "block mb-2", children: jsxRuntimeExports.jsxs("span", { className: "text-sm font-medium text-gray-700", children: ["Column: ", jsxRuntimeExports.jsx("span", { className: "text-primary-600", children: col.column })] }) }), jsxRuntimeExports.jsxs("select", { value: selectedTables[col.column] || '', onChange: (e) => {
39413
39440
  setSelectedTables((prev) => (Object.assign(Object.assign({}, prev), { [col.column]: e.target.value })));
39414
- }, className: "w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500", children: [jsxRuntimeExports.jsx("option", { value: "", children: "Select a table..." }), col.tables.map((table) => (jsxRuntimeExports.jsx("option", { value: table, children: table }, table)))] })] }, col.column))) }) }), jsxRuntimeExports.jsxs("div", { className: "flex gap-2 pt-3 border-t border-gray-200", children: [jsxRuntimeExports.jsx(Button, { onClick: onCancel, variant: "outline", className: "flex-1", children: "Cancel" }), jsxRuntimeExports.jsx(Button, { onClick: handleSubmit, disabled: !allSelected, className: "flex-1 bg-primary-600 hover:bg-primary-700 text-white disabled:bg-gray-400", children: "Confirm Selection" })] })] }));
39441
+ }, className: "w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500", children: [jsxRuntimeExports.jsx("option", { value: "", children: "Select a table..." }), col.tables.map((table) => (jsxRuntimeExports.jsx("option", { value: table, children: table }, table)))] })] }, col.column))) }) }), jsxRuntimeExports.jsxs("div", { className: "flex gap-2 pt-3 border-t border-gray-200 flex-shrink-0", children: [jsxRuntimeExports.jsx(Button, { onClick: onCancel, variant: "outline", className: "flex-1", children: "Cancel" }), jsxRuntimeExports.jsx(Button, { onClick: handleSubmit, disabled: !allSelected, className: "flex-1 bg-primary-600 hover:bg-primary-700 text-white disabled:bg-gray-400", children: "Confirm Selection" })] })] }));
39415
39442
  }
39416
39443
  function FiltersContent({ filterGroups, showHeader, onFilterChange, widget, appendMessage, query, isFirstLoad, widgetBackendUrl, widgetId, startLoadingTimeout, clearLoadingTimeout, filterState, onApplyFilters, isEditing = false, }) {
39444
+ var _a;
39417
39445
  const hasCalledRef = React.useRef(false);
39418
39446
  const [expandedGroups, setExpandedGroups] = React.useState({});
39419
39447
  const [selectedFilters, setSelectedFilters] = React.useState({});
39420
39448
  const [searchQueries, setSearchQueries] = React.useState({});
39449
+ const [isSubmitting, setIsSubmitting] = React.useState(false);
39421
39450
  const isEmpty = filterGroups.length === 0;
39422
39451
  const handleRefresh = async () => {
39423
39452
  if (query) {
@@ -39455,6 +39484,29 @@ function FiltersContent({ filterGroups, showHeader, onFilterChange, widget, appe
39455
39484
  });
39456
39485
  setExpandedGroups(initialExpanded);
39457
39486
  }, [filterGroups]);
39487
+ // Initialize selected filters from widget.config.filters
39488
+ React.useEffect(() => {
39489
+ var _a;
39490
+ if (((_a = widget.config) === null || _a === void 0 ? void 0 : _a.filters) && filterGroups.length > 0) {
39491
+ const initialSelectedFilters = {};
39492
+ Object.entries(widget.config.filters).forEach(([groupId, labels]) => {
39493
+ const group = filterGroups.find(g => g.id === groupId);
39494
+ if (group && Array.isArray(labels)) {
39495
+ // Convert labels back to option IDs
39496
+ const optionIds = labels
39497
+ .map(label => {
39498
+ const option = group.options.find(opt => opt.label === label);
39499
+ return option === null || option === void 0 ? void 0 : option.id;
39500
+ })
39501
+ .filter((id) => id !== undefined);
39502
+ if (optionIds.length > 0) {
39503
+ initialSelectedFilters[groupId] = optionIds;
39504
+ }
39505
+ }
39506
+ });
39507
+ setSelectedFilters(initialSelectedFilters);
39508
+ }
39509
+ }, [(_a = widget.config) === null || _a === void 0 ? void 0 : _a.filters, filterGroups]);
39458
39510
  const toggleGroup = (groupId) => {
39459
39511
  setExpandedGroups((prev) => (Object.assign(Object.assign({}, prev), { [groupId]: !prev[groupId] })));
39460
39512
  };
@@ -39501,24 +39553,24 @@ function FiltersContent({ filterGroups, showHeader, onFilterChange, widget, appe
39501
39553
  if (isEmpty) {
39502
39554
  return createLoadingComponent$4();
39503
39555
  }
39504
- return (jsxRuntimeExports.jsxs("div", { className: "h-full flex flex-col bg-white rounded-lg", children: [showHeader && (jsxRuntimeExports.jsx("div", { className: "px-4 py-3 border-b border-gray-100", children: jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntimeExports.jsx(lucideReact.Bot, { className: "h-4 w-4" }), jsxRuntimeExports.jsx("h3", { className: "font-semibold text-gray-800", children: "Filters" })] }) })), jsxRuntimeExports.jsx(ScrollArea, { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: "p-2", children: filterGroups.map((group) => {
39556
+ return (jsxRuntimeExports.jsxs("div", { className: "h-full flex flex-col bg-white rounded-lg overflow-hidden", children: [showHeader && (jsxRuntimeExports.jsx("div", { className: "px-4 py-3 border-b border-gray-100 flex-shrink-0", children: jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntimeExports.jsx(lucideReact.Bot, { className: "h-4 w-4" }), jsxRuntimeExports.jsx("h3", { className: "font-semibold text-gray-800", children: "Filters" })] }) })), jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-y-auto min-h-0 scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-transparent", children: jsxRuntimeExports.jsx("div", { className: "p-2", children: filterGroups.map((group) => {
39505
39557
  const isExpanded = expandedGroups[group.id];
39506
39558
  const selectedCount = getSelectedCount(group.id);
39507
39559
  const filteredOptions = getFilteredOptions(group);
39508
39560
  return (jsxRuntimeExports.jsxs("div", { className: "mb-2 border border-gray-100 rounded-lg overflow-hidden", children: [jsxRuntimeExports.jsxs("button", { onClick: () => toggleGroup(group.id), className: cn("w-full flex items-center justify-between px-3 py-2.5", "bg-gray-50 hover:bg-gray-100 transition-colors", "focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-inset"), children: [jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [isExpanded ? (jsxRuntimeExports.jsx(lucideReact.ChevronDown, { className: "w-4 h-4 text-primary-600" })) : (jsxRuntimeExports.jsx(lucideReact.ChevronRight, { className: "w-4 h-4 text-gray-500" })), jsxRuntimeExports.jsx("span", { className: "font-medium text-sm text-gray-700", children: group.name }), selectedCount > 0 && (jsxRuntimeExports.jsx("span", { className: "ml-1 px-1.5 py-0.5 text-xs font-medium bg-primary-100 text-primary-700 rounded-full", children: selectedCount }))] }), selectedCount > 0 && (jsxRuntimeExports.jsx("button", { onClick: (e) => {
39509
39561
  e.stopPropagation();
39510
39562
  clearGroupFilters(group.id);
39511
- }, className: "text-xs text-primary-600 hover:text-primary-800 hover:underline", children: "Clear" }))] }), jsxRuntimeExports.jsx("div", { className: cn("overflow-hidden transition-all duration-200 ease-in-out", isExpanded ? "max-h-[400px]" : "max-h-0"), children: jsxRuntimeExports.jsxs("div", { className: "px-3 py-2 bg-white", children: [group.options.length > 5 && (jsxRuntimeExports.jsxs("div", { className: "relative mb-2", children: [jsxRuntimeExports.jsx(lucideReact.Search, { className: "absolute left-2.5 top-1/2 transform -translate-y-1/2 w-3.5 h-3.5 text-gray-400" }), jsxRuntimeExports.jsx(Input, { type: "text", placeholder: `Search ${group.name.toLowerCase()}...`, value: searchQueries[group.id] || "", onChange: (e) => handleSearchChange(group.id, e.target.value), className: "pl-8 h-8 text-sm border-gray-200 focus:border-primary-400 focus:ring-primary-400" })] })), jsxRuntimeExports.jsx("div", { className: "space-y-1 max-h-[250px] overflow-y-auto pr-1", children: filteredOptions.length === 0 ? (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500 py-2 text-center", children: "No options found" })) : (filteredOptions.map((option) => {
39563
+ }, className: "text-xs text-primary-600 hover:text-primary-800 hover:underline", children: "Clear" }))] }), jsxRuntimeExports.jsx("div", { className: cn("overflow-hidden transition-all duration-200 ease-in-out", isExpanded ? "max-h-[500px]" : "max-h-0"), children: jsxRuntimeExports.jsxs("div", { className: "px-3 py-2 bg-white", children: [group.options.length > 5 && (jsxRuntimeExports.jsxs("div", { className: "relative mb-2", children: [jsxRuntimeExports.jsx(lucideReact.Search, { className: "absolute left-2.5 top-1/2 transform -translate-y-1/2 w-3.5 h-3.5 text-gray-400" }), jsxRuntimeExports.jsx(Input, { type: "text", placeholder: `Search ${group.name.toLowerCase()}...`, value: searchQueries[group.id] || "", onChange: (e) => handleSearchChange(group.id, e.target.value), className: "pl-8 h-8 text-sm border-gray-200 focus:border-primary-400 focus:ring-primary-400" })] })), jsxRuntimeExports.jsx("div", { className: "space-y-1 max-h-[350px] overflow-y-auto pr-1 scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-transparent", children: filteredOptions.length === 0 ? (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500 py-2 text-center", children: "No options found" })) : (filteredOptions.map((option) => {
39512
39564
  var _a;
39513
39565
  const isChecked = ((_a = selectedFilters[group.id]) === null || _a === void 0 ? void 0 : _a.includes(option.id)) || false;
39514
39566
  return (jsxRuntimeExports.jsxs("label", { className: cn("flex items-center gap-2.5 px-2 py-1.5 rounded-md cursor-pointer", "hover:bg-gray-50 transition-colors", isChecked && "bg-primary-50"), children: [jsxRuntimeExports.jsx(Checkbox, { checked: isChecked, onCheckedChange: (checked) => handleCheckboxChange(group.id, option.id, checked), className: cn("h-4 w-4 rounded border-gray-300", "data-[state=checked]:bg-primary-600 data-[state=checked]:border-primary-600") }), jsxRuntimeExports.jsx("span", { className: cn("text-sm flex-1 truncate", isChecked ? "text-primary-700 font-medium" : "text-gray-600"), title: option.label, children: option.label }), option.count !== undefined && (jsxRuntimeExports.jsxs("span", { className: "text-xs text-gray-400", children: ["(", option.count, ")"] }))] }, option.id));
39515
39567
  })) })] }) })] }, group.id));
39516
- }) }) }), jsxRuntimeExports.jsx("div", { className: "px-4 py-2 border-t border-gray-100 bg-gray-50", children: jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [Object.values(selectedFilters).some((arr) => arr.length > 0) ? (jsxRuntimeExports.jsx("button", { onClick: () => {
39568
+ }) }) }), jsxRuntimeExports.jsx("div", { className: "px-4 py-2 border-t border-gray-100 bg-gray-50 flex-shrink-0", children: jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [Object.values(selectedFilters).some((arr) => arr.length > 0) ? (jsxRuntimeExports.jsx("button", { onClick: () => {
39517
39569
  setSelectedFilters({});
39518
39570
  if (onFilterChange) {
39519
39571
  onFilterChange({});
39520
39572
  }
39521
- }, className: "py-1.5 text-sm font-medium text-primary-600 hover:text-primary-800 hover:bg-primary-50 rounded-md transition-colors px-2", children: "Clear All Filters" })) : (jsxRuntimeExports.jsx("div", {})), jsxRuntimeExports.jsx(Button, { onClick: () => {
39573
+ }, className: "py-1.5 text-sm font-medium text-primary-600 hover:text-primary-800 hover:bg-primary-50 rounded-md transition-colors px-2", children: "Clear All Filters" })) : (jsxRuntimeExports.jsx("div", {})), jsxRuntimeExports.jsx(Button, { onClick: async () => {
39522
39574
  // Convert option IDs back to original label values
39523
39575
  const filtersWithLabels = {};
39524
39576
  filterGroups.forEach((group) => {
@@ -39528,8 +39580,37 @@ function FiltersContent({ filterGroups, showHeader, onFilterChange, widget, appe
39528
39580
  return (option === null || option === void 0 ? void 0 : option.label) || optionId;
39529
39581
  });
39530
39582
  });
39531
- onApplyFilters === null || onApplyFilters === void 0 ? void 0 : onApplyFilters(filtersWithLabels);
39532
- }, disabled: isEditing, className: `${isEditing ? 'bg-gray-400 cursor-not-allowed' : 'bg-primary-600 hover:bg-primary-700'} text-white`, title: isEditing ? 'Save the layout first to apply filters' : '', children: "Apply Filters" })] }) })] }));
39583
+ // Call API to update widget config with filters
39584
+ if (widgetBackendUrl && widgetId) {
39585
+ setIsSubmitting(true);
39586
+ try {
39587
+ const response = await fetch(`${widgetBackendUrl}/api/widgets/${widgetId}/config`, {
39588
+ method: 'PATCH',
39589
+ headers: {
39590
+ 'Content-Type': 'application/json',
39591
+ },
39592
+ body: JSON.stringify({
39593
+ config: Object.assign(Object.assign({}, widget.config), { filters: filtersWithLabels }),
39594
+ }),
39595
+ });
39596
+ if (!response.ok) {
39597
+ throw new Error('Failed to update widget config');
39598
+ }
39599
+ // Call the onApplyFilters callback after successful API call
39600
+ onApplyFilters === null || onApplyFilters === void 0 ? void 0 : onApplyFilters(filtersWithLabels);
39601
+ }
39602
+ catch (error) {
39603
+ console.error('Error updating widget config:', error);
39604
+ }
39605
+ finally {
39606
+ setIsSubmitting(false);
39607
+ }
39608
+ }
39609
+ else {
39610
+ // Fallback if no backend URL - just call the callback
39611
+ onApplyFilters === null || onApplyFilters === void 0 ? void 0 : onApplyFilters(filtersWithLabels);
39612
+ }
39613
+ }, disabled: isEditing || isSubmitting, className: `${isEditing || isSubmitting ? 'bg-gray-400 cursor-not-allowed' : 'bg-primary-600 hover:bg-primary-700'} text-white`, title: isEditing ? 'Save the layout first to apply filters' : '', children: isSubmitting ? (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx(lucideReact.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Applying..."] })) : ('Apply Filters') })] }) })] }));
39533
39614
  }
39534
39615
  function CopilotKitFilters({ widget, showHeader, onFilterChange, onResetReady, widgetBackendUrl, datasetId, onApplyFilters, isEditing = false, }) {
39535
39616
  var _a, _b, _c, _d, _e, _f, _g, _h;
@@ -41383,7 +41464,7 @@ const IconMap = {
41383
41464
  'pie-chart': lucideReact.PieChart,
41384
41465
  'chatbot': lucideReact.Bot,
41385
41466
  };
41386
- function WidgetDashboard({ pageId, isEditing, selectedWidget = null, onWidgetSelect, refreshKey, widgetBackendUrl, onSaveLayoutReady, openWidgetPallete = false, onCloseWidgetPallete, defaultAgentName = "adk-construction-project-agent", userId, onApplyFilters, activeFilters = {} }) {
41467
+ function WidgetDashboard({ pageId, isEditing, selectedWidget = null, onWidgetSelect, refreshKey, widgetBackendUrl, onSaveLayoutReady, openWidgetPallete = false, onCloseWidgetPallete, defaultAgentName = "adk-construction-project-agent", userId, onApplyFilters, filterResults, isApplyingFilters = false }) {
41387
41468
  const [widgets, setWidgets] = React.useState([]);
41388
41469
  const [datasetId, setDatasetId] = React.useState('');
41389
41470
  const [availableWidgets, setAvailableWidgets] = React.useState([]);
@@ -41398,6 +41479,31 @@ function WidgetDashboard({ pageId, isEditing, selectedWidget = null, onWidgetSel
41398
41479
  const [editInitialQuery, setEditInitialQuery] = React.useState("");
41399
41480
  const [editingWidget, setEditingWidget] = React.useState(null);
41400
41481
  const [widgetResetFunctions, setWidgetResetFunctions] = React.useState(new Map());
41482
+ // Helper to get filter status for a widget
41483
+ const getWidgetFilterStatus = React.useCallback((widgetId) => {
41484
+ if (!filterResults)
41485
+ return null;
41486
+ // Only show status for agent widgets
41487
+ const result = filterResults.results.find(r => r.widgetId === widgetId);
41488
+ if (!result)
41489
+ return null;
41490
+ return result;
41491
+ }, [filterResults]);
41492
+ // Helper to get badge variant based on filter status
41493
+ const getFilterStatusBadge = (status) => {
41494
+ switch (status) {
41495
+ case "updated":
41496
+ return { variant: "success", label: "Filtered", icon: "✓" };
41497
+ case "no_data":
41498
+ return { variant: "warning", label: "No Data", icon: "⚠" };
41499
+ case "skipped":
41500
+ return { variant: "muted", label: "Skipped", icon: "−" };
41501
+ case "failed":
41502
+ return { variant: "error", label: "Failed", icon: "✕" };
41503
+ default:
41504
+ return { variant: "muted", label: status, icon: "?" };
41505
+ }
41506
+ };
41401
41507
  // Use external selectedWidget if provided, otherwise use internal state
41402
41508
  const currentSelectedWidget = selectedWidget !== undefined ? selectedWidget : internalSelectedWidget;
41403
41509
  const setSelectedWidget = onWidgetSelect || setInternalSelectedWidget;
@@ -41462,6 +41568,19 @@ function WidgetDashboard({ pageId, isEditing, selectedWidget = null, onWidgetSel
41462
41568
  }
41463
41569
  });
41464
41570
  window.dispatchEvent(event);
41571
+ // If filters have been applied (filter widget exists), regenerate filtered_sql_query for this widget
41572
+ // This handles the case where a widget's query is changed after filters have been applied
41573
+ const hasFilterWidget = widgets.some(w => w.type === 'filters');
41574
+ if (hasFilterWidget && editingWidget.type === 'agent' && widgetBackendUrl) {
41575
+ // Dispatch event to notify that filtered query needs regeneration for this widget
41576
+ const regenerateEvent = new CustomEvent('regenerateFilteredQuery', {
41577
+ detail: {
41578
+ widgetId: editingWidget.id,
41579
+ dashboardId: pageId
41580
+ }
41581
+ });
41582
+ window.dispatchEvent(regenerateEvent);
41583
+ }
41465
41584
  }
41466
41585
  };
41467
41586
  const handleResetReady = React.useCallback((widgetId, resetFn) => {
@@ -41553,6 +41672,15 @@ function WidgetDashboard({ pageId, isEditing, selectedWidget = null, onWidgetSel
41553
41672
  };
41554
41673
  loadWidgetTypes();
41555
41674
  }, []);
41675
+ // Track original widget IDs from initial load
41676
+ const originalWidgetIdsRef = React.useRef(new Set());
41677
+ // Update original widget IDs when widgets are loaded from server
41678
+ React.useEffect(() => {
41679
+ if (pageData === null || pageData === void 0 ? void 0 : pageData.widgets) {
41680
+ const ids = new Set(pageData.widgets.map((w) => w.id));
41681
+ originalWidgetIdsRef.current = ids;
41682
+ }
41683
+ }, [pageData]);
41556
41684
  // Use ref to store the latest save function without causing re-renders
41557
41685
  const saveLayoutRef = React.useRef();
41558
41686
  // Update the ref whenever dependencies change
@@ -41560,6 +41688,13 @@ function WidgetDashboard({ pageId, isEditing, selectedWidget = null, onWidgetSel
41560
41688
  if (!pageData) {
41561
41689
  throw new Error("No page data available");
41562
41690
  }
41691
+ // Identify new agent widgets (added since last load/save)
41692
+ const currentWidgetIds = new Set(widgets.map(w => w.id));
41693
+ const newAgentWidgetIds = widgets
41694
+ .filter(w => w.type === 'agent' && !originalWidgetIdsRef.current.has(w.id))
41695
+ .map(w => w.id);
41696
+ // Check if there's a filter widget on this dashboard
41697
+ const hasFilterWidget = widgets.some(w => w.type === 'filters');
41563
41698
  try {
41564
41699
  const response = await fetch(getApiUrl(`/api/pages/${pageId}`), {
41565
41700
  method: "PUT",
@@ -41577,16 +41712,42 @@ function WidgetDashboard({ pageId, isEditing, selectedWidget = null, onWidgetSel
41577
41712
  throw new Error("Failed to save layout");
41578
41713
  }
41579
41714
  const savedData = await response.json();
41580
- // setPageData(savedData);
41581
- // // Update local widgets state to clear isFirstLoad flag
41582
- // setWidgets(prevWidgets => prevWidgets.map(widget => ({
41583
- // ...widget,
41584
- // config: {
41585
- // ...widget.config,
41586
- // isFirstLoad: false
41587
- // }
41588
- // })));
41715
+ // Update original widget IDs to include newly saved widgets
41716
+ originalWidgetIdsRef.current = currentWidgetIds;
41589
41717
  console.log('Layout saved successfully');
41718
+ // If there are new agent widgets and a filter widget exists,
41719
+ // schedule generation of filtered_sql_query for new widgets
41720
+ // Note: This is done asynchronously after save to not block the save operation
41721
+ // The actual generation will happen after the widget agents have run and created sql_query
41722
+ if (newAgentWidgetIds.length > 0 && hasFilterWidget) {
41723
+ console.log(`[WidgetDashboard] New agent widgets detected: ${newAgentWidgetIds.join(', ')}`);
41724
+ console.log('[WidgetDashboard] Filter widget exists - filtered_sql_query will be generated when filters are applied');
41725
+ // Optionally, we could trigger generation after a delay to allow widget agents to initialize
41726
+ // For now, we rely on the auto-generation in /api/filters/apply when filters are applied
41727
+ // Uncomment below to proactively generate after a delay:
41728
+ /*
41729
+ setTimeout(async () => {
41730
+ try {
41731
+ console.log('[WidgetDashboard] Generating filtered_sql_query for new widgets...');
41732
+ const generateResponse = await fetch(getApiUrl('/api/filters/generate-queries'), {
41733
+ method: 'POST',
41734
+ headers: { 'Content-Type': 'application/json' },
41735
+ body: JSON.stringify({
41736
+ dashboard_id: pageId,
41737
+ widget_ids: newAgentWidgetIds,
41738
+ regenerate: false,
41739
+ }),
41740
+ });
41741
+ if (generateResponse.ok) {
41742
+ const result = await generateResponse.json();
41743
+ console.log('[WidgetDashboard] Generate queries result:', result);
41744
+ }
41745
+ } catch (err) {
41746
+ console.warn('[WidgetDashboard] Could not generate filtered queries for new widgets:', err);
41747
+ }
41748
+ }, 5000); // 5 second delay to allow widget agents to initialize
41749
+ */
41750
+ }
41590
41751
  }
41591
41752
  catch (err) {
41592
41753
  console.error("Save error:", err);
@@ -41866,10 +42027,10 @@ function WidgetDashboard({ pageId, isEditing, selectedWidget = null, onWidgetSel
41866
42027
  onCloseWidgetPallete && onCloseWidgetPallete();
41867
42028
  }, defaultAgentName: defaultAgentName, hasFiltersWidget: widgets.some(w => w.type === 'filters') }), jsxRuntimeExports.jsx(EditWidgetDialog, { editingWidget: editingWidget, setWidgets: setWidgets, initialText: editInitialQuery, isOpen: showEditModal, onClose: () => setShowEditModal(false), onSubmit: handleEditSubmit }), jsxRuntimeExports.jsx("div", { className: "min-h-full", onDragOver: (e) => e.preventDefault(), onDrop: handleDrop, onClick: () => setSelectedWidget(null), children: isLoading ? (jsxRuntimeExports.jsx("div", { className: "flex items-center justify-center h-full", children: jsxRuntimeExports.jsx(lucideReact.Loader2, { className: "h-8 w-8 animate-spin" }) })) : (jsxRuntimeExports.jsx(RGL, { className: "layout m-0 p-0 gap-2", layouts: { lg: getLayoutFromWidgets() }, breakpoints: { lg: 1200, md: 996, sm: 768, xs: 480 }, cols: { lg: 12, md: 8, sm: 6, xs: 2 }, rowHeight: 60, isDraggable: isEditing, isResizable: isEditing, draggableHandle: ".drag-icon", onLayoutChange: handleLayoutChange, compactType: "vertical", containerPadding: [0, 0], margin: [16, 16], children: widgets.map((w) => {
41868
42029
  var _a, _b;
41869
- const hasActiveFilters = Object.keys(activeFilters).length > 0 && Object.values(activeFilters).some(arr => arr.length > 0);
41870
- const shouldShowFilterBadge = hasActiveFilters && w.type !== "filters" && w.type !== "text" && w.type !== "spacer" && w.type !== "divider" && w.type !== "header" && w.type !== "footer";
41871
- return (jsxRuntimeExports.jsxs("div", { className: `${(w.type === "text" || w.type === "spacer") ? `${((_b = (_a = w === null || w === void 0 ? void 0 : w.config) === null || _a === void 0 ? void 0 : _a.content) === null || _b === void 0 ? 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'}`}`, children: [isEditing &&
41872
- jsxRuntimeExports.jsxs("div", { className: `flex items-center justify-end mb-4 relative ${(w.type === "text" || w.type === "spacer") ? "pl-4 pr-4 pt-4" : ""}`, children: [jsxRuntimeExports.jsxs("div", { className: "flex items-center drag-icon cursor-grab absolute left-1/2 -translate-x-1/2", children: [jsxRuntimeExports.jsx(lucideReact.GripHorizontal, { className: "" }), jsxRuntimeExports.jsx(lucideReact.GripHorizontal, { className: "-ml-[3px]" }), jsxRuntimeExports.jsx(lucideReact.GripHorizontal, { className: "-ml-[3px]" })] }), jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 cursor-pointer justify-end", children: [jsxRuntimeExports.jsx(lucideReact.Trash2, { onClick: () => removeWidget(w.id), className: "w-5 h-5 text-red-700" }), (w.type !== "spacer" && w.type !== "chatbot") && jsxRuntimeExports.jsx(lucideReact.Edit, { onClick: () => onClickSettings && onClickSettings(w), className: "w-5 h-5 text-gray-600" })] })] }), !isEditing && shouldShowFilterBadge && (jsxRuntimeExports.jsxs("div", { className: "absolute top-2 right-2 z-10 bg-primary-600 text-white px-2 py-1 rounded-md shadow-md flex items-center gap-1.5 text-xs font-medium", children: [jsxRuntimeExports.jsx(lucideReact.Filter, { className: "w-3 h-3" }), jsxRuntimeExports.jsx("span", { children: "Filtered" })] })), jsxRuntimeExports.jsxs("div", { className: `${((w === null || w === void 0 ? void 0 : w.type) === 'text' || (w === null || w === void 0 ? void 0 : w.type) === 'spacer') ? `${isEditing ? 'px-4' : ''}` : "h-full"} w-full relative`, children: [(w === null || w === void 0 ? void 0 : w.type) === "chatbot" &&
42030
+ const filterStatus = w.type === 'agent' ? getWidgetFilterStatus(w.id) : null;
42031
+ const badgeInfo = filterStatus ? getFilterStatusBadge(filterStatus.status) : null;
42032
+ return (jsxRuntimeExports.jsxs("div", { className: `${(w.type === "text" || w.type === "spacer") ? `${((_b = (_a = w === null || w === void 0 ? void 0 : w.config) === null || _a === void 0 ? void 0 : _a.content) === null || _b === void 0 ? 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: [w.type === 'agent' && badgeInfo && !isApplyingFilters && (jsxRuntimeExports.jsx("div", { className: "absolute top-2 right-2 z-10", title: (filterStatus === null || filterStatus === void 0 ? void 0 : filterStatus.reason) || (filterStatus === null || filterStatus === void 0 ? void 0 : filterStatus.error) || '', children: jsxRuntimeExports.jsxs(Badge, { variant: badgeInfo.variant, className: "text-[10px] px-2 py-0.5 gap-1", children: [jsxRuntimeExports.jsx("span", { children: badgeInfo.icon }), jsxRuntimeExports.jsx("span", { children: badgeInfo.label })] }) })), w.type === 'agent' && isApplyingFilters && (jsxRuntimeExports.jsx("div", { className: "absolute top-2 right-2 z-10", children: jsxRuntimeExports.jsxs(Badge, { variant: "secondary", className: "text-[10px] px-2 py-0.5 gap-1 animate-pulse", children: [jsxRuntimeExports.jsx(lucideReact.Loader2, { className: "w-3 h-3 animate-spin" }), jsxRuntimeExports.jsx("span", { children: "Filtering..." })] }) })), isEditing &&
42033
+ jsxRuntimeExports.jsxs("div", { className: `flex items-center justify-end mb-4 relative ${(w.type === "text" || w.type === "spacer") ? "pl-4 pr-4 pt-4" : ""}`, children: [jsxRuntimeExports.jsxs("div", { className: "flex items-center drag-icon cursor-grab absolute left-1/2 -translate-x-1/2", children: [jsxRuntimeExports.jsx(lucideReact.GripHorizontal, { className: "" }), jsxRuntimeExports.jsx(lucideReact.GripHorizontal, { className: "-ml-[3px]" }), jsxRuntimeExports.jsx(lucideReact.GripHorizontal, { className: "-ml-[3px]" })] }), jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 cursor-pointer justify-end", children: [jsxRuntimeExports.jsx(lucideReact.Trash2, { onClick: () => removeWidget(w.id), className: "w-5 h-5 text-red-700" }), (w.type !== "spacer" && w.type !== "chatbot") && jsxRuntimeExports.jsx(lucideReact.Edit, { onClick: () => onClickSettings && onClickSettings(w), className: "w-5 h-5 text-gray-600" })] })] }), jsxRuntimeExports.jsxs("div", { className: `${((w === null || w === void 0 ? void 0 : w.type) === 'text' || (w === null || w === void 0 ? void 0 : w.type) === 'spacer') ? `${isEditing ? 'px-4' : ''}` : "h-full"} w-full relative`, children: [(w === null || w === void 0 ? void 0 : w.type) === "chatbot" &&
41873
42034
  jsxRuntimeExports.jsxs("div", { className: "relative z-50", children: [jsxRuntimeExports.jsx("div", { onClick: () => handleClearChat(w === null || w === void 0 ? void 0 : w.id), onMouseOver: () => setVisibleClearButton(w === null || w === void 0 ? 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: jsxRuntimeExports.jsx(lucideReact.MessageCircleX, { className: "w-5 h-5" }) }), 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 || w === void 0 ? void 0 : w.id) ? "block" : "hidden"}`, children: "Clear Chat" })] }), jsxRuntimeExports.jsx(WidgetRenderer, { widget: w, widgetBackendUrl: widgetBackendUrl, onResetReady: handleResetReady, widgetIds: widgets.filter(widget => widget.type !== 'chatbot').map(widget => widget.id), datasetId: datasetId, pageId: pageId, onApplyFilters: onApplyFilters, isEditing: isEditing })] })] }, w.id));
41874
42035
  }) })) })] }));
41875
42036
  }
@@ -44767,7 +44928,6 @@ function DashboardPages({ widgetBackendUrl }) {
44767
44928
  const [pages, setPages] = React.useState([]);
44768
44929
  const [isLoading, setIsLoading] = React.useState(true);
44769
44930
  const [error, setError] = React.useState(null);
44770
- const [activeFilters, setActiveFilters] = React.useState({});
44771
44931
  // Helper function to get API URL
44772
44932
  const getApiUrl = (endpoint) => {
44773
44933
  const baseUrl = widgetBackendUrl || '';
@@ -44776,10 +44936,6 @@ function DashboardPages({ widgetBackendUrl }) {
44776
44936
  React.useEffect(() => {
44777
44937
  loadPages();
44778
44938
  }, []);
44779
- const handleApplyFilters = (pageId) => (filters) => {
44780
- console.log('Filters applied for page:', pageId, filters);
44781
- setActiveFilters(prev => (Object.assign(Object.assign({}, prev), { [pageId]: filters })));
44782
- };
44783
44939
  const loadPages = async () => {
44784
44940
  try {
44785
44941
  setIsLoading(true);
@@ -44824,7 +44980,7 @@ function DashboardPages({ widgetBackendUrl }) {
44824
44980
  }
44825
44981
  return (jsxRuntimeExports.jsxs("div", { className: "container mx-auto p-6 h-screen flex flex-col", children: [jsxRuntimeExports.jsxs("div", { className: "mb-6", children: [jsxRuntimeExports.jsx("h1", { className: "text-2xl font-bold", children: "Dashboard Pages" }), jsxRuntimeExports.jsx("p", { className: "text-muted-foreground", children: "View all your saved pages in dashboard mode" })] }), jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-hidden", children: jsxRuntimeExports.jsxs(Tabs, { defaultValue: (_a = pages[0]) === null || _a === void 0 ? void 0 : _a.id, className: "h-full flex flex-col", children: [jsxRuntimeExports.jsx(TabsList, { className: "grid w-full grid-cols-auto gap-1 mb-4", style: {
44826
44982
  gridTemplateColumns: `repeat(${pages.length}, minmax(120px, 1fr))`
44827
- }, children: pages.map((page) => (jsxRuntimeExports.jsx(TabsTrigger, { value: page.id, className: "truncate px-3 py-2 data-[state=active]:bg-primary data-[state=active]:text-primary-foreground data-[state=active]:shadow-lg border-2 data-[state=active]:border-primary data-[state=inactive]:border-transparent hover:bg-accent hover:text-accent-foreground transition-all duration-200", title: page.title, children: page.title }, page.id))) }), pages.map((page) => (jsxRuntimeExports.jsx(TabsContent, { value: page.id, className: "flex-1 overflow-hidden m-0", children: jsxRuntimeExports.jsx("div", { className: "h-full border rounded-lg overflow-hidden", children: jsxRuntimeExports.jsx(WidgetDashboard, { pageId: page.id, isEditing: false, widgetBackendUrl: widgetBackendUrl, onApplyFilters: handleApplyFilters(page.id), activeFilters: activeFilters[page.id] || {} }) }) }, page.id)))] }) })] }));
44983
+ }, children: pages.map((page) => (jsxRuntimeExports.jsx(TabsTrigger, { value: page.id, className: "truncate px-3 py-2 data-[state=active]:bg-primary data-[state=active]:text-primary-foreground data-[state=active]:shadow-lg border-2 data-[state=active]:border-primary data-[state=inactive]:border-transparent hover:bg-accent hover:text-accent-foreground transition-all duration-200", title: page.title, children: page.title }, page.id))) }), pages.map((page) => (jsxRuntimeExports.jsx(TabsContent, { value: page.id, className: "flex-1 overflow-hidden m-0", children: jsxRuntimeExports.jsx("div", { className: "h-full border rounded-lg overflow-hidden", children: jsxRuntimeExports.jsx(WidgetDashboard, { pageId: page.id, isEditing: false, widgetBackendUrl: widgetBackendUrl }) }) }, page.id)))] }) })] }));
44828
44984
  }
44829
44985
 
44830
44986
  exports.Button = Button;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dp-widgets-framework",
3
- "version": "1.5.8",
3
+ "version": "1.6.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org"