fluentui-extended 2026.2.6 → 2026.2.8
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 +2 -2
- package/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +183 -124
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +183 -124
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -349,7 +349,7 @@ The Validate button checks query structure and optionally tests against the Dyna
|
|
|
349
349
|
```
|
|
350
350
|
|
|
351
351
|
When running inside Dynamics 365:
|
|
352
|
-
-
|
|
352
|
+
- Uses native fetch to `/api/data/v9.2/` endpoints
|
|
353
353
|
- Executes a test query with `$top=1&$count=true`
|
|
354
354
|
- Shows record count or API error message
|
|
355
355
|
|
|
@@ -405,7 +405,7 @@ const fields: QueryBuilderField[] = [
|
|
|
405
405
|
|------|------|---------|-------------|
|
|
406
406
|
| `entityName` | `string` | - | Logical name of the entity (required) |
|
|
407
407
|
| `entityDisplayName` | `string` | - | Display name shown in header |
|
|
408
|
-
| `fields` | `QueryBuilderField[]` | - | Fields for filtering (auto-loaded
|
|
408
|
+
| `fields` | `QueryBuilderField[]` | - | Fields for filtering (auto-loaded via Web API if omitted) |
|
|
409
409
|
| `initialFetchXml` | `string` | - | FetchXML to pre-populate the query builder |
|
|
410
410
|
| `initialState` | `QueryBuilderState` | - | Initial query state object |
|
|
411
411
|
| `onSerializedChange` | `(result: QueryBuilderApplyResult) => void` | - | Called when query changes |
|
package/dist/index.d.mts
CHANGED
|
@@ -156,7 +156,7 @@ interface QueryBuilderLookupOption {
|
|
|
156
156
|
}
|
|
157
157
|
interface QueryBuilderProps {
|
|
158
158
|
entityName: string;
|
|
159
|
-
/** Entity set name for OData queries (e.g., "accounts"). If not provided, will be fetched from
|
|
159
|
+
/** Entity set name for OData queries (e.g., "accounts"). If not provided, will be fetched from the Web API. */
|
|
160
160
|
entitySetName?: string;
|
|
161
161
|
entityDisplayName?: string;
|
|
162
162
|
fields?: QueryBuilderField[];
|
|
@@ -189,6 +189,8 @@ interface QueryBuilderProps {
|
|
|
189
189
|
onSerializedChange?: (result: QueryBuilderApplyResult) => void;
|
|
190
190
|
/** Callback for lookup field search - returns options for the lookup dropdown */
|
|
191
191
|
onLookupSearch?: (fieldId: string, searchText: string) => Promise<QueryBuilderLookupOption[]> | QueryBuilderLookupOption[];
|
|
192
|
+
/** Callback to fetch fields for a related entity. If provided, this is used instead of the native Web API. */
|
|
193
|
+
onFetchEntityFields?: (entityLogicalName: string) => Promise<QueryBuilderField[]>;
|
|
192
194
|
}
|
|
193
195
|
|
|
194
196
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -156,7 +156,7 @@ interface QueryBuilderLookupOption {
|
|
|
156
156
|
}
|
|
157
157
|
interface QueryBuilderProps {
|
|
158
158
|
entityName: string;
|
|
159
|
-
/** Entity set name for OData queries (e.g., "accounts"). If not provided, will be fetched from
|
|
159
|
+
/** Entity set name for OData queries (e.g., "accounts"). If not provided, will be fetched from the Web API. */
|
|
160
160
|
entitySetName?: string;
|
|
161
161
|
entityDisplayName?: string;
|
|
162
162
|
fields?: QueryBuilderField[];
|
|
@@ -189,6 +189,8 @@ interface QueryBuilderProps {
|
|
|
189
189
|
onSerializedChange?: (result: QueryBuilderApplyResult) => void;
|
|
190
190
|
/** Callback for lookup field search - returns options for the lookup dropdown */
|
|
191
191
|
onLookupSearch?: (fieldId: string, searchText: string) => Promise<QueryBuilderLookupOption[]> | QueryBuilderLookupOption[];
|
|
192
|
+
/** Callback to fetch fields for a related entity. If provided, this is used instead of the native Web API. */
|
|
193
|
+
onFetchEntityFields?: (entityLogicalName: string) => Promise<QueryBuilderField[]>;
|
|
192
194
|
}
|
|
193
195
|
|
|
194
196
|
/**
|
package/dist/index.js
CHANGED
|
@@ -2452,9 +2452,13 @@ var isValueEmpty = (value) => {
|
|
|
2452
2452
|
return String(value).trim() === "";
|
|
2453
2453
|
};
|
|
2454
2454
|
var dataTypeFromAttribute = (attribute) => {
|
|
2455
|
-
const
|
|
2455
|
+
const typeValue = attribute?.AttributeTypeName?.Value || attribute?.AttributeType || attribute?.Type || "";
|
|
2456
|
+
const type = String(typeValue).toLowerCase();
|
|
2457
|
+
if (attribute?.Targets && Array.isArray(attribute.Targets) && attribute.Targets.length > 0) {
|
|
2458
|
+
return "lookup";
|
|
2459
|
+
}
|
|
2456
2460
|
if (["picklist", "state", "status"].includes(type)) return "optionset";
|
|
2457
|
-
if (["lookup", "customer", "owner", "partylist", "uniqueidentifier"].includes(type)) return "lookup";
|
|
2461
|
+
if (["lookup", "customer", "owner", "partylist", "uniqueidentifier", "lookuptype"].includes(type)) return "lookup";
|
|
2458
2462
|
if (["datetime"].includes(type)) return "datetime";
|
|
2459
2463
|
if (["boolean"].includes(type)) return "boolean";
|
|
2460
2464
|
if (["integer", "decimal", "double", "money", "bigint", "int"].includes(type)) return "number";
|
|
@@ -2532,9 +2536,8 @@ var LookupValueInput = ({
|
|
|
2532
2536
|
const [lookupLoading, setLookupLoading] = React3__namespace.useState(false);
|
|
2533
2537
|
const [resultCount, setResultCount] = React3__namespace.useState(0);
|
|
2534
2538
|
const [headerText, setHeaderText] = React3__namespace.useState("");
|
|
2535
|
-
const
|
|
2536
|
-
|
|
2537
|
-
if (!xrm?.WebApi?.retrieveMultipleRecords || !targets || targets.length === 0) {
|
|
2539
|
+
const searchUsingWebApi = React3__namespace.useCallback(async (searchText, limit = 15) => {
|
|
2540
|
+
if (!targets || targets.length === 0) {
|
|
2538
2541
|
return { results: [], entityDisplayName: "" };
|
|
2539
2542
|
}
|
|
2540
2543
|
const results = [];
|
|
@@ -2546,12 +2549,20 @@ var LookupValueInput = ({
|
|
|
2546
2549
|
}
|
|
2547
2550
|
try {
|
|
2548
2551
|
const nameAttr = target.primaryNameAttribute;
|
|
2549
|
-
let
|
|
2552
|
+
let queryOptions = `$select=${nameAttr}&$top=${limit}`;
|
|
2550
2553
|
if (searchText) {
|
|
2551
|
-
|
|
2554
|
+
queryOptions += `&$filter=contains(${nameAttr},'${searchText.replace(/'/g, "''")}')`;
|
|
2552
2555
|
}
|
|
2553
|
-
const response = await
|
|
2554
|
-
|
|
2556
|
+
const response = await fetch(`/api/data/v9.2/${target.entitySetName}?${queryOptions}`, {
|
|
2557
|
+
headers: {
|
|
2558
|
+
"OData-MaxVersion": "4.0",
|
|
2559
|
+
"OData-Version": "4.0",
|
|
2560
|
+
"Accept": "application/json"
|
|
2561
|
+
}
|
|
2562
|
+
});
|
|
2563
|
+
if (!response.ok) continue;
|
|
2564
|
+
const data = await response.json();
|
|
2565
|
+
const records = data.value || [];
|
|
2555
2566
|
for (const record of records) {
|
|
2556
2567
|
const idField = `${target.entityLogicalName}id`;
|
|
2557
2568
|
const id = record[idField] || record.id || "";
|
|
@@ -2570,12 +2581,11 @@ var LookupValueInput = ({
|
|
|
2570
2581
|
}, [targets]);
|
|
2571
2582
|
React3__namespace.useEffect(() => {
|
|
2572
2583
|
const loadInitialRecords = async () => {
|
|
2573
|
-
const xrm = window.Xrm;
|
|
2574
2584
|
const hasValidTargets2 = targets && targets.length > 0 && targets.some((t) => t.entitySetName && t.primaryNameAttribute);
|
|
2575
|
-
if (!disabled && hasValidTargets2 &&
|
|
2585
|
+
if (!disabled && hasValidTargets2 && !onLookupSearch) {
|
|
2576
2586
|
setLookupLoading(true);
|
|
2577
2587
|
try {
|
|
2578
|
-
const searchResult = await
|
|
2588
|
+
const searchResult = await searchUsingWebApi("", 5);
|
|
2579
2589
|
setLookupOptions(searchResult.results);
|
|
2580
2590
|
setResultCount(searchResult.results.length);
|
|
2581
2591
|
setHeaderText(searchResult.entityDisplayName);
|
|
@@ -2585,7 +2595,7 @@ var LookupValueInput = ({
|
|
|
2585
2595
|
}
|
|
2586
2596
|
};
|
|
2587
2597
|
loadInitialRecords();
|
|
2588
|
-
}, [targets, disabled, onLookupSearch,
|
|
2598
|
+
}, [targets, disabled, onLookupSearch, searchUsingWebApi]);
|
|
2589
2599
|
const handleSearchChange = React3__namespace.useCallback(
|
|
2590
2600
|
async (searchText) => {
|
|
2591
2601
|
setLookupLoading(true);
|
|
@@ -2601,7 +2611,7 @@ var LookupValueInput = ({
|
|
|
2601
2611
|
}));
|
|
2602
2612
|
entityName = targets?.[0]?.displayName || "";
|
|
2603
2613
|
} else {
|
|
2604
|
-
const searchResult = await
|
|
2614
|
+
const searchResult = await searchUsingWebApi(searchText);
|
|
2605
2615
|
results = searchResult.results;
|
|
2606
2616
|
entityName = searchResult.entityDisplayName;
|
|
2607
2617
|
}
|
|
@@ -2612,7 +2622,7 @@ var LookupValueInput = ({
|
|
|
2612
2622
|
setLookupLoading(false);
|
|
2613
2623
|
}
|
|
2614
2624
|
},
|
|
2615
|
-
[fieldId, onLookupSearch,
|
|
2625
|
+
[fieldId, onLookupSearch, searchUsingWebApi, targets]
|
|
2616
2626
|
);
|
|
2617
2627
|
const handleOptionSelect = React3__namespace.useCallback(
|
|
2618
2628
|
(option) => {
|
|
@@ -2727,67 +2737,118 @@ var QueryBuilder = (props) => {
|
|
|
2727
2737
|
}
|
|
2728
2738
|
setLoading(true);
|
|
2729
2739
|
try {
|
|
2730
|
-
const
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2740
|
+
const entityResponse = await fetch(
|
|
2741
|
+
`/api/data/v9.2/EntityDefinitions(LogicalName='${props.entityName}')?$select=EntitySetName,DisplayName,PrimaryNameAttribute`,
|
|
2742
|
+
{
|
|
2743
|
+
headers: {
|
|
2744
|
+
"OData-MaxVersion": "4.0",
|
|
2745
|
+
"OData-Version": "4.0",
|
|
2746
|
+
"Accept": "application/json"
|
|
2747
|
+
}
|
|
2735
2748
|
}
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2749
|
+
);
|
|
2750
|
+
if (!entityResponse.ok) {
|
|
2751
|
+
console.warn("[QueryBuilder] Failed to fetch entity metadata");
|
|
2752
|
+
return;
|
|
2753
|
+
}
|
|
2754
|
+
const entityMetadata = await entityResponse.json();
|
|
2755
|
+
if (entityMetadata?.EntitySetName && !disposed) {
|
|
2756
|
+
setEntitySetName(entityMetadata.EntitySetName);
|
|
2757
|
+
}
|
|
2758
|
+
const attributesResponse = await fetch(
|
|
2759
|
+
`/api/data/v9.2/EntityDefinitions(LogicalName='${props.entityName}')/Attributes?$select=LogicalName,SchemaName,DisplayName,AttributeType,AttributeTypeName`,
|
|
2760
|
+
{
|
|
2761
|
+
headers: {
|
|
2762
|
+
"OData-MaxVersion": "4.0",
|
|
2763
|
+
"OData-Version": "4.0",
|
|
2764
|
+
"Accept": "application/json"
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
);
|
|
2768
|
+
if (!attributesResponse.ok) {
|
|
2769
|
+
console.warn("[QueryBuilder] Failed to fetch entity attributes");
|
|
2770
|
+
return;
|
|
2771
|
+
}
|
|
2772
|
+
const attributesData = await attributesResponse.json();
|
|
2773
|
+
const attributesArray = attributesData.value || [];
|
|
2774
|
+
const lookupResponse = await fetch(
|
|
2775
|
+
`/api/data/v9.2/EntityDefinitions(LogicalName='${props.entityName}')/Attributes/Microsoft.Dynamics.CRM.LookupAttributeMetadata?$select=LogicalName,SchemaName,DisplayName,AttributeType,AttributeTypeName,Targets`,
|
|
2776
|
+
{
|
|
2777
|
+
headers: {
|
|
2778
|
+
"OData-MaxVersion": "4.0",
|
|
2779
|
+
"OData-Version": "4.0",
|
|
2780
|
+
"Accept": "application/json"
|
|
2745
2781
|
}
|
|
2746
2782
|
}
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2783
|
+
);
|
|
2784
|
+
const lookupData = lookupResponse.ok ? await lookupResponse.json() : { value: [] };
|
|
2785
|
+
const lookupAttributes = lookupData.value || [];
|
|
2786
|
+
const lookupMap = new Map(lookupAttributes.map((attr) => [attr.LogicalName, attr]));
|
|
2787
|
+
const mergedAttributes = attributesArray.map((attr) => {
|
|
2788
|
+
const lookupAttr = lookupMap.get(attr.LogicalName);
|
|
2789
|
+
if (lookupAttr?.Targets) {
|
|
2790
|
+
return { ...attr, Targets: lookupAttr.Targets };
|
|
2791
|
+
}
|
|
2792
|
+
return attr;
|
|
2793
|
+
});
|
|
2794
|
+
const targetEntityNames = /* @__PURE__ */ new Set();
|
|
2795
|
+
for (const attribute of mergedAttributes) {
|
|
2796
|
+
if (Array.isArray(attribute?.Targets)) {
|
|
2797
|
+
for (const target of attribute.Targets) {
|
|
2798
|
+
const entityName = typeof target === "string" ? target : target?.entityLogicalName;
|
|
2799
|
+
if (entityName) targetEntityNames.add(entityName);
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
const targetMetadataCache = {};
|
|
2804
|
+
for (const targetEntityName of targetEntityNames) {
|
|
2805
|
+
try {
|
|
2806
|
+
const targetResponse = await fetch(
|
|
2807
|
+
`/api/data/v9.2/EntityDefinitions(LogicalName='${targetEntityName}')?$select=EntitySetName,DisplayName,PrimaryNameAttribute`,
|
|
2808
|
+
{
|
|
2809
|
+
headers: {
|
|
2810
|
+
"OData-MaxVersion": "4.0",
|
|
2811
|
+
"OData-Version": "4.0",
|
|
2812
|
+
"Accept": "application/json"
|
|
2813
|
+
}
|
|
2814
|
+
}
|
|
2815
|
+
);
|
|
2816
|
+
if (targetResponse.ok) {
|
|
2817
|
+
const targetMeta = await targetResponse.json();
|
|
2751
2818
|
targetMetadataCache[targetEntityName] = {
|
|
2752
2819
|
entitySetName: targetMeta?.EntitySetName,
|
|
2753
2820
|
displayName: targetMeta?.DisplayName?.UserLocalizedLabel?.Label || targetMeta?.LogicalName,
|
|
2754
2821
|
primaryNameAttribute: targetMeta?.PrimaryNameAttribute
|
|
2755
2822
|
};
|
|
2756
|
-
} catch (targetErr) {
|
|
2757
|
-
console.warn(`[QueryBuilder] Could not fetch metadata for target entity "${targetEntityName}":`, targetErr);
|
|
2758
2823
|
}
|
|
2824
|
+
} catch (targetErr) {
|
|
2825
|
+
console.warn(`[QueryBuilder] Could not fetch metadata for target entity "${targetEntityName}":`, targetErr);
|
|
2759
2826
|
}
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
})).filter((option) => option.value !== void 0 && option.value !== null) : void 0;
|
|
2767
|
-
const targets = dataType === "lookup" && Array.isArray(attribute.Targets) ? attribute.Targets.map((target) => {
|
|
2768
|
-
const entityLogicalName = typeof target === "string" ? target : target?.entityLogicalName || target;
|
|
2769
|
-
const cached = targetMetadataCache[entityLogicalName] || {};
|
|
2770
|
-
return {
|
|
2771
|
-
entityLogicalName,
|
|
2772
|
-
entitySetName: target?.entitySetName || cached.entitySetName,
|
|
2773
|
-
displayName: target?.displayName || cached.displayName,
|
|
2774
|
-
primaryNameAttribute: target?.primaryNameAttribute || cached.primaryNameAttribute
|
|
2775
|
-
};
|
|
2776
|
-
}) : void 0;
|
|
2777
|
-
const displayName = attribute.DisplayName;
|
|
2778
|
-
const label = typeof displayName === "string" ? displayName : displayName?.UserLocalizedLabel?.Label || attribute.SchemaName || attribute.LogicalName;
|
|
2827
|
+
}
|
|
2828
|
+
const resolvedFields = mergedAttributes.filter((attribute) => attribute?.LogicalName && attribute?.IsValidForAdvancedFind !== false).map((attribute) => {
|
|
2829
|
+
const dataType = dataTypeFromAttribute(attribute);
|
|
2830
|
+
const targets = dataType === "lookup" && Array.isArray(attribute.Targets) ? attribute.Targets.map((target) => {
|
|
2831
|
+
const entityLogicalName = typeof target === "string" ? target : target?.entityLogicalName || target;
|
|
2832
|
+
const cached = targetMetadataCache[entityLogicalName] || {};
|
|
2779
2833
|
return {
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
options,
|
|
2785
|
-
targets
|
|
2834
|
+
entityLogicalName,
|
|
2835
|
+
entitySetName: target?.entitySetName || cached.entitySetName,
|
|
2836
|
+
displayName: target?.displayName || cached.displayName,
|
|
2837
|
+
primaryNameAttribute: target?.primaryNameAttribute || cached.primaryNameAttribute
|
|
2786
2838
|
};
|
|
2787
|
-
})
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2839
|
+
}) : void 0;
|
|
2840
|
+
const displayName = attribute.DisplayName;
|
|
2841
|
+
const label = typeof displayName === "string" ? displayName : displayName?.UserLocalizedLabel?.Label || attribute.SchemaName || attribute.LogicalName;
|
|
2842
|
+
return {
|
|
2843
|
+
id: attribute.LogicalName,
|
|
2844
|
+
label,
|
|
2845
|
+
schemaName: attribute.SchemaName,
|
|
2846
|
+
dataType,
|
|
2847
|
+
targets
|
|
2848
|
+
};
|
|
2849
|
+
}).sort((left, right) => String(left.label).localeCompare(String(right.label), void 0, { sensitivity: "base" }));
|
|
2850
|
+
if (!disposed && resolvedFields.length > 0) {
|
|
2851
|
+
setAvailableFields(resolvedFields);
|
|
2791
2852
|
}
|
|
2792
2853
|
} catch (error) {
|
|
2793
2854
|
console.error("[QueryBuilder] Error loading fields:", error);
|
|
@@ -2873,45 +2934,48 @@ var QueryBuilder = (props) => {
|
|
|
2873
2934
|
const loadRelatedEntityFields = React3__namespace.useCallback(
|
|
2874
2935
|
async (groupId, conditionId, targetEntity) => {
|
|
2875
2936
|
try {
|
|
2876
|
-
|
|
2877
|
-
if (
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2937
|
+
let resolvedFields = [];
|
|
2938
|
+
if (props.onFetchEntityFields) {
|
|
2939
|
+
resolvedFields = await props.onFetchEntityFields(targetEntity);
|
|
2940
|
+
} else {
|
|
2941
|
+
const response = await fetch(
|
|
2942
|
+
`/api/data/v9.2/EntityDefinitions(LogicalName='${targetEntity}')/Attributes?$select=LogicalName,SchemaName,DisplayName,AttributeType,AttributeTypeName`,
|
|
2943
|
+
{
|
|
2944
|
+
headers: {
|
|
2945
|
+
"OData-MaxVersion": "4.0",
|
|
2946
|
+
"OData-Version": "4.0",
|
|
2947
|
+
"Accept": "application/json"
|
|
2948
|
+
}
|
|
2949
|
+
}
|
|
2950
|
+
);
|
|
2951
|
+
if (!response.ok) {
|
|
2952
|
+
console.warn("[QueryBuilder] Failed to fetch entity metadata:", response.status, response.statusText);
|
|
2953
|
+
return;
|
|
2954
|
+
}
|
|
2955
|
+
const data = await response.json();
|
|
2956
|
+
const attributesArray = data.value || [];
|
|
2957
|
+
if (attributesArray.length === 0) {
|
|
2958
|
+
console.warn("[QueryBuilder] No attributes found for entity:", targetEntity);
|
|
2959
|
+
return;
|
|
2960
|
+
}
|
|
2961
|
+
resolvedFields = attributesArray.filter((attr) => {
|
|
2962
|
+
if (!attr?.LogicalName) return false;
|
|
2963
|
+
const attrType = attr.AttributeType || attr.AttributeTypeName?.Value;
|
|
2964
|
+
if (attrType === "Virtual" || attrType === "CalendarRules") return false;
|
|
2965
|
+
if (attrType === "Uniqueidentifier" && !attr.LogicalName.endsWith("id")) return false;
|
|
2966
|
+
return true;
|
|
2967
|
+
}).map((attr) => {
|
|
2968
|
+
const dataType = dataTypeFromAttribute(attr);
|
|
2969
|
+
const displayName = attr.DisplayName;
|
|
2970
|
+
const label = typeof displayName === "string" ? displayName : displayName?.UserLocalizedLabel?.Label || attr.SchemaName || attr.LogicalName;
|
|
2971
|
+
return {
|
|
2972
|
+
id: attr.LogicalName,
|
|
2973
|
+
label,
|
|
2974
|
+
schemaName: attr.SchemaName,
|
|
2975
|
+
dataType
|
|
2976
|
+
};
|
|
2977
|
+
}).sort((a, b) => String(a.label).localeCompare(String(b.label), void 0, { sensitivity: "base" }));
|
|
2891
2978
|
}
|
|
2892
|
-
const resolvedFields = attributesArray.filter((attr) => {
|
|
2893
|
-
if (!attr?.LogicalName) return false;
|
|
2894
|
-
const attrType = attr.AttributeType || attr.AttributeTypeName?.Value;
|
|
2895
|
-
if (attrType === "Virtual" || attrType === "CalendarRules") return false;
|
|
2896
|
-
if (attrType === "Uniqueidentifier" && !attr.LogicalName.endsWith("id")) return false;
|
|
2897
|
-
return true;
|
|
2898
|
-
}).map((attr) => {
|
|
2899
|
-
const dataType = dataTypeFromAttribute(attr);
|
|
2900
|
-
const optionSet = attr?.OptionSet?.Options;
|
|
2901
|
-
const options = dataType === "optionset" && Array.isArray(optionSet) ? optionSet.map((opt) => ({
|
|
2902
|
-
label: opt?.Label?.UserLocalizedLabel?.Label || opt?.Label || String(opt?.Value),
|
|
2903
|
-
value: opt?.Value
|
|
2904
|
-
})).filter((opt) => opt.value !== void 0 && opt.value !== null) : void 0;
|
|
2905
|
-
const displayName = attr.DisplayName;
|
|
2906
|
-
const label = typeof displayName === "string" ? displayName : displayName?.UserLocalizedLabel?.Label || attr.SchemaName || attr.LogicalName;
|
|
2907
|
-
return {
|
|
2908
|
-
id: attr.LogicalName,
|
|
2909
|
-
label,
|
|
2910
|
-
schemaName: attr.SchemaName,
|
|
2911
|
-
dataType,
|
|
2912
|
-
options
|
|
2913
|
-
};
|
|
2914
|
-
}).sort((a, b) => String(a.label).localeCompare(String(b.label), void 0, { sensitivity: "base" }));
|
|
2915
2979
|
if (resolvedFields.length > 0) {
|
|
2916
2980
|
const defaultCondition = createCondition(resolvedFields[0]);
|
|
2917
2981
|
updateGroup(groupId, (group) => ({
|
|
@@ -2931,7 +2995,7 @@ var QueryBuilder = (props) => {
|
|
|
2931
2995
|
console.error("[QueryBuilder] Error loading related entity fields:", error);
|
|
2932
2996
|
}
|
|
2933
2997
|
},
|
|
2934
|
-
[updateGroup]
|
|
2998
|
+
[props.onFetchEntityFields, updateGroup]
|
|
2935
2999
|
);
|
|
2936
3000
|
const addNestedCondition = React3__namespace.useCallback(
|
|
2937
3001
|
(groupId, conditionId, nestedFields) => {
|
|
@@ -3054,24 +3118,8 @@ var QueryBuilder = (props) => {
|
|
|
3054
3118
|
if (!validationResult) return /* @__PURE__ */ new Set();
|
|
3055
3119
|
return new Set(validationResult.errors.map((e) => e.conditionId).filter(Boolean));
|
|
3056
3120
|
}, [validationResult]);
|
|
3057
|
-
const isXrmAvailable = React3__namespace.useMemo(() => {
|
|
3058
|
-
try {
|
|
3059
|
-
return typeof window.Xrm?.WebApi?.retrieveMultipleRecords === "function";
|
|
3060
|
-
} catch {
|
|
3061
|
-
return false;
|
|
3062
|
-
}
|
|
3063
|
-
}, []);
|
|
3064
3121
|
const onValidate = React3__namespace.useCallback(async () => {
|
|
3065
3122
|
const result = validateQueryBuilderState(builderState, availableFields);
|
|
3066
|
-
if (!isXrmAvailable) {
|
|
3067
|
-
result.apiValidation = {
|
|
3068
|
-
available: false,
|
|
3069
|
-
tested: false
|
|
3070
|
-
};
|
|
3071
|
-
setValidationResult(result);
|
|
3072
|
-
setValidationDialogOpen(true);
|
|
3073
|
-
return;
|
|
3074
|
-
}
|
|
3075
3123
|
if (!result.isValid) {
|
|
3076
3124
|
result.apiValidation = {
|
|
3077
3125
|
available: true,
|
|
@@ -3085,18 +3133,29 @@ var QueryBuilder = (props) => {
|
|
|
3085
3133
|
setValidationResult(result);
|
|
3086
3134
|
setValidationDialogOpen(true);
|
|
3087
3135
|
try {
|
|
3088
|
-
const Xrm = window.Xrm;
|
|
3089
3136
|
const { odataFilter } = serializeQueryBuilderState(builderState, availableFields, props.entityName, entitySetName);
|
|
3090
|
-
const options = odataFilter ? `?$filter=${odataFilter}&$top=1&$count=true` : "?$top=1&$count=true";
|
|
3091
3137
|
const entitySetForApi = entitySetName || props.entityName;
|
|
3092
|
-
const
|
|
3138
|
+
const queryOptions = odataFilter ? `$filter=${odataFilter}&$top=1&$count=true` : "$top=1&$count=true";
|
|
3139
|
+
const response = await fetch(`/api/data/v9.2/${entitySetForApi}?${queryOptions}`, {
|
|
3140
|
+
headers: {
|
|
3141
|
+
"OData-MaxVersion": "4.0",
|
|
3142
|
+
"OData-Version": "4.0",
|
|
3143
|
+
"Accept": "application/json",
|
|
3144
|
+
"Prefer": 'odata.include-annotations="*"'
|
|
3145
|
+
}
|
|
3146
|
+
});
|
|
3147
|
+
if (!response.ok) {
|
|
3148
|
+
const errorText = await response.text();
|
|
3149
|
+
throw new Error(errorText || `HTTP ${response.status}`);
|
|
3150
|
+
}
|
|
3151
|
+
const data = await response.json();
|
|
3093
3152
|
setValidationResult({
|
|
3094
3153
|
...result,
|
|
3095
3154
|
apiValidation: {
|
|
3096
3155
|
available: true,
|
|
3097
3156
|
tested: true,
|
|
3098
3157
|
success: true,
|
|
3099
|
-
recordCount:
|
|
3158
|
+
recordCount: data["@odata.count"] ?? data.value?.length ?? 0
|
|
3100
3159
|
}
|
|
3101
3160
|
});
|
|
3102
3161
|
} catch (err) {
|
|
@@ -3112,7 +3171,7 @@ var QueryBuilder = (props) => {
|
|
|
3112
3171
|
} finally {
|
|
3113
3172
|
setApiValidating(false);
|
|
3114
3173
|
}
|
|
3115
|
-
}, [builderState, availableFields, entitySetName,
|
|
3174
|
+
}, [builderState, availableFields, entitySetName, props.entityName]);
|
|
3116
3175
|
const onOpenUploadDialog = React3__namespace.useCallback(() => {
|
|
3117
3176
|
setUploadXmlText("");
|
|
3118
3177
|
setUploadError(null);
|