@handled-ai/design-system 0.18.51 → 0.18.53

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.
Files changed (42) hide show
  1. package/dist/components/badge.d.ts +1 -1
  2. package/dist/components/button.d.ts +1 -1
  3. package/dist/components/data-table-filter.d.ts +21 -6
  4. package/dist/components/data-table-filter.js +134 -9
  5. package/dist/components/data-table-filter.js.map +1 -1
  6. package/dist/components/entity-panel.js +5 -5
  7. package/dist/components/entity-panel.js.map +1 -1
  8. package/dist/components/pill.d.ts +1 -1
  9. package/dist/components/score-why-chips.d.ts +1 -1
  10. package/dist/components/signal-feedback-inline.d.ts +28 -12
  11. package/dist/components/signal-feedback-inline.js +146 -10
  12. package/dist/components/signal-feedback-inline.js.map +1 -1
  13. package/dist/components/signal-priority-popover.d.ts +1 -1
  14. package/dist/components/signal-priority-popover.js +7 -16
  15. package/dist/components/signal-priority-popover.js.map +1 -1
  16. package/dist/components/tabs.d.ts +1 -1
  17. package/dist/index.d.ts +3 -3
  18. package/dist/index.js.map +1 -1
  19. package/dist/prototype/index.d.ts +1 -1
  20. package/dist/prototype/prototype-accounts-view.d.ts +1 -1
  21. package/dist/prototype/prototype-admin-view.d.ts +1 -1
  22. package/dist/prototype/prototype-config.d.ts +1 -1
  23. package/dist/prototype/prototype-inbox-view.d.ts +3 -3
  24. package/dist/prototype/prototype-inbox-view.js +1 -3
  25. package/dist/prototype/prototype-inbox-view.js.map +1 -1
  26. package/dist/prototype/prototype-insights-view.d.ts +1 -1
  27. package/dist/prototype/prototype-shell.d.ts +1 -1
  28. package/dist/{signal-priority-popover-DTedstRL.d.ts → signal-priority-popover-QJngMAj7.d.ts} +4 -13
  29. package/package.json +1 -1
  30. package/src/components/__tests__/case-panel-why.test.tsx +126 -0
  31. package/src/components/__tests__/data-table-filter.test.tsx +130 -0
  32. package/src/components/__tests__/entity-metadata-grid.test.tsx +27 -1
  33. package/src/components/__tests__/signal-priority-popover.test.tsx +4 -41
  34. package/src/components/data-table-filter.tsx +160 -9
  35. package/src/components/entity-panel.tsx +7 -5
  36. package/src/components/signal-feedback-inline.tsx +181 -20
  37. package/src/components/signal-priority-popover.tsx +6 -19
  38. package/src/index.ts +1 -1
  39. package/src/prototype/__tests__/detail-view-opportunity-preview.test.tsx +90 -0
  40. package/src/prototype/__tests__/detail-view-score-why.test.tsx +0 -34
  41. package/src/prototype/prototype-config.ts +3 -7
  42. package/src/prototype/prototype-inbox-view.tsx +3 -5
@@ -3,7 +3,7 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const badgeVariants: (props?: ({
6
- variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "ghost" | null | undefined;
6
+ variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" | "link" | null | undefined;
7
7
  } & class_variance_authority_types.ClassProp) | undefined) => string;
