fluentui-extended 2026.8.30 → 2026.8.35

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/README.md CHANGED
@@ -293,6 +293,45 @@ Run the test harness with `npm run harness` to see all examples in action.
293
293
  | `disabled` | `boolean` | `false` | Disable the lookup |
294
294
  | `open` | `boolean` | - | Controlled open state for the dropdown |
295
295
  | `onOpenChange` | `(open: boolean) => void` | - | Callback when dropdown open state changes |
296
+ | `disableClientFilter` | `boolean` | `false` | Disable client-side filtering of options. Use this when filtering is performed server-side via `onSearchChange` |
297
+ | `searchFields` | `string` | - | Hidden searchable text (never rendered). Use this to include additional searchable content (codes, IDs) while displaying JSX in `secondaryText` |
298
+
299
+ ### Client-Side Filtering
300
+
301
+ By default, the Lookup component filters options client-side as the user types. The filtering logic works as follows:
302
+
303
+ 1. **Primary field (`text`)** — Always searched, regardless of other props
304
+ 2. **Search fields (`searchFields`)** — If provided, this hidden text is searched (useful when `secondaryText` is JSX)
305
+ 3. **Secondary text (`secondaryText`)** — Only searched if it's a string (JSX elements are skipped)
306
+
307
+ This allows you to use rich JSX (badges, icons) in `secondaryText` while still providing searchable text via `searchFields`:
308
+
309
+ ```tsx
310
+ const options: LookupOption[] = [
311
+ {
312
+ key: 'PROD-001',
313
+ text: 'Acme Widget', // Always searchable
314
+ searchFields: 'PROD-001 SKU-12345 acme-widget', // Hidden searchable text
315
+ secondaryText: ( // Rich display (not searchable)
316
+ <span style={{ display: 'flex', gap: 4 }}>
317
+ <Badge size="small">PROD-001</Badge>
318
+ <Badge size="small" color="brand">SKU-12345</Badge>
319
+ </span>
320
+ ),
321
+ },
322
+ ];
323
+ ```
324
+
325
+ **Server-Side Filtering:** When using `onSearchChange` to fetch results from an API, set `disableClientFilter={true}` to prevent the client from re-filtering server results:
326
+
327
+ ```tsx
328
+ <Lookup
329
+ options={apiResults}
330
+ onSearchChange={(searchText) => fetchFromApi(searchText)}
331
+ disableClientFilter={true} // API already filtered the results
332
+ loading={isLoading}
333
+ />
334
+ ```
296
335
 
297
336
  ### Cross-Document Support (Dynamics 365 Iframes)
298
337
 