8
8
  declare function Badge({ className, variant, asChild, ...props }: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants> & {
9
9
  asChild?: boolean;
@@ -3,7 +3,7 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const buttonVariants: (props?: ({
6
- variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "ghost" | null | undefined;
6
+ variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" | "link" | null | undefined;
7
7
  size?: "default" | "sm" | "lg" | "icon" | null | undefined;
8
8
  } & class_variance_authority_types.ClassProp) | undefined) => string;
9
9
  declare function Button({ className, variant, size, asChild, ...props }: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & {
@@ -5,15 +5,12 @@ interface FilterOption {
5
5
  label: string;
6
6
  value: string;
7
7
  }
8
- interface DataTableFilterCategory {
8
+ interface DataTableFilterCategoryBase {
9
9
  id: string;
10
10
  label: string;
11
11
  icon: React.ComponentType<{
12
12
  className?: string;
13
13
  }>;
14
- options: (string | FilterOption)[];
15
- /** Filter behavior. Defaults to "multi" (checkbox multi-select). */
16
- type?: "multi" | "single" | "boolean";
17
14
  /**
18
15
  * Submenu search behavior. Defaults to the DataTableFilter
19
16
  * optionSearchThreshold prop. Use true to always show search or false to
@@ -23,6 +20,20 @@ interface DataTableFilterCategory {
23
20
  threshold?: number;
24
21
  };
25
22
  }
23
+ interface DataTableOptionFilterCategory extends DataTableFilterCategoryBase {
24
+ options: (string | FilterOption)[];
25
+ /** Filter behavior. Defaults to "multi" (checkbox multi-select). */
26
+ type?: "multi" | "single" | "boolean";
27
+ }
28
+ interface DataTableTextFilterCategory extends DataTableFilterCategoryBase {
29
+ /** Free-text filter behavior. Renders a top-level submenu with a text input. */
30
+ type: "text";
31
+ /** Placeholder shown in the text filter input. */
32
+ valuePlaceholder?: string;
33
+ /** Not used for text filters; optional for backwards-compatible category shapes. */
34
+ options?: (string | FilterOption)[];
35
+ }
36
+ type DataTableFilterCategory = DataTableOptionFilterCategory | DataTableTextFilterCategory;
26
37
  interface DataTableFilterProps {
27
38
  categories: DataTableFilterCategory[];
28
39
  selectedFilters: Record<string, string[]>;
@@ -44,7 +55,11 @@ interface DataTableFilterProps {
44
55
  onConditionFiltersChange?: (conditions: ConditionFilterValue[]) => void;
45
56
  /** Dropdown entry label for the condition-builder panel. Default: "Add filter". */
46
57
  conditionBuilderLabel?: string;
58
+ /** Active free-text filters keyed by category id. */
59
+ textFilters?: Record<string, string>;
60
+ /** Callback when a free-text filter value is applied or cleared. */
61
+ onTextFilterChange?: (categoryId: string, value: string) => void;
47
62
  }
48
- declare function DataTableFilter({ categories, selectedFilters, onToggleFilter, className, optionSearchThreshold, presetFilters, onTogglePreset, presetLabel, conditionFields, conditionFilters, onConditionFiltersChange, conditionBuilderLabel, }: DataTableFilterProps): React.JSX.Element;
63
+ declare function DataTableFilter({ categories, selectedFilters, onToggleFilter, className, optionSearchThreshold, presetFilters, onTogglePreset, presetLabel, conditionFields, conditionFilters, onConditionFiltersChange, conditionBuilderLabel, textFilters, onTextFilterChange, }: DataTableFilterProps): React.JSX.Element;
49
64
 
50
- export { DataTableFilter, type DataTableFilterCategory, type DataTableFilterProps, type FilterOption };
65
+ export { DataTableFilter, type DataTableFilterCategory, type DataTableFilterProps, type DataTableOptionFilterCategory, type DataTableTextFilterCategory, type FilterOption };
@@ -45,6 +45,110 @@ function getOptionValue(option) {
45
45
  function getOptionLabel(option) {
46
46
  return typeof option === "string" ? option : option.label;
47
47
  }
48
+ function isTextFilterCategory(category) {
49
+ return category.type === "text";
50
+ }
51
+ function TextFilterSubmenu({
52
+ category,
53
+ value,
54
+ onValueChange
55
+ }) {
56
+ var _a;
57
+ const [draftValue, setDraftValue] = React.useState(value);
58
+ React.useEffect(() => {
59
+ setDraftValue(value);
60
+ }, [value]);
61
+ const active = value.trim().length > 0;
62
+ const applyValue = React.useCallback(() => {
63
+ onValueChange == null ? void 0 : onValueChange(category.id, draftValue.trim());
64
+ }, [category.id, draftValue, onValueChange]);
65
+ return /* @__PURE__ */ jsxs(
66
+ DropdownMenuSub,
67
+ {
68
+ onOpenChange: (open) => {
69
+ if (!open) {
70
+ setDraftValue(value);
71
+ }
72
+ },
73
+ children: [
74
+ /* @__PURE__ */ jsxs(
75
+ DropdownMenuSubTrigger,
76
+ {
77
+ className: cn(
78
+ "cursor-pointer py-1.5 text-xs",
79
+ active && "text-brand-purple"
80
+ ),
81
+ children: [
82
+ /* @__PURE__ */ jsx(
83
+ category.icon,
84
+ {
85
+ className: cn(
86
+ "mr-2 h-3.5 w-3.5 text-muted-foreground",
87
+ active && "text-brand-purple"
88
+ )
89
+ }
90
+ ),
91
+ category.label,
92
+ active ? /* @__PURE__ */ jsx(Check, { className: "ml-auto h-4 w-4" }) : null
93
+ ]
94
+ }
95
+ ),
96
+ /* @__PURE__ */ jsx(DropdownMenuSubContent, { className: "w-64 p-2", children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
97
+ /* @__PURE__ */ jsx(
98
+ "input",
99
+ {
100
+ "aria-label": category.label,
101
+ className: "h-8 w-full rounded-md bg-muted/50 px-2 py-1 text-xs outline-none transition-colors placeholder:text-muted-foreground/70 focus:bg-muted",
102
+ placeholder: (_a = category.valuePlaceholder) != null ? _a : `Enter ${category.label.toLowerCase()}...`,
103
+ value: draftValue,
104
+ onChange: (event) => setDraftValue(event.target.value),
105
+ onClick: (event) => event.stopPropagation(),
106
+ onKeyDown: (event) => {
107
+ event.stopPropagation();
108
+ if (event.key === "Enter") {
109
+ event.preventDefault();
110
+ applyValue();
111
+ }
112
+ }
113
+ }
114
+ ),
115
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2", children: [
116
+ active ? /* @__PURE__ */ jsx(
117
+ Button,
118
+ {
119
+ type: "button",
120
+ variant: "ghost",
121
+ size: "sm",
122
+ className: "h-7 px-2 text-xs",
123
+ onClick: (event) => {
124
+ event.preventDefault();
125
+ event.stopPropagation();
126
+ setDraftValue("");
127
+ onValueChange == null ? void 0 : onValueChange(category.id, "");
128
+ },
129
+ children: "Clear"
130
+ }
131
+ ) : null,
132
+ /* @__PURE__ */ jsx(
133
+ Button,
134
+ {
135
+ type: "button",
136
+ size: "sm",
137
+ className: "h-7 px-2 text-xs",
138
+ onClick: (event) => {
139
+ event.preventDefault();
140
+ event.stopPropagation();
141
+ applyValue();
142
+ },
143
+ children: "Apply"
144
+ }
145
+ )
146
+ ] })
147
+ ] }) })
148
+ ]
149
+ }
150
+ );
151
+ }
48
152
  function DataTableFilter({
49
153
  categories,
50
154
  selectedFilters,
@@ -57,7 +161,9 @@ function DataTableFilter({
57
161
  conditionFields = [],
58
162
  conditionFilters = [],
59
163
  onConditionFiltersChange,
60
- conditionBuilderLabel = "Add filter"
164
+ conditionBuilderLabel = "Add filter",
165
+ textFilters = {},
166
+ onTextFilterChange
61
167
  }) {
62
168
  const [query, setQuery] = React.useState("");
63
169
  const [subQueries, setSubQueries] = React.useState({});
@@ -72,6 +178,9 @@ function DataTableFilter({
72
178
  if (category.label.toLowerCase().includes(normalized)) {
73
179
  return true;
74
180
  }
181
+ if (isTextFilterCategory(category)) {
182
+ return false;
183
+ }
75
184
  return category.options.some(
76
185
  (option) => getOptionLabel(option).toLowerCase().includes(normalized)
77
186
  );
@@ -89,8 +198,15 @@ function DataTableFilter({
89
198
  (count, selected) => count + selected.length,
90
199
  0
91
200
  );
92
- return userCount + conditionFilters.length;
93
- }, [selectedFilters, conditionFilters.length]);
201
+ const textCount = categories.reduce((count, category) => {
202
+ var _a;
203
+ if (!isTextFilterCategory(category)) {
204
+ return count;
205
+ }
206
+ return ((_a = textFilters[category.id]) == null ? void 0 : _a.trim()) ? count + 1 : count;
207
+ }, 0);
208
+ return userCount + conditionFilters.length + textCount;
209
+ }, [categories, selectedFilters, conditionFilters.length, textFilters]);
94
210
  const presetChips = React.useMemo(() => {
95
211
  var _a, _b;
96
212
  if (!presetFilters) return [];
@@ -98,9 +214,7 @@ function DataTableFilter({
98
214
  for (const [categoryId, values] of Object.entries(presetFilters)) {
99
215
  const category = categories.find((c) => c.id === categoryId);
100
216
  for (const value of values) {
101
- const option = category == null ? void 0 : category.options.find(
102
- (opt) => getOptionValue(opt) === value
103
- );
217
+ const option = category && !isTextFilterCategory(category) ? category.options.find((opt) => getOptionValue(opt) === value) : void 0;
104
218
  const label = option ? getOptionLabel(option) : value;
105
219
  const active = (_b = (_a = selectedFilters[categoryId]) == null ? void 0 : _a.includes(value)) != null ? _b : false;
106
220
  chips.push({ categoryId, value, label, active });
@@ -142,7 +256,7 @@ function DataTableFilter({
142
256
  ] }) }),
143
257
  /* @__PURE__ */ jsxs("div", { className: "max-h-[320px] overflow-y-auto p-1", children: [
144
258
  visibleCategories.map((category) => {
145
- var _a, _b, _c, _d, _e;
259
+ var _a, _b, _c, _d, _e, _f;
146
260
  const filterType = (_a = category.type) != null ? _a : "multi";
147
261
  if (filterType === "boolean") {
148
262
  const active = (_c = (_b = selectedFilters[category.id]) == null ? void 0 : _b.includes("true")) != null ? _c : false;
@@ -166,7 +280,18 @@ function DataTableFilter({
166
280
  category.id
167
281
  );
168
282
  }
169
- const subQuery = ((_d = subQueries[category.id]) != null ? _d : "").trim().toLowerCase();
283
+ if (isTextFilterCategory(category)) {
284
+ return /* @__PURE__ */ jsx(
285
+ TextFilterSubmenu,
286
+ {
287
+ category,
288
+ value: (_d = textFilters[category.id]) != null ? _d : "",
289
+ onValueChange: onTextFilterChange
290
+ },
291
+ category.id
292
+ );
293
+ }
294
+ const subQuery = ((_e = subQueries[category.id]) != null ? _e : "").trim().toLowerCase();
170
295
  const filteredOptions = subQuery ? category.options.filter(
171
296
  (opt) => getOptionLabel(opt).toLowerCase().includes(subQuery)
172
297
  ) : category.options;
@@ -200,7 +325,7 @@ function DataTableFilter({
200
325
  {
201
326
  className: "h-7 w-full rounded-md bg-muted/50 py-1 pr-2 pl-7 text-xs outline-none transition-colors placeholder:text-muted-foreground/70 focus:bg-muted",
202
327
  placeholder: "Search...",
203
- value: (_e = subQueries[category.id]) != null ? _e : "",
328
+ value: (_f = subQueries[category.id]) != null ? _f : "",
204
329
  onChange: (e) => setSubQueries((prev) => __spreadProps(__spreadValues({}, prev), { [category.id]: e.target.value })),
205
330
  onClick: (e) => e.stopPropagation(),
206
331
  onKeyDown: (e) => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/data-table-filter.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { Check, ListFilter, Plus, Search } from \"lucide-react\"\n\nimport { Popover as PopoverPrimitive } from \"radix-ui\"\n\nimport { cn } from \"../lib/utils\"\nimport { Button } from \"./button\"\nimport {\n DataTableConditionFilter,\n shouldShowOptionSearch,\n type ConditionFieldDef,\n type ConditionFilterValue,\n} from \"./data-table-condition-filter\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger,\n} from \"./dropdown-menu\"\n\nexport interface FilterOption {\n label: string\n value: string\n}\n\nexport interface DataTableFilterCategory {\n id: string\n label: string\n icon: React.ComponentType<{ className?: string }>\n options: (string | FilterOption)[]\n /** Filter behavior. Defaults to \"multi\" (checkbox multi-select). */\n type?: \"multi\" | \"single\" | \"boolean\"\n /**\n * Submenu search behavior. Defaults to the DataTableFilter\n * optionSearchThreshold prop. Use true to always show search or false to\n * hide it for a specific category.\n */\n searchable?: boolean | { threshold?: number }\n}\n\nfunction getOptionValue(option: string | FilterOption): string {\n return typeof option === \"string\" ? option : option.value\n}\nfunction getOptionLabel(option: string | FilterOption): string {\n return typeof option === \"string\" ? option : option.label\n}\n\nexport interface DataTableFilterProps {\n categories: DataTableFilterCategory[]\n selectedFilters: Record<string, string[]>\n onToggleFilter: (categoryId: string, option: string) => void\n className?: string\n /** Minimum number of options before showing the sub-menu search input. Defaults to 8. */\n optionSearchThreshold?: number\n /** Filters applied by default. Shown as distinct chips that can be toggled off but not dismissed. */\n presetFilters?: Record<string, string[]>\n /** Callback when a preset filter is toggled on/off. */\n onTogglePreset?: (categoryId: string, option: string) => void\n /** Label shown on preset chips to distinguish from user-applied filters. Default: \"Default\". */\n presetLabel?: string\n /** Fields exposed in the unified condition-builder panel. */\n conditionFields?: ConditionFieldDef[]\n /** Active builder-managed field/operator/value conditions. */\n conditionFilters?: ConditionFilterValue[]\n /** Callback when builder-managed conditions are applied, removed, or cleared. */\n onConditionFiltersChange?: (conditions: ConditionFilterValue[]) => void\n /** Dropdown entry label for the condition-builder panel. Default: \"Add filter\". */\n conditionBuilderLabel?: string\n}\n\nexport function DataTableFilter({\n categories,\n selectedFilters,\n onToggleFilter,\n className,\n optionSearchThreshold = 8,\n presetFilters,\n onTogglePreset,\n presetLabel = \"Default\",\n conditionFields = [],\n conditionFilters = [],\n onConditionFiltersChange,\n conditionBuilderLabel = \"Add filter\",\n}: DataTableFilterProps) {\n const [query, setQuery] = React.useState(\"\")\n const [subQueries, setSubQueries] = React.useState<Record<string, string>>({})\n const [conditionBuilderOpen, setConditionBuilderOpen] = React.useState(false)\n const hasConditionBuilder = conditionFields.length > 0\n\n const visibleCategories = React.useMemo(() => {\n const normalized = query.trim().toLowerCase()\n if (!normalized) {\n return categories\n }\n\n return categories.filter((category) => {\n if (category.label.toLowerCase().includes(normalized)) {\n return true\n }\n\n return category.options.some((option) =>\n getOptionLabel(option).toLowerCase().includes(normalized)\n )\n })\n }, [categories, query])\n\n /** Check if a specific option is a preset filter */\n const isPresetOption = React.useCallback(\n (categoryId: string, value: string): boolean => {\n return presetFilters?.[categoryId]?.includes(value) ?? false\n },\n [presetFilters]\n )\n\n const activeCount = React.useMemo(() => {\n const userCount = Object.values(selectedFilters).reduce(\n (count, selected) => count + selected.length,\n 0\n )\n\n return userCount + conditionFilters.length\n }, [selectedFilters, conditionFilters.length])\n\n /** Collect all preset chips to render */\n const presetChips = React.useMemo(() => {\n if (!presetFilters) return []\n\n const chips: { categoryId: string; value: string; label: string; active: boolean }[] = []\n\n for (const [categoryId, values] of Object.entries(presetFilters)) {\n const category = categories.find((c) => c.id === categoryId)\n for (const value of values) {\n const option = category?.options.find(\n (opt) => getOptionValue(opt) === value\n )\n const label = option ? getOptionLabel(option) : value\n const active = selectedFilters[categoryId]?.includes(value) ?? false\n chips.push({ categoryId, value, label, active })\n }\n }\n\n return chips\n }, [presetFilters, categories, selectedFilters])\n\n const triggerButton = (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className={cn(\n \"h-8 gap-2 rounded-md border-border/60 bg-background text-xs font-normal shadow-none hover:bg-muted/50\",\n className\n )}\n >\n <ListFilter className=\"h-3.5 w-3.5\" />\n Filter\n {activeCount > 0 ? (\n <span className=\"rounded bg-muted px-1.5 py-0 text-[10px] font-semibold text-foreground\">\n {activeCount}\n </span>\n ) : null}\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" className=\"w-[240px] p-0\">\n <div className=\"sticky top-0 z-10 border-b border-border bg-popover p-2\">\n <div className=\"relative\">\n <Search className=\"absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground\" />\n <input\n className=\"h-8 w-full rounded-md bg-muted/50 py-1 pr-2 pl-7 text-xs outline-none transition-colors placeholder:text-muted-foreground/70 focus:bg-muted\"\n placeholder=\"Search filters...\"\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n onClick={(event) => event.stopPropagation()}\n onKeyDown={(event) => event.stopPropagation()}\n />\n </div>\n </div>\n\n <div className=\"max-h-[320px] overflow-y-auto p-1\">\n {visibleCategories.map((category) => {\n const filterType = category.type ?? \"multi\"\n\n /* ── Boolean toggle ─────────────────────────────────── */\n if (filterType === \"boolean\") {\n const active = selectedFilters[category.id]?.includes(\"true\") ?? false\n return (\n <DropdownMenuItem\n key={category.id}\n className={cn(\n \"cursor-pointer py-1.5 text-xs\",\n active && \"text-brand-purple\"\n )}\n onSelect={(event) => {\n event.preventDefault()\n onToggleFilter(category.id, \"true\")\n }}\n >\n <category.icon className=\"mr-2 h-3.5 w-3.5\" />\n {category.label}\n {active ? <Check className=\"ml-auto h-4 w-4\" /> : null}\n </DropdownMenuItem>\n )\n }\n\n /* ── Sub-menu (single / multi) ──────────────────────── */\n const subQuery = (subQueries[category.id] ?? \"\").trim().toLowerCase()\n const filteredOptions = subQuery\n ? category.options.filter((opt) =>\n getOptionLabel(opt).toLowerCase().includes(subQuery)\n )\n : category.options\n const shouldShowSubmenuSearch = shouldShowOptionSearch(\n category.searchable,\n category.options.length,\n optionSearchThreshold,\n )\n\n return (\n <DropdownMenuSub\n key={category.id}\n onOpenChange={(open) => {\n if (!open) {\n setSubQueries((prev) => {\n const next = { ...prev }\n delete next[category.id]\n return next\n })\n }\n }}\n >\n <DropdownMenuSubTrigger className=\"cursor-pointer py-1.5 text-xs\">\n <category.icon className=\"mr-2 h-3.5 w-3.5 text-muted-foreground\" />\n {category.label}\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent className=\"max-h-[320px] w-52 overflow-y-auto p-1\">\n {/* Submenu search — shown for long lists or categories that opt in. */}\n {shouldShowSubmenuSearch && (\n <div className=\"sticky top-0 z-10 border-b border-border bg-popover p-1.5\">\n <div className=\"relative\">\n <Search className=\"absolute left-2 top-1/2 h-3 w-3 -translate-y-1/2 text-muted-foreground\" />\n <input\n className=\"h-7 w-full rounded-md bg-muted/50 py-1 pr-2 pl-7 text-xs outline-none transition-colors placeholder:text-muted-foreground/70 focus:bg-muted\"\n placeholder=\"Search...\"\n value={subQueries[category.id] ?? \"\"}\n onChange={(e) =>\n setSubQueries((prev) => ({ ...prev, [category.id]: e.target.value }))\n }\n onClick={(e) => e.stopPropagation()}\n onKeyDown={(e) => {\n // Allow navigation keys to propagate to Radix menu handling\n // so keyboard users can move to and select filtered options.\n const navKeys = [\"ArrowDown\", \"ArrowUp\", \"Enter\", \"Escape\", \"Tab\"]\n if (!navKeys.includes(e.key)) {\n e.stopPropagation()\n }\n }}\n />\n </div>\n </div>\n )}\n {/* Filtered options */}\n {filteredOptions.map((option) => {\n const value = getOptionValue(option)\n const label = getOptionLabel(option)\n const selected = selectedFilters[category.id]?.includes(value) ?? false\n const isPreset = isPresetOption(category.id, value)\n return (\n <DropdownMenuItem\n key={value}\n className=\"cursor-pointer justify-between text-xs\"\n onSelect={(event) => {\n event.preventDefault()\n onToggleFilter(category.id, value)\n }}\n >\n {label}\n {selected ? (\n isPreset ? (\n <span className=\"text-brand-purple text-[10px] font-semibold\">\n {presetLabel}\n </span>\n ) : filterType === \"single\" ? (\n <span className=\"h-1.5 w-1.5 rounded-full bg-current\" />\n ) : (\n <span className=\"text-[10px] font-semibold text-brand-purple\">\n Applied\n </span>\n )\n ) : null}\n </DropdownMenuItem>\n )\n })}\n {filteredOptions.length === 0 && category.options.length > 0 && (\n <div className=\"p-2 text-center text-xs text-muted-foreground\">\n No matches\n </div>\n )}\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n )\n })}\n\n {visibleCategories.length === 0 ? (\n <div className=\"p-2 text-center text-xs text-muted-foreground\">\n No filters found\n </div>\n ) : null}\n </div>\n\n {hasConditionBuilder ? (\n <div className=\"border-t border-border p-1\">\n <PopoverPrimitive.Root\n open={conditionBuilderOpen}\n onOpenChange={setConditionBuilderOpen}\n >\n <PopoverPrimitive.Trigger asChild>\n <DropdownMenuItem\n className=\"cursor-pointer py-1.5 text-xs\"\n onSelect={(event) => {\n event.preventDefault()\n setConditionBuilderOpen(true)\n }}\n >\n <Plus className=\"mr-2 h-3.5 w-3.5 text-muted-foreground\" />\n {conditionBuilderLabel}\n {conditionFilters.length > 0 ? (\n <span className=\"ml-auto rounded bg-muted px-1.5 py-0 text-[10px] font-semibold\">\n {conditionFilters.length}\n </span>\n ) : null}\n </DropdownMenuItem>\n </PopoverPrimitive.Trigger>\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n align=\"start\"\n side=\"right\"\n sideOffset={8}\n className=\"z-50 outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\"\n onEscapeKeyDown={() => setConditionBuilderOpen(false)}\n onInteractOutside={(event) => {\n const target = event.target as HTMLElement | null\n if (target?.closest('[data-slot=\"dropdown-menu-content\"]')) {\n event.preventDefault()\n }\n }}\n >\n <DataTableConditionFilter\n fields={conditionFields}\n conditions={conditionFilters}\n onConditionsChange={onConditionFiltersChange ?? (() => {})}\n />\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n </PopoverPrimitive.Root>\n </div>\n ) : null}\n </DropdownMenuContent>\n </DropdownMenu>\n )\n\n // If there are preset chips, wrap trigger + chips together\n if (presetChips.length > 0) {\n return (\n <div className=\"flex flex-wrap items-center gap-1.5\">\n {triggerButton}\n {presetChips.map((chip) => (\n <button\n key={`${chip.categoryId}-${chip.value}`}\n type=\"button\"\n onClick={() => onTogglePreset?.(chip.categoryId, chip.value)}\n className={cn(\n \"inline-flex items-center gap-1 rounded-md border px-2 py-0.5 text-[11px] font-medium transition-colors\",\n chip.active\n ? \"border-dashed border-brand-purple/30 bg-brand-purple/5 text-brand-purple/80\"\n : \"border-border/40 bg-transparent text-muted-foreground/60 line-through\"\n )}\n >\n <span className=\"text-brand-purple/50 text-[10px]\">\n {presetLabel}:{\" \"}\n </span>\n {chip.label}\n </button>\n ))}\n </div>\n )\n }\n\n return triggerButton\n}\n"],"mappings":"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","names":["_a","_b"]}
1
+ {"version":3,"sources":["../../src/components/data-table-filter.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { Check, ListFilter, Plus, Search } from \"lucide-react\"\n\nimport { Popover as PopoverPrimitive } from \"radix-ui\"\n\nimport { cn } from \"../lib/utils\"\nimport { Button } from \"./button\"\nimport {\n DataTableConditionFilter,\n shouldShowOptionSearch,\n type ConditionFieldDef,\n type ConditionFilterValue,\n} from \"./data-table-condition-filter\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger,\n} from \"./dropdown-menu\"\n\nexport interface FilterOption {\n label: string\n value: string\n}\n\ninterface DataTableFilterCategoryBase {\n id: string\n label: string\n icon: React.ComponentType<{ className?: string }>\n /**\n * Submenu search behavior. Defaults to the DataTableFilter\n * optionSearchThreshold prop. Use true to always show search or false to\n * hide it for a specific category.\n */\n searchable?: boolean | { threshold?: number }\n}\n\nexport interface DataTableOptionFilterCategory extends DataTableFilterCategoryBase {\n options: (string | FilterOption)[]\n /** Filter behavior. Defaults to \"multi\" (checkbox multi-select). */\n type?: \"multi\" | \"single\" | \"boolean\"\n}\n\nexport interface DataTableTextFilterCategory extends DataTableFilterCategoryBase {\n /** Free-text filter behavior. Renders a top-level submenu with a text input. */\n type: \"text\"\n /** Placeholder shown in the text filter input. */\n valuePlaceholder?: string\n /** Not used for text filters; optional for backwards-compatible category shapes. */\n options?: (string | FilterOption)[]\n}\n\nexport type DataTableFilterCategory =\n | DataTableOptionFilterCategory\n | DataTableTextFilterCategory\n\nfunction getOptionValue(option: string | FilterOption): string {\n return typeof option === \"string\" ? option : option.value\n}\nfunction getOptionLabel(option: string | FilterOption): string {\n return typeof option === \"string\" ? option : option.label\n}\n\nfunction isTextFilterCategory(\n category: DataTableFilterCategory\n): category is DataTableTextFilterCategory {\n return category.type === \"text\"\n}\n\nfunction TextFilterSubmenu({\n category,\n value,\n onValueChange,\n}: {\n category: DataTableTextFilterCategory\n value: string\n onValueChange?: (categoryId: string, value: string) => void\n}) {\n const [draftValue, setDraftValue] = React.useState(value)\n\n React.useEffect(() => {\n setDraftValue(value)\n }, [value])\n\n const active = value.trim().length > 0\n const applyValue = React.useCallback(() => {\n onValueChange?.(category.id, draftValue.trim())\n }, [category.id, draftValue, onValueChange])\n\n return (\n <DropdownMenuSub\n onOpenChange={(open) => {\n if (!open) {\n setDraftValue(value)\n }\n }}\n >\n <DropdownMenuSubTrigger\n className={cn(\n \"cursor-pointer py-1.5 text-xs\",\n active && \"text-brand-purple\"\n )}\n >\n <category.icon\n className={cn(\n \"mr-2 h-3.5 w-3.5 text-muted-foreground\",\n active && \"text-brand-purple\"\n )}\n />\n {category.label}\n {active ? <Check className=\"ml-auto h-4 w-4\" /> : null}\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent className=\"w-64 p-2\">\n <div className=\"space-y-2\">\n <input\n aria-label={category.label}\n className=\"h-8 w-full rounded-md bg-muted/50 px-2 py-1 text-xs outline-none transition-colors placeholder:text-muted-foreground/70 focus:bg-muted\"\n placeholder={\n category.valuePlaceholder ??\n `Enter ${category.label.toLowerCase()}...`\n }\n value={draftValue}\n onChange={(event) => setDraftValue(event.target.value)}\n onClick={(event) => event.stopPropagation()}\n onKeyDown={(event) => {\n event.stopPropagation()\n if (event.key === \"Enter\") {\n event.preventDefault()\n applyValue()\n }\n }}\n />\n <div className=\"flex items-center justify-end gap-2\">\n {active ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-7 px-2 text-xs\"\n onClick={(event) => {\n event.preventDefault()\n event.stopPropagation()\n setDraftValue(\"\")\n onValueChange?.(category.id, \"\")\n }}\n >\n Clear\n </Button>\n ) : null}\n <Button\n type=\"button\"\n size=\"sm\"\n className=\"h-7 px-2 text-xs\"\n onClick={(event) => {\n event.preventDefault()\n event.stopPropagation()\n applyValue()\n }}\n >\n Apply\n </Button>\n </div>\n </div>\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n )\n}\n\nexport interface DataTableFilterProps {\n categories: DataTableFilterCategory[]\n selectedFilters: Record<string, string[]>\n onToggleFilter: (categoryId: string, option: string) => void\n className?: string\n /** Minimum number of options before showing the sub-menu search input. Defaults to 8. */\n optionSearchThreshold?: number\n /** Filters applied by default. Shown as distinct chips that can be toggled off but not dismissed. */\n presetFilters?: Record<string, string[]>\n /** Callback when a preset filter is toggled on/off. */\n onTogglePreset?: (categoryId: string, option: string) => void\n /** Label shown on preset chips to distinguish from user-applied filters. Default: \"Default\". */\n presetLabel?: string\n /** Fields exposed in the unified condition-builder panel. */\n conditionFields?: ConditionFieldDef[]\n /** Active builder-managed field/operator/value conditions. */\n conditionFilters?: ConditionFilterValue[]\n /** Callback when builder-managed conditions are applied, removed, or cleared. */\n onConditionFiltersChange?: (conditions: ConditionFilterValue[]) => void\n /** Dropdown entry label for the condition-builder panel. Default: \"Add filter\". */\n conditionBuilderLabel?: string\n /** Active free-text filters keyed by category id. */\n textFilters?: Record<string, string>\n /** Callback when a free-text filter value is applied or cleared. */\n onTextFilterChange?: (categoryId: string, value: string) => void\n}\n\nexport function DataTableFilter({\n categories,\n selectedFilters,\n onToggleFilter,\n className,\n optionSearchThreshold = 8,\n presetFilters,\n onTogglePreset,\n presetLabel = \"Default\",\n conditionFields = [],\n conditionFilters = [],\n onConditionFiltersChange,\n conditionBuilderLabel = \"Add filter\",\n textFilters = {},\n onTextFilterChange,\n}: DataTableFilterProps) {\n const [query, setQuery] = React.useState(\"\")\n const [subQueries, setSubQueries] = React.useState<Record<string, string>>({})\n const [conditionBuilderOpen, setConditionBuilderOpen] = React.useState(false)\n const hasConditionBuilder = conditionFields.length > 0\n\n const visibleCategories = React.useMemo(() => {\n const normalized = query.trim().toLowerCase()\n if (!normalized) {\n return categories\n }\n\n return categories.filter((category) => {\n if (category.label.toLowerCase().includes(normalized)) {\n return true\n }\n\n if (isTextFilterCategory(category)) {\n return false\n }\n\n return category.options.some((option) =>\n getOptionLabel(option).toLowerCase().includes(normalized)\n )\n })\n }, [categories, query])\n\n /** Check if a specific option is a preset filter */\n const isPresetOption = React.useCallback(\n (categoryId: string, value: string): boolean => {\n return presetFilters?.[categoryId]?.includes(value) ?? false\n },\n [presetFilters]\n )\n\n const activeCount = React.useMemo(() => {\n const userCount = Object.values(selectedFilters).reduce(\n (count, selected) => count + selected.length,\n 0\n )\n\n const textCount = categories.reduce((count, category) => {\n if (!isTextFilterCategory(category)) {\n return count\n }\n\n return textFilters[category.id]?.trim() ? count + 1 : count\n }, 0)\n\n return userCount + conditionFilters.length + textCount\n }, [categories, selectedFilters, conditionFilters.length, textFilters])\n\n /** Collect all preset chips to render */\n const presetChips = React.useMemo(() => {\n if (!presetFilters) return []\n\n const chips: { categoryId: string; value: string; label: string; active: boolean }[] = []\n\n for (const [categoryId, values] of Object.entries(presetFilters)) {\n const category = categories.find((c) => c.id === categoryId)\n for (const value of values) {\n const option = category && !isTextFilterCategory(category)\n ? category.options.find((opt) => getOptionValue(opt) === value)\n : undefined\n const label = option ? getOptionLabel(option) : value\n const active = selectedFilters[categoryId]?.includes(value) ?? false\n chips.push({ categoryId, value, label, active })\n }\n }\n\n return chips\n }, [presetFilters, categories, selectedFilters])\n\n const triggerButton = (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className={cn(\n \"h-8 gap-2 rounded-md border-border/60 bg-background text-xs font-normal shadow-none hover:bg-muted/50\",\n className\n )}\n >\n <ListFilter className=\"h-3.5 w-3.5\" />\n Filter\n {activeCount > 0 ? (\n <span className=\"rounded bg-muted px-1.5 py-0 text-[10px] font-semibold text-foreground\">\n {activeCount}\n </span>\n ) : null}\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" className=\"w-[240px] p-0\">\n <div className=\"sticky top-0 z-10 border-b border-border bg-popover p-2\">\n <div className=\"relative\">\n <Search className=\"absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground\" />\n <input\n className=\"h-8 w-full rounded-md bg-muted/50 py-1 pr-2 pl-7 text-xs outline-none transition-colors placeholder:text-muted-foreground/70 focus:bg-muted\"\n placeholder=\"Search filters...\"\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n onClick={(event) => event.stopPropagation()}\n onKeyDown={(event) => event.stopPropagation()}\n />\n </div>\n </div>\n\n <div className=\"max-h-[320px] overflow-y-auto p-1\">\n {visibleCategories.map((category) => {\n const filterType = category.type ?? \"multi\"\n\n /* ── Boolean toggle ─────────────────────────────────── */\n if (filterType === \"boolean\") {\n const active = selectedFilters[category.id]?.includes(\"true\") ?? false\n return (\n <DropdownMenuItem\n key={category.id}\n className={cn(\n \"cursor-pointer py-1.5 text-xs\",\n active && \"text-brand-purple\"\n )}\n onSelect={(event) => {\n event.preventDefault()\n onToggleFilter(category.id, \"true\")\n }}\n >\n <category.icon className=\"mr-2 h-3.5 w-3.5\" />\n {category.label}\n {active ? <Check className=\"ml-auto h-4 w-4\" /> : null}\n </DropdownMenuItem>\n )\n }\n\n /* ── Free-text submenu ───────────────────────────────── */\n if (isTextFilterCategory(category)) {\n return (\n <TextFilterSubmenu\n key={category.id}\n category={category}\n value={textFilters[category.id] ?? \"\"}\n onValueChange={onTextFilterChange}\n />\n )\n }\n\n /* ── Sub-menu (single / multi) ──────────────────────── */\n const subQuery = (subQueries[category.id] ?? \"\").trim().toLowerCase()\n const filteredOptions = subQuery\n ? category.options.filter((opt) =>\n getOptionLabel(opt).toLowerCase().includes(subQuery)\n )\n : category.options\n const shouldShowSubmenuSearch = shouldShowOptionSearch(\n category.searchable,\n category.options.length,\n optionSearchThreshold,\n )\n\n return (\n <DropdownMenuSub\n key={category.id}\n onOpenChange={(open) => {\n if (!open) {\n setSubQueries((prev) => {\n const next = { ...prev }\n delete next[category.id]\n return next\n })\n }\n }}\n >\n <DropdownMenuSubTrigger className=\"cursor-pointer py-1.5 text-xs\">\n <category.icon className=\"mr-2 h-3.5 w-3.5 text-muted-foreground\" />\n {category.label}\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent className=\"max-h-[320px] w-52 overflow-y-auto p-1\">\n {/* Submenu search — shown for long lists or categories that opt in. */}\n {shouldShowSubmenuSearch && (\n <div className=\"sticky top-0 z-10 border-b border-border bg-popover p-1.5\">\n <div className=\"relative\">\n <Search className=\"absolute left-2 top-1/2 h-3 w-3 -translate-y-1/2 text-muted-foreground\" />\n <input\n className=\"h-7 w-full rounded-md bg-muted/50 py-1 pr-2 pl-7 text-xs outline-none transition-colors placeholder:text-muted-foreground/70 focus:bg-muted\"\n placeholder=\"Search...\"\n value={subQueries[category.id] ?? \"\"}\n onChange={(e) =>\n setSubQueries((prev) => ({ ...prev, [category.id]: e.target.value }))\n }\n onClick={(e) => e.stopPropagation()}\n onKeyDown={(e) => {\n // Allow navigation keys to propagate to Radix menu handling\n // so keyboard users can move to and select filtered options.\n const navKeys = [\"ArrowDown\", \"ArrowUp\", \"Enter\", \"Escape\", \"Tab\"]\n if (!navKeys.includes(e.key)) {\n e.stopPropagation()\n }\n }}\n />\n </div>\n </div>\n )}\n {/* Filtered options */}\n {filteredOptions.map((option) => {\n const value = getOptionValue(option)\n const label = getOptionLabel(option)\n const selected = selectedFilters[category.id]?.includes(value) ?? false\n const isPreset = isPresetOption(category.id, value)\n return (\n <DropdownMenuItem\n key={value}\n className=\"cursor-pointer justify-between text-xs\"\n onSelect={(event) => {\n event.preventDefault()\n onToggleFilter(category.id, value)\n }}\n >\n {label}\n {selected ? (\n isPreset ? (\n <span className=\"text-brand-purple text-[10px] font-semibold\">\n {presetLabel}\n </span>\n ) : filterType === \"single\" ? (\n <span className=\"h-1.5 w-1.5 rounded-full bg-current\" />\n ) : (\n <span className=\"text-[10px] font-semibold text-brand-purple\">\n Applied\n </span>\n )\n ) : null}\n </DropdownMenuItem>\n )\n })}\n {filteredOptions.length === 0 && category.options.length > 0 && (\n <div className=\"p-2 text-center text-xs text-muted-foreground\">\n No matches\n </div>\n )}\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n )\n })}\n\n {visibleCategories.length === 0 ? (\n <div className=\"p-2 text-center text-xs text-muted-foreground\">\n No filters found\n </div>\n ) : null}\n </div>\n\n {hasConditionBuilder ? (\n <div className=\"border-t border-border p-1\">\n <PopoverPrimitive.Root\n open={conditionBuilderOpen}\n onOpenChange={setConditionBuilderOpen}\n >\n <PopoverPrimitive.Trigger asChild>\n <DropdownMenuItem\n className=\"cursor-pointer py-1.5 text-xs\"\n onSelect={(event) => {\n event.preventDefault()\n setConditionBuilderOpen(true)\n }}\n >\n <Plus className=\"mr-2 h-3.5 w-3.5 text-muted-foreground\" />\n {conditionBuilderLabel}\n {conditionFilters.length > 0 ? (\n <span className=\"ml-auto rounded bg-muted px-1.5 py-0 text-[10px] font-semibold\">\n {conditionFilters.length}\n </span>\n ) : null}\n </DropdownMenuItem>\n </PopoverPrimitive.Trigger>\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n align=\"start\"\n side=\"right\"\n sideOffset={8}\n className=\"z-50 outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\"\n onEscapeKeyDown={() => setConditionBuilderOpen(false)}\n onInteractOutside={(event) => {\n const target = event.target as HTMLElement | null\n if (target?.closest('[data-slot=\"dropdown-menu-content\"]')) {\n event.preventDefault()\n }\n }}\n >\n <DataTableConditionFilter\n fields={conditionFields}\n conditions={conditionFilters}\n onConditionsChange={onConditionFiltersChange ?? (() => {})}\n />\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n </PopoverPrimitive.Root>\n </div>\n ) : null}\n </DropdownMenuContent>\n </DropdownMenu>\n )\n\n // If there are preset chips, wrap trigger + chips together\n if (presetChips.length > 0) {\n return (\n <div className=\"flex flex-wrap items-center gap-1.5\">\n {triggerButton}\n {presetChips.map((chip) => (\n <button\n key={`${chip.categoryId}-${chip.value}`}\n type=\"button\"\n onClick={() => onTogglePreset?.(chip.categoryId, chip.value)}\n className={cn(\n \"inline-flex items-center gap-1 rounded-md border px-2 py-0.5 text-[11px] font-medium transition-colors\",\n chip.active\n ? \"border-dashed border-brand-purple/30 bg-brand-purple/5 text-brand-purple/80\"\n : \"border-border/40 bg-transparent text-muted-foreground/60 line-through\"\n )}\n >\n <span className=\"text-brand-purple/50 text-[10px]\">\n {presetLabel}:{\" \"}\n </span>\n {chip.label}\n </button>\n ))}\n </div>\n )\n }\n\n return triggerButton\n}\n"],"mappings":"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","names":["_a","_b"]}
@@ -174,12 +174,12 @@ function EntityPanelTabs({
174
174
  )) });
175
175
  }
176
176
  function EntityMetadataGrid({ fields }) {
177
- return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 md:grid-cols-[140px_1fr] gap-y-3 gap-x-4 mb-7 text-[13px] overflow-hidden", children: fields.map((field, idx) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
178
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-muted-foreground text-[13px] font-normal", children: [
179
- /* @__PURE__ */ jsx(field.icon, { className: "w-3.5 h-3.5 shrink-0" }),
180
- /* @__PURE__ */ jsx("span", { children: field.label })
177
+ return /* @__PURE__ */ jsx("div", { className: "mb-7 grid min-w-0 grid-cols-1 gap-x-4 gap-y-3 overflow-hidden text-[13px] md:grid-cols-[minmax(0,140px)_minmax(0,1fr)]", children: fields.map((field, idx) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
178
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start gap-1.5 text-[13px] font-normal text-muted-foreground", children: [
179
+ /* @__PURE__ */ jsx(field.icon, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }),
180
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 break-words leading-relaxed [overflow-wrap:anywhere]", children: field.label })
181
181
  ] }),
182
- /* @__PURE__ */ jsx("div", { className: "min-w-0 truncate text-foreground", children: field.value })
182
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 whitespace-normal break-words leading-relaxed text-foreground [overflow-wrap:anywhere] [&_*]:max-w-full [&_*]:[overflow-wrap:anywhere] [&_a]:break-all", children: field.value })
183
183
  ] }, idx)) });
184
184
  }
185
185
  function EntitySection({