@@ -327,6 +366,7 @@ interface LookupOption {
327
366
  key: string; // Unique identifier (required)
328
367
  text: string; // Display text (required)
329
368
  secondaryText?: ReactNode; // Secondary line - string, Badge, or JSX
369
+ searchFields?: string; // Hidden searchable text (never rendered)
330
370
  icon?: ReactNode; // Icon component (e.g., <BuildingRegular />)
331
371
  details?: LookupOptionDetail[]; // Expandable details (chevron appears)
332
372
  data?: unknown; // Custom data payload for your app
@@ -339,7 +379,7 @@ interface LookupOptionDetail {
339
379
  }
340
380
  ```
341
381
 
342
- > **Note:** Both `secondaryText` and `details` support React elements, not just strings. See [Rich Secondary Text](#rich-secondary-text-with-react-elements) for examples.
382
+ > **Note:** Both `secondaryText` and `details` support React elements, not just strings. See [Rich Secondary Text](#rich-secondary-text-with-react-elements) for examples. When using JSX in `secondaryText`, use `searchFields` to provide hidden searchable text (see [Client-Side Filtering](#client-side-filtering)).
343
383
 
344
384
  ## Keyboard Navigation
345
385
 
package/dist/index.d.mts CHANGED
@@ -14,6 +14,13 @@ interface LookupOption {
14
14
  text: string;
15
15
  /** Optional secondary text - can be a string or React element */
16
16
  secondaryText?: React.ReactNode;
17
+ /**
18
+ * Optional searchable text that is never rendered. Use this to include
19
+ * additional searchable content (codes, IDs, etc.) without affecting display.
20
+ * Client-side filtering will search this field in addition to `text` and
21
+ * string `secondaryText`.
22
+ */
23
+ searchFields?: string;
17
24
  /** Optional icon to display */
18
25
  icon?: React.ReactNode;
19
26
  /** Optional expandable details */
@@ -64,6 +71,12 @@ interface LookupProps extends Omit<InputProps, 'onChange' | 'value'> {
64
71
  * Use together with `open` for controlled mode, or standalone to observe changes.
65
72
  */
66
73
  onOpenChange?: (open: boolean) => void;
74
+ /**
75
+ * Disable client-side filtering of options. Use this when filtering is
76
+ * performed server-side via `onSearchChange` and the returned options are
77
+ * already filtered. Defaults to `false` (client-side filtering enabled).
78
+ */
79
+ disableClientFilter?: boolean;
67
80
  }
68
81
 
69
82
  declare const Lookup: React.FC<LookupProps>;
@@ -127,6 +140,8 @@ interface QueryBuilderCondition {
127
140
  nestedLogic?: 'and' | 'or';
128
141
  /** Fields available for the related entity (loaded dynamically) */
129
142
  nestedFields?: QueryBuilderField[];
143
+ /** True when the attribute from parsed FetchXML didn't match any known field */
144
+ isUnknownField?: boolean;
130
145
  }
131
146
  interface QueryBuilderGroup {
132
147
  id: string;
@@ -203,6 +218,8 @@ interface QueryBuilderProps {
203
218
  onFetchEntityFields?: (entityLogicalName: string) => Promise<QueryBuilderField[]>;
204
219
  /** Debug/trace callback for logging component behavior */
205
220
  onTrace?: (message: string, data?: any) => void;
221
+ /** Enable verbose console.debug tracing (disabled by default) */
222
+ debug?: boolean;
206
223
  }
207
224
 
208
225
  /**
package/dist/index.d.ts CHANGED
@@ -14,6 +14,13 @@ interface LookupOption {
14
14
  text: string;
15
15
  /** Optional secondary text - can be a string or React element */
16
16
  secondaryText?: React.ReactNode;
17
+ /**
18
+ * Optional searchable text that is never rendered. Use this to include
19
+ * additional searchable content (codes, IDs, etc.) without affecting display.
20
+ * Client-side filtering will search this field in addition to `text` and
21
+ * string `secondaryText`.
22
+ */
23
+ searchFields?: string;
17
24
  /** Optional icon to display */
18
25
  icon?: React.ReactNode;
19
26
  /** Optional expandable details */
@@ -64,6 +71,12 @@ interface LookupProps extends Omit<InputProps, 'onChange' | 'value'> {
64
71
  * Use together with `open` for controlled mode, or standalone to observe changes.
65
72
  */
66
73
  onOpenChange?: (open: boolean) => void;
74
+ /**
75
+ * Disable client-side filtering of options. Use this when filtering is
76
+ * performed server-side via `onSearchChange` and the returned options are
77
+ * already filtered. Defaults to `false` (client-side filtering enabled).
78
+ */
79
+ disableClientFilter?: boolean;
67
80
  }
68
81
 
69
82
  declare const Lookup: React.FC<LookupProps>;
@@ -127,6 +140,8 @@ interface QueryBuilderCondition {
127
140
  nestedLogic?: 'and' | 'or';
128
141
  /** Fields available for the related entity (loaded dynamically) */
129
142
  nestedFields?: QueryBuilderField[];
143
+ /** True when the attribute from parsed FetchXML didn't match any known field */
144
+ isUnknownField?: boolean;
130
145
  }
131
146
  interface QueryBuilderGroup {
132
147
  id: string;
@@ -203,6 +218,8 @@ interface QueryBuilderProps {
203
218
  onFetchEntityFields?: (entityLogicalName: string) => Promise<QueryBuilderField[]>;
204
219
  /** Debug/trace callback for logging component behavior */
205
220
  onTrace?: (message: string, data?: any) => void;
221
+ /** Enable verbose console.debug tracing (disabled by default) */
222
+ debug?: boolean;
206
223
  }
207
224
 
208
225
  /**
package/dist/index.js CHANGED
@@ -281,6 +281,7 @@ var Lookup = ({
281
281
  footer,
282
282
  open: controlledOpen,
283
283
  onOpenChange,
284
+ disableClientFilter = false,
284
285
  ...inputProps
285
286
  }) => {
286
287
  const styles = useLookupStyles();
@@ -316,6 +317,9 @@ var Lookup = ({
316
317
  [selectedOptionProp, options, selectedKey, internalSelectedOption]
317
318
  );
318
319
  const filteredOptions = React3__namespace.useMemo(() => {
320
+ if (disableClientFilter) {
321
+ return options;
322
+ }
319
323
  if (!searchText || searchText.length < minSearchLength) {
320
324
  return options;
321
325
  }
@@ -324,12 +328,15 @@ var Lookup = ({
324
328
  if (opt.text.toLowerCase().includes(lowerSearch)) {
325
329
  return true;
326
330
  }
331
+ if (opt.searchFields && opt.searchFields.toLowerCase().includes(lowerSearch)) {
332
+ return true;
333
+ }
327
334
  if (typeof opt.secondaryText === "string") {
328
335
  return opt.secondaryText.toLowerCase().includes(lowerSearch);
329
336
  }
330
337
  return false;
331
338
  });
332
- }, [options, searchText, minSearchLength]);
339
+ }, [options, searchText, minSearchLength, disableClientFilter]);
333
340
  const highlightedOptionId = React3__namespace.useMemo(() => {
334
341
  if (!isOpen || highlightedIndex < 0 || highlightedIndex >= filteredOptions.length) {
335
342
  return void 0;
@@ -964,6 +971,72 @@ var useQueryBuilderStyles = reactComponents.makeStyles({
964
971
  backgroundColor: reactComponents.tokens.colorPaletteRedBackground1,
965
972
  borderRadius: reactComponents.tokens.borderRadiusSmall
966
973
  },
974
+ conditionUnknownBanner: {
975
+ display: "flex",
976
+ alignItems: "center",
977
+ gap: reactComponents.tokens.spacingHorizontalXS,
978
+ paddingTop: "2px",
979
+ paddingBottom: "2px",
980
+ paddingLeft: reactComponents.tokens.spacingHorizontalXS,
981
+ paddingRight: reactComponents.tokens.spacingHorizontalXS,
982
+ marginBottom: reactComponents.tokens.spacingVerticalXS,
983
+ backgroundColor: reactComponents.tokens.colorStatusWarningBackground1,
984
+ borderRadius: reactComponents.tokens.borderRadiusSmall,
985
+ fontSize: reactComponents.tokens.fontSizeBase100,
986
+ color: reactComponents.tokens.colorStatusWarningForeground3
987
+ },
988
+ conditionUnknownText: {
989
+ flex: "1 1 auto"
990
+ },
991
+ conditionUnknownButton: {
992
+ fontSize: reactComponents.tokens.fontSizeBase100,
993
+ minWidth: "0",
994
+ paddingTop: "0",
995
+ paddingBottom: "0",
996
+ paddingLeft: reactComponents.tokens.spacingHorizontalXS,
997
+ paddingRight: reactComponents.tokens.spacingHorizontalXS,
998
+ height: "20px",
999
+ color: reactComponents.tokens.colorStatusWarningForeground3
1000
+ },
1001
+ dialogSurfaceNarrow: {
1002
+ maxWidth: "600px"
1003
+ },
1004
+ dialogSurfaceCompact: {
1005
+ maxWidth: "500px"
1006
+ },
1007
+ dialogUploadContent: {
1008
+ display: "flex",
1009
+ flexDirection: "column",
1010
+ gap: reactComponents.tokens.spacingVerticalM
1011
+ },
1012
+ dialogValidationContent: {
1013
+ display: "flex",
1014
+ flexDirection: "column",
1015
+ gap: reactComponents.tokens.spacingVerticalL
1016
+ },
1017
+ monacoTextarea: {
1018
+ minHeight: "200px",
1019
+ fontFamily: reactComponents.tokens.fontFamilyMonospace
1020
+ },
1021
+ uploadErrorText: {
1022
+ color: reactComponents.tokens.colorStatusDangerForeground1
1023
+ },
1024
+ validationSectionTitle: {
1025
+ display: "flex",
1026
+ alignItems: "center",
1027
+ gap: reactComponents.tokens.spacingHorizontalXXS
1028
+ },
1029
+ validationApiRow: {
1030
+ display: "flex",
1031
+ alignItems: "center",
1032
+ gap: reactComponents.tokens.spacingHorizontalS
1033
+ },
1034
+ emptyRelatedEntity: {
1035
+ paddingTop: reactComponents.tokens.spacingVerticalS,
1036
+ paddingBottom: reactComponents.tokens.spacingVerticalS,
1037
+ color: reactComponents.tokens.colorNeutralForeground3,
1038
+ fontSize: reactComponents.tokens.fontSizeBase200
1039
+ },
967
1040
  validationIcon: {
968
1041
  marginRight: reactComponents.tokens.spacingHorizontalXS
969
1042
  },
@@ -2242,7 +2315,8 @@ var createParsedCondition = (attr, operator, value, value2, fieldMatch, valueDis
2242
2315
  value,
2243
2316
  value2: value2 ?? "",
2244
2317
  ...valueDisplayName ? { valueDisplayName } : {},
2245
- ...entityAlias ? { entityAlias } : {}
2318
+ ...entityAlias ? { entityAlias } : {},
2319
+ ...!fieldMatch ? { isUnknownField: true } : {}
2246
2320
  });
2247
2321
  var parseConditionElement = (condEl, fields) => {
2248
2322
  const attr = condEl.getAttribute("attribute") || "";
@@ -2889,14 +2963,16 @@ var enrichOptionsetFields = async (fields, entityName, trace) => {
2889
2963
  var QueryBuilder = (props) => {
2890
2964
  const styles = useQueryBuilderStyles();
2891
2965
  const trace = React3__namespace.useCallback((message, data) => {
2892
- console.debug(
2893
- "%c FluentUI-Extended ",
2894
- "background: #845EF7; color: white; padding: 2px 4px; border-radius: 2px; font-weight: bold;",
2895
- message,
2896
- data || ""
2897
- );
2966
+ if (props.debug) {
2967
+ console.debug(
2968
+ "%c FluentUI-Extended ",
2969
+ "background: #845EF7; color: white; padding: 2px 4px; border-radius: 2px; font-weight: bold;",
2970
+ message,
2971
+ data || ""
2972
+ );
2973
+ }
2898
2974
  props.onTrace?.(message, data);
2899
- }, [props]);
2975
+ }, [props.debug, props.onTrace]);
2900
2976
  const [loading, setLoading] = React3__namespace.useState(false);
2901
2977
  const [availableFields, setAvailableFields] = React3__namespace.useState(
2902
2978
  props.fields && props.fields.length > 0 ? props.fields : FALLBACK_FIELDS
@@ -3125,6 +3201,9 @@ var QueryBuilder = (props) => {
3125
3201
  if (condition.kind === "relatedEntity") {
3126
3202
  return condition;
3127
3203
  }
3204
+ if (condition.isUnknownField && !availableFields.some((f) => f.id === condition.fieldId)) {
3205
+ return condition;
3206
+ }
3128
3207
  const matchedField = availableFields.find((field) => field.id === condition.fieldId) || fallbackField;
3129
3208
  const operators = getOperatorsForType(matchedField.dataType);
3130
3209
  const nextOperator = operators.some((item) => item.value === condition.operator) ? condition.operator : operators[0]?.value;
@@ -3632,16 +3711,16 @@ var QueryBuilder = (props) => {
3632
3711
  onClick: onOpenUploadDialog
3633
3712
  },
3634
3713
  "Import FetchXML"
3635
- ), /* @__PURE__ */ React3__namespace.createElement(reactComponents.Dialog, { open: uploadDialogOpen, onOpenChange: (_, data) => setUploadDialogOpen(data.open) }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogSurface, { style: { maxWidth: "600px" } }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogBody, null, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogTitle, null, "Import FetchXML"), /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogContent, { style: { display: "flex", flexDirection: "column", gap: "12px" } }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, null, "Paste your FetchXML below to rebuild the query:"), /* @__PURE__ */ React3__namespace.createElement(
3714
+ ), /* @__PURE__ */ React3__namespace.createElement(reactComponents.Dialog, { open: uploadDialogOpen, onOpenChange: (_, data) => setUploadDialogOpen(data.open) }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogSurface, { className: styles.dialogSurfaceNarrow }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogBody, null, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogTitle, null, "Import FetchXML"), /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogContent, { className: styles.dialogUploadContent }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, null, "Paste your FetchXML below to rebuild the query:"), /* @__PURE__ */ React3__namespace.createElement(
3636
3715
  reactComponents.Textarea,
3637
3716
  {
3638
3717
  placeholder: "<fetch><entity name='account'><filter>...</filter></entity></fetch>",
3639
3718
  value: uploadXmlText,
3640
3719
  onChange: (_, data) => setUploadXmlText(data.value),
3641
- style: { minHeight: "200px", fontFamily: "monospace" },
3720
+ className: styles.monacoTextarea,
3642
3721
  resize: "vertical"
3643
3722
  }
3644
- ), uploadError && /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { style: { color: "var(--colorStatusDangerForeground1)" } }, uploadError)), /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogActions, null, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogTrigger, { disableButtonEnhancement: true }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Button, { appearance: "secondary" }, "Cancel")), /* @__PURE__ */ React3__namespace.createElement(reactComponents.Button, { appearance: "primary", onClick: onApplyUploadedXml, disabled: !uploadXmlText.trim() }, "Apply"))))), props.showValidateButton !== false && /* @__PURE__ */ React3__namespace.createElement(
3723
+ ), uploadError && /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { className: styles.uploadErrorText }, uploadError)), /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogActions, null, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogTrigger, { disableButtonEnhancement: true }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Button, { appearance: "secondary" }, "Cancel")), /* @__PURE__ */ React3__namespace.createElement(reactComponents.Button, { appearance: "primary", onClick: onApplyUploadedXml, disabled: !uploadXmlText.trim() }, "Apply"))))), props.showValidateButton !== false && /* @__PURE__ */ React3__namespace.createElement(
3645
3724
  reactComponents.Button,
3646
3725
  {
3647
3726
  size: "small",
@@ -3650,7 +3729,7 @@ var QueryBuilder = (props) => {
3650
3729
  onClick: onValidate
3651
3730
  },
3652
3731
  "Validate"
3653
- ), /* @__PURE__ */ React3__namespace.createElement(reactComponents.Dialog, { open: validationDialogOpen, onOpenChange: (_, data) => setValidationDialogOpen(data.open) }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogSurface, { style: { maxWidth: "500px" } }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogBody, null, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogTitle, null, "Query Validation"), /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogContent, { style: { display: "flex", flexDirection: "column", gap: "16px" } }, /* @__PURE__ */ React3__namespace.createElement("div", null, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { weight: "semibold", style: { display: "flex", alignItems: "center", gap: "4px" } }, validationResult?.isValid ? /* @__PURE__ */ React3__namespace.createElement("span", { className: styles.validationSuccess }, /* @__PURE__ */ React3__namespace.createElement(reactIcons.CheckmarkCircleRegular, { className: styles.validationIcon }), "Query Structure: Valid") : /* @__PURE__ */ React3__namespace.createElement("span", { className: styles.validationError }, /* @__PURE__ */ React3__namespace.createElement(reactIcons.WarningRegular, { className: styles.validationIcon }), "Query Structure: Errors Found")), !validationResult?.isValid && /* @__PURE__ */ React3__namespace.createElement("ul", { className: styles.validationErrorList }, validationResult?.errors.map((error, idx) => /* @__PURE__ */ React3__namespace.createElement("li", { key: idx, className: styles.validationErrorItem }, /* @__PURE__ */ React3__namespace.createElement("strong", null, error.fieldLabel, ":"), " ", error.message)))), /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.apiValidationSection }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { weight: "semibold", style: { display: "flex", alignItems: "center", gap: "4px" } }, "Dynamics 365 API Test"), !validationResult?.apiValidation?.available ? /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { className: styles.apiUnavailable }, "API validation unavailable \u2014 not running in Dynamics 365 environment.") : !validationResult?.isValid ? /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { className: styles.apiUnavailable }, "Fix query structure errors before testing against the API.") : apiValidating ? /* @__PURE__ */ React3__namespace.createElement("div", { style: { display: "flex", alignItems: "center", gap: "8px" } }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Spinner, { size: "tiny" }), /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, null, "Testing query against Dynamics 365...")) : validationResult?.apiValidation?.tested ? validationResult.apiValidation.success ? /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { className: styles.validationSuccess }, /* @__PURE__ */ React3__namespace.createElement(reactIcons.CheckmarkCircleRegular, { className: styles.validationIcon }), "Query executed successfully. ", validationResult.apiValidation.recordCount ?? 0, " record(s) would match.") : /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { className: styles.validationError }, /* @__PURE__ */ React3__namespace.createElement(reactIcons.WarningRegular, { className: styles.validationIcon }), "API Error: ", validationResult.apiValidation.errorMessage) : /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { className: styles.apiUnavailable }, "Waiting for validation..."))), /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogActions, null, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogTrigger, { disableButtonEnhancement: true }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Button, { appearance: "primary" }, "OK")))))), props.showDeleteAllFiltersButton !== false && /* @__PURE__ */ React3__namespace.createElement(
3732
+ ), /* @__PURE__ */ React3__namespace.createElement(reactComponents.Dialog, { open: validationDialogOpen, onOpenChange: (_, data) => setValidationDialogOpen(data.open) }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogSurface, { className: styles.dialogSurfaceCompact }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogBody, null, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogTitle, null, "Query Validation"), /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogContent, { className: styles.dialogValidationContent }, /* @__PURE__ */ React3__namespace.createElement("div", null, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { weight: "semibold", className: styles.validationSectionTitle }, validationResult?.isValid ? /* @__PURE__ */ React3__namespace.createElement("span", { className: styles.validationSuccess }, /* @__PURE__ */ React3__namespace.createElement(reactIcons.CheckmarkCircleRegular, { className: styles.validationIcon }), "Query Structure: Valid") : /* @__PURE__ */ React3__namespace.createElement("span", { className: styles.validationError }, /* @__PURE__ */ React3__namespace.createElement(reactIcons.WarningRegular, { className: styles.validationIcon }), "Query Structure: Errors Found")), !validationResult?.isValid && /* @__PURE__ */ React3__namespace.createElement("ul", { className: styles.validationErrorList }, validationResult?.errors.map((error, idx) => /* @__PURE__ */ React3__namespace.createElement("li", { key: idx, className: styles.validationErrorItem }, /* @__PURE__ */ React3__namespace.createElement("strong", null, error.fieldLabel, ":"), " ", error.message)))), /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.apiValidationSection }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { weight: "semibold", className: styles.validationSectionTitle }, "Dynamics 365 API Test"), !validationResult?.apiValidation?.available ? /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { className: styles.apiUnavailable }, "API validation unavailable \u2014 not running in Dynamics 365 environment.") : !validationResult?.isValid ? /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { className: styles.apiUnavailable }, "Fix query structure errors before testing against the API.") : apiValidating ? /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.validationApiRow }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Spinner, { size: "tiny" }), /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, null, "Testing query against Dynamics 365...")) : validationResult?.apiValidation?.tested ? validationResult.apiValidation.success ? /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { className: styles.validationSuccess }, /* @__PURE__ */ React3__namespace.createElement(reactIcons.CheckmarkCircleRegular, { className: styles.validationIcon }), "Query executed successfully. ", validationResult.apiValidation.recordCount ?? 0, " record(s) would match.") : /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { className: styles.validationError }, /* @__PURE__ */ React3__namespace.createElement(reactIcons.WarningRegular, { className: styles.validationIcon }), "API Error: ", validationResult.apiValidation.errorMessage) : /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { className: styles.apiUnavailable }, "Waiting for validation..."))), /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogActions, null, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogTrigger, { disableButtonEnhancement: true }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Button, { appearance: "primary" }, "OK")))))), props.showDeleteAllFiltersButton !== false && /* @__PURE__ */ React3__namespace.createElement(
3654
3733
  reactComponents.Button,
3655
3734
  {
3656
3735
  size: "small",
@@ -3852,17 +3931,27 @@ var QueryBuilder = (props) => {
3852
3931
  onClick: () => addNestedCondition(group.id, condition.id, nestedFields)
3853
3932
  },
3854
3933
  "Add condition"
3855
- ))), selectedRelated && nestedFields.length === 0 && nestedConditions.length === 0 && /* @__PURE__ */ React3__namespace.createElement("div", { style: { padding: "8px 0", color: "var(--colorNeutralForeground3)", fontSize: "12px" } }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Spinner, { size: "tiny", label: "Loading fields..." })), !selectedRelated && /* @__PURE__ */ React3__namespace.createElement("div", { style: { padding: "8px 0", color: "var(--colorNeutralForeground3)", fontSize: "12px" } }, "Select a related entity to add filter conditions.")));
3934
+ ))), selectedRelated && nestedFields.length === 0 && nestedConditions.length === 0 && /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.emptyRelatedEntity }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Spinner, { size: "tiny", label: "Loading fields..." })), !selectedRelated && /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.emptyRelatedEntity })));
3856
3935
  }
3936
+ const isFieldUnknown = condition.isUnknownField && !availableFields.some((f) => f.id === condition.fieldId);
3857
3937
  const selectedField = availableFields.find((field) => field.id === condition.fieldId) || defaultField;
3858
3938
  const operators = getOperatorsForType(selectedField.dataType);
3859
3939
  const isNullOperator = condition.operator === "null" || condition.operator === "notnull";
3860
3940
  const isBetween = condition.operator === "between";
3861
3941
  const conditionRowClass = reactComponents.mergeClasses(
3862
3942
  styles.conditionTreeRow,
3863
- invalidConditionIds.has(condition.id) && styles.conditionInvalid
3943
+ (invalidConditionIds.has(condition.id) || isFieldUnknown) && styles.conditionInvalid
3864
3944
  );
3865
- return /* @__PURE__ */ React3__namespace.createElement("div", { className: conditionRowClass, key: condition.id }, /* @__PURE__ */ React3__namespace.createElement("div", { className: connectorClass }, /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.conditionConnectorLine }), /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.conditionConnectorBranch })), /* @__PURE__ */ React3__namespace.createElement("div", { className: rowGridClass, role: "row" }, /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.fieldCell, role: "gridcell" }, /* @__PURE__ */ React3__namespace.createElement(
3945
+ return /* @__PURE__ */ React3__namespace.createElement("div", { className: conditionRowClass, key: condition.id }, /* @__PURE__ */ React3__namespace.createElement("div", { className: connectorClass }, /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.conditionConnectorLine }), /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.conditionConnectorBranch })), isFieldUnknown ? /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.conditionUnknownBanner }, /* @__PURE__ */ React3__namespace.createElement("span", null, "\u26A0"), /* @__PURE__ */ React3__namespace.createElement("span", { className: styles.conditionUnknownText }, "Unknown field ", /* @__PURE__ */ React3__namespace.createElement("strong", null, condition.fieldId), " \u2014 not found in entity metadata. Update or remove this condition before saving."), /* @__PURE__ */ React3__namespace.createElement(
3946
+ reactComponents.Button,
3947
+ {
3948
+ appearance: "subtle",
3949
+ size: "small",
3950
+ className: styles.conditionUnknownButton,
3951
+ onClick: () => removeCondition(group.id, condition.id)
3952
+ },
3953
+ "Remove"
3954
+ )) : /* @__PURE__ */ React3__namespace.createElement("div", { className: rowGridClass, role: "row" }, /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.fieldCell, role: "gridcell" }, /* @__PURE__ */ React3__namespace.createElement(
3866
3955
  Lookup,
3867
3956
  {
3868
3957
  className: styles.compactControl,