@mohsinonxrm/pptb-fetchxml-studio 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/LICENSE +661 -0
  2. package/dist/assets/index-DD9fzkbz.js +232 -0
  3. package/dist/index.html +70 -0
  4. package/index.html +0 -1
  5. package/npm-shrinkwrap.json +6329 -0
  6. package/package.json +95 -51
  7. package/CHANGELOG.md +0 -120
  8. package/eslint.config.js +0 -23
  9. package/public/vite.svg +0 -1
  10. package/src/app/AppShell.tsx +0 -1733
  11. package/src/assets/20px-Microsoft_Office_Excel_(2025/342/200/223present).svg.png +0 -0
  12. package/src/assets/react.svg +0 -1
  13. package/src/features/fetchxml/api/dataverseMetadata.ts +0 -192
  14. package/src/features/fetchxml/api/excelExport.ts +0 -318
  15. package/src/features/fetchxml/api/formattedValues.ts +0 -107
  16. package/src/features/fetchxml/api/pptbClient.ts +0 -2635
  17. package/src/features/fetchxml/model/displaySettings.ts +0 -18
  18. package/src/features/fetchxml/model/fetchxml.ts +0 -346
  19. package/src/features/fetchxml/model/fetchxmlIntellisense.ts +0 -583
  20. package/src/features/fetchxml/model/fetchxmlParser.ts +0 -708
  21. package/src/features/fetchxml/model/layoutxml.ts +0 -372
  22. package/src/features/fetchxml/model/nodes.ts +0 -255
  23. package/src/features/fetchxml/model/operators.ts +0 -624
  24. package/src/features/fetchxml/model/treeUtils.ts +0 -174
  25. package/src/features/fetchxml/state/builderStore.tsx +0 -1160
  26. package/src/features/fetchxml/state/cache.ts +0 -393
  27. package/src/features/fetchxml/ui/Dialogs/BulkDeleteDialog.tsx +0 -352
  28. package/src/features/fetchxml/ui/Dialogs/DeleteConfirmDialog.tsx +0 -396
  29. package/src/features/fetchxml/ui/Dialogs/SaveViewDialog.tsx +0 -612
  30. package/src/features/fetchxml/ui/Dialogs/SolutionPicker.tsx +0 -114
  31. package/src/features/fetchxml/ui/Dialogs/WorkflowPickerDialog.tsx +0 -437
  32. package/src/features/fetchxml/ui/LeftPane/PropertiesPanel/editors/AttributeEditor.tsx +0 -214
  33. package/src/features/fetchxml/ui/LeftPane/PropertiesPanel/editors/ConditionEditor.tsx +0 -533
  34. package/src/features/fetchxml/ui/LeftPane/PropertiesPanel/editors/FetchOptionsEditor.tsx +0 -296
  35. package/src/features/fetchxml/ui/LeftPane/PropertiesPanel/editors/FilterEditor.tsx +0 -106
  36. package/src/features/fetchxml/ui/LeftPane/PropertiesPanel/editors/LinkEntityEditor.tsx +0 -433
  37. package/src/features/fetchxml/ui/LeftPane/PropertiesPanel/editors/OrderEditor.tsx +0 -161
  38. package/src/features/fetchxml/ui/LeftPane/PropertiesPanel/editors/ValueOfPicker.tsx +0 -194
  39. package/src/features/fetchxml/ui/LeftPane/PropertiesPanel.tsx +0 -225
  40. package/src/features/fetchxml/ui/LeftPane/TreeView.tsx +0 -604
  41. package/src/features/fetchxml/ui/RightPane/AddColumnsPanel.tsx +0 -800
  42. package/src/features/fetchxml/ui/RightPane/ColumnConfigDialog.tsx +0 -170
  43. package/src/features/fetchxml/ui/RightPane/DataGridCellRenderers.tsx +0 -356
  44. package/src/features/fetchxml/ui/RightPane/EditColumnsPanel.tsx +0 -372
  45. package/src/features/fetchxml/ui/RightPane/FetchXmlEditor.tsx +0 -399
  46. package/src/features/fetchxml/ui/RightPane/FormattedValueUtils.ts +0 -189
  47. package/src/features/fetchxml/ui/RightPane/LayoutXmlViewer.tsx +0 -171
  48. package/src/features/fetchxml/ui/RightPane/PreviewTabs.tsx +0 -471
  49. package/src/features/fetchxml/ui/RightPane/ResultsCommandBar.tsx +0 -430
  50. package/src/features/fetchxml/ui/RightPane/ResultsGrid.tsx +0 -983
  51. package/src/features/fetchxml/ui/RightPane/XmlTextArea.tsx +0 -65
  52. package/src/features/fetchxml/ui/Settings/SettingsDrawer.tsx +0 -175
  53. package/src/features/fetchxml/ui/Toolbar/EntitySelector.tsx +0 -788
  54. package/src/features/fetchxml/ui/Toolbar/LoadViewPicker.tsx +0 -274
  55. package/src/features/fetchxml/ui/Toolbar/SaveViewButton.tsx +0 -174
  56. package/src/main.tsx +0 -11
  57. package/src/shared/components/AttributePicker.tsx +0 -138
  58. package/src/shared/components/BetweenInputPicker.tsx +0 -120
  59. package/src/shared/components/BooleanValuePicker.tsx +0 -165
  60. package/src/shared/components/DateTimeInputPicker.tsx +0 -170
  61. package/src/shared/components/DynamicValueList.tsx +0 -128
  62. package/src/shared/components/ExcelIcon.tsx +0 -30
  63. package/src/shared/components/MultiValuePicker.tsx +0 -178
  64. package/src/shared/components/NumericInputPicker.tsx +0 -156
  65. package/src/shared/components/OptionSetValuePicker.tsx +0 -150
  66. package/src/shared/components/RelationshipPicker.tsx +0 -268
  67. package/src/shared/contexts/ThemeContext.tsx +0 -19
  68. package/src/shared/hooks/useAccessMode.ts +0 -77
  69. package/src/shared/hooks/useLazyMetadata.ts +0 -132
  70. package/src/shared/hooks/usePptbContext.ts +0 -170
  71. package/src/shared/hooks/usePublisherFilter.ts +0 -214
  72. package/src/shared/hooks/useSolutionFilter.ts +0 -162
  73. package/src/shared/utils/debug.ts +0 -87
  74. package/tsconfig.app.json +0 -28
  75. package/tsconfig.json +0 -7
  76. package/tsconfig.node.json +0 -26
  77. package/vite.config.ts +0 -7
@@ -1,18 +0,0 @@
1
- /**
2
- * Display settings types and defaults
3
- */
4
-
5
- export type ValueDisplayMode = "formatted" | "raw" | "both";
6
-
7
- export interface DisplaySettings {
8
- /** Show logical names in column headers instead of display names */
9
- useLogicalNames: boolean;
10
- /** How to display cell values: formatted, raw, or both */
11
- valueDisplayMode: ValueDisplayMode;
12
- }
13
-
14
- /** Default display settings */
15
- export const defaultDisplaySettings: DisplaySettings = {
16
- useLogicalNames: false,
17
- valueDisplayMode: "formatted",
18
- };
@@ -1,346 +0,0 @@
1
- /**
2
- * Pure functions to generate FetchXML from tree structure
3
- * Follows Dataverse FetchXML semantics
4
- */
5
-
6
- import type {
7
- FetchNode,
8
- EntityNode,
9
- AttributeNode,
10
- AllAttributesNode,
11
- OrderNode,
12
- FilterNode,
13
- ConditionNode,
14
- LinkEntityNode,
15
- } from "./nodes";
16
- import { operatorRequiresValue } from "./operators";
17
-
18
- /**
19
- * Convert a value to string for FetchXML, avoiding scientific notation for numbers
20
- */
21
- function formatValueForFetchXml(val: unknown): string {
22
- if (typeof val === "number") {
23
- // Avoid scientific notation (1e-12) by using toFixed with sufficient precision
24
- // Then remove trailing zeros
25
- if (Math.abs(val) < 1e-10 && val !== 0) {
26
- // Very small numbers - use high precision
27
- return val.toFixed(20).replace(/\.?0+$/, "");
28
- } else if (Number.isInteger(val)) {
29
- // Integer - no decimal point needed
30
- return val.toString();
31
- } else {
32
- // Decimal - preserve precision but remove trailing zeros
33
- return val.toFixed(10).replace(/\.?0+$/, "");
34
- }
35
- }
36
- return String(val);
37
- }
38
-
39
- /**
40
- * Generate complete FetchXML from FetchNode
41
- */
42
- export function generateFetchXml(fetchNode: FetchNode): string {
43
- const lines: string[] = [];
44
-
45
- // Build fetch element with options
46
- const fetchAttrs: string[] = [];
47
- if (fetchNode.options.aggregate) fetchAttrs.push('aggregate="true"');
48
- if (fetchNode.options.distinct) fetchAttrs.push('distinct="true"');
49
- if (fetchNode.options.top !== undefined) fetchAttrs.push(`top="${fetchNode.options.top}"`);
50
- if (fetchNode.options.count !== undefined) fetchAttrs.push(`count="${fetchNode.options.count}"`);
51
- if (fetchNode.options.page !== undefined) fetchAttrs.push(`page="${fetchNode.options.page}"`);
52
- if (fetchNode.options.returnTotalRecordCount) fetchAttrs.push('returntotalrecordcount="true"');
53
- if (fetchNode.options.noLock) fetchAttrs.push('no-lock="true"');
54
- if (fetchNode.options.utcOffset !== undefined)
55
- fetchAttrs.push(`utc-offset="${fetchNode.options.utcOffset}"`);
56
- if (fetchNode.options.pagingCookie)
57
- fetchAttrs.push(`paging-cookie="${escapeXml(fetchNode.options.pagingCookie)}"`);
58
-
59
- const fetchAttrStr = fetchAttrs.length > 0 ? " " + fetchAttrs.join(" ") : "";
60
- lines.push(`<fetch${fetchAttrStr}>`);
61
-
62
- // Generate entity
63
- lines.push(...generateEntity(fetchNode.entity, 1));
64
-
65
- lines.push("</fetch>");
66
-
67
- return lines.join("\n");
68
- }
69
-
70
- /**
71
- * Generate entity element
72
- */
73
- function generateEntity(entity: EntityNode, indent: number): string[] {
74
- const lines: string[] = [];
75
- const spaces = " ".repeat(indent);
76
-
77
- lines.push(`${spaces}<entity name="${entity.name}">`);
78
-
79
- // All-attributes (single node, not array)
80
- if (entity.allAttributes?.enabled) {
81
- lines.push(...generateAllAttributes(entity.allAttributes, indent + 1));
82
- }
83
-
84
- // CRITICAL: Always include the primary ID attribute for row selection to work
85
- // The primary ID is {entityname}id (e.g., accountid, contactid)
86
- const primaryIdAttrName = `${entity.name}id`;
87
- const hasPrimaryId =
88
- entity.attributes.some((attr) => attr.name === primaryIdAttrName) ||
89
- entity.allAttributes?.enabled;
90
-
91
- if (!hasPrimaryId) {
92
- // Inject primary ID attribute at the beginning
93
- const primaryIdSpaces = " ".repeat(indent + 1);
94
- lines.push(`${primaryIdSpaces}<attribute name="${primaryIdAttrName}" />`);
95
- }
96
-
97
- // Attributes
98
- entity.attributes.forEach((attr) => {
99
- lines.push(...generateAttribute(attr, indent + 1));
100
- });
101
-
102
- // Orders
103
- entity.orders?.forEach((order) => {
104
- lines.push(...generateOrder(order, indent + 1));
105
- });
106
-
107
- // Filters
108
- entity.filters?.forEach((filter) => {
109
- lines.push(...generateFilter(filter, indent + 1));
110
- });
111
-
112
- // Link-entities
113
- entity.links?.forEach((link) => {
114
- lines.push(...generateLinkEntity(link, indent + 1));
115
- });
116
-
117
- lines.push(`${spaces}</entity>`);
118
-
119
- return lines;
120
- }
121
-
122
- /**
123
- * Generate all-attributes element
124
- */
125
- function generateAllAttributes(_allAttr: AllAttributesNode, indent: number): string[] {
126
- const spaces = " ".repeat(indent);
127
- return [`${spaces}<all-attributes />`];
128
- }
129
-
130
- /**
131
- * Generate attribute element
132
- */
133
- function generateAttribute(attr: AttributeNode, indent: number): string[] {
134
- const spaces = " ".repeat(indent);
135
- const attrs: string[] = [`name="${attr.name}"`];
136
-
137
- if (attr.alias) attrs.push(`alias="${attr.alias}"`);
138
- if (attr.aggregate) attrs.push(`aggregate="${attr.aggregate}"`);
139
- if (attr.groupby) attrs.push('groupby="true"');
140
- if (attr.dategrouping) attrs.push(`dategrouping="${attr.dategrouping}"`);
141
- if (attr.usertimezone !== undefined) attrs.push(`usertimezone="${attr.usertimezone}"`);
142
-
143
- return [`${spaces}<attribute ${attrs.join(" ")} />`];
144
- }
145
-
146
- /**
147
- * Generate order element
148
- */
149
- function generateOrder(order: OrderNode, indent: number): string[] {
150
- const spaces = " ".repeat(indent);
151
- const attrs: string[] = [`attribute="${order.attribute}"`];
152
-
153
- if (order.descending) attrs.push('descending="true"');
154
- if (order.entityname) attrs.push(`entityname="${order.entityname}"`);
155
-
156
- return [`${spaces}<order ${attrs.join(" ")} />`];
157
- }
158
-
159
- /**
160
- * Generate filter element (recursive for subfilters)
161
- */
162
- function generateFilter(filter: FilterNode, indent: number): string[] {
163
- const lines: string[] = [];
164
- const spaces = " ".repeat(indent);
165
-
166
- const attrs: string[] = [`type="${filter.conjunction}"`];
167
- if (filter.hint) attrs.push(`hint="${filter.hint}"`);
168
-
169
- lines.push(`${spaces}<filter ${attrs.join(" ")}>`);
170
-
171
- // Conditions
172
- filter.conditions?.forEach((condition) => {
173
- lines.push(...generateCondition(condition, indent + 1));
174
- });
175
-
176
- // Subfilters (recursive)
177
- filter.subfilters?.forEach((subfilter) => {
178
- lines.push(...generateFilter(subfilter, indent + 1));
179
- });
180
-
181
- // Link-entities inside filter (for any/not any/all/not all)
182
- filter.links?.forEach((link) => {
183
- lines.push(...generateLinkEntity(link, indent + 1));
184
- });
185
-
186
- lines.push(`${spaces}</filter>`);
187
-
188
- return lines;
189
- }
190
-
191
- /**
192
- * Generate condition element
193
- */
194
- function generateCondition(condition: ConditionNode, indent: number): string[] {
195
- const lines: string[] = [];
196
- const spaces = " ".repeat(indent);
197
-
198
- const attrs: string[] = [
199
- `attribute="${condition.attribute}"`,
200
- `operator="${condition.operator}"`,
201
- ];
202
-
203
- if (condition.entityname) attrs.push(`entityname="${condition.entityname}"`);
204
- if (condition.aggregate) attrs.push(`aggregate="${condition.aggregate}"`);
205
- // Note: ConditionNode doesn't have alias in our type definition
206
- if (condition.valueof) attrs.push(`valueof="${condition.valueof}"`);
207
-
208
- // Use the centralized operator logic to determine if value is needed
209
- const needsValue = operatorRequiresValue(condition.operator);
210
-
211
- if (!needsValue) {
212
- // Self-closing tag for operators that don't need values (null, eq-userid, etc.)
213
- return [`${spaces}<condition ${attrs.join(" ")} />`];
214
- }
215
-
216
- // Operators that need value attribute
217
- if (condition.value !== undefined && condition.value !== null) {
218
- // Handle array values (for 'in', 'not-in', 'contain-values', 'between', etc.)
219
- if (Array.isArray(condition.value)) {
220
- lines.push(`${spaces}<condition ${attrs.join(" ")}>`);
221
- condition.value.forEach((val) => {
222
- if (val !== undefined && val !== null) {
223
- lines.push(`${spaces} <value>${escapeXml(formatValueForFetchXml(val))}</value>`);
224
- }
225
- });
226
- lines.push(`${spaces}</condition>`);
227
- } else {
228
- // Single value attribute
229
- attrs.push(`value="${escapeXml(formatValueForFetchXml(condition.value))}"`);
230
- lines.push(`${spaces}<condition ${attrs.join(" ")} />`);
231
- }
232
- } else {
233
- // No value provided but operator needs one - still generate with empty value
234
- lines.push(`${spaces}<condition ${attrs.join(" ")} />`);
235
- }
236
-
237
- return lines;
238
- }
239
-
240
- /**
241
- * Generate link-entity element (recursive)
242
- */
243
- function generateLinkEntity(link: LinkEntityNode, indent: number): string[] {
244
- const lines: string[] = [];
245
- const spaces = " ".repeat(indent);
246
-
247
- const attrs: string[] = [`name="${link.name}"`, `from="${link.from}"`, `to="${link.to}"`];
248
-
249
- if (link.alias) attrs.push(`alias="${link.alias}"`);
250
- if (link.linkType !== "inner") attrs.push(`link-type="${link.linkType}"`);
251
- if (link.intersect) attrs.push('intersect="true"');
252
- if (link.visible !== undefined) attrs.push(`visible="${link.visible}"`);
253
-
254
- lines.push(`${spaces}<link-entity ${attrs.join(" ")}>`);
255
-
256
- // All-attributes (single node, not array)
257
- if (link.allAttributes?.enabled) {
258
- lines.push(...generateAllAttributes(link.allAttributes, indent + 1));
259
- }
260
-
261
- // Attributes
262
- link.attributes?.forEach((attr) => {
263
- lines.push(...generateAttribute(attr, indent + 1));
264
- });
265
-
266
- // Orders
267
- link.orders?.forEach((order) => {
268
- lines.push(...generateOrder(order, indent + 1));
269
- });
270
-
271
- // Filters
272
- link.filters?.forEach((filter) => {
273
- lines.push(...generateFilter(filter, indent + 1));
274
- });
275
-
276
- // Nested link-entities (recursive)
277
- link.links?.forEach((nestedLink) => {
278
- lines.push(...generateLinkEntity(nestedLink, indent + 1));
279
- });
280
-
281
- lines.push(`${spaces}</link-entity>`);
282
-
283
- return lines;
284
- }
285
-
286
- /**
287
- * Escape XML special characters
288
- */
289
- function escapeXml(str: string): string {
290
- return str
291
- .replace(/&/g, "&amp;")
292
- .replace(/</g, "&lt;")
293
- .replace(/>/g, "&gt;")
294
- .replace(/"/g, "&quot;")
295
- .replace(/'/g, "&apos;");
296
- }
297
-
298
- /**
299
- * Modify FetchXML string to add/update paging parameters
300
- * Used for fetching subsequent pages during Retrieve All or infinite scroll
301
- *
302
- * Per Dataverse requirements:
303
- * - page: Page number (1-based)
304
- * - paging-cookie: XML cookie from previous response, must be XML-entity encoded
305
- * - count: Page size (records per page)
306
- */
307
- export function addPagingToFetchXml(
308
- fetchXml: string,
309
- page: number,
310
- pagingCookie?: string,
311
- count?: number
312
- ): string {
313
- // Parse the fetch element attributes
314
- const fetchMatch = fetchXml.match(/<fetch([^>]*)>/);
315
- if (!fetchMatch) return fetchXml;
316
-
317
- let fetchAttrs = fetchMatch[1];
318
-
319
- // Remove existing page, paging-cookie, count attributes
320
- // Note: paging-cookie can contain encoded quotes, so we need a more robust pattern
321
- fetchAttrs = fetchAttrs
322
- .replace(/\s*page="[^"]*"/g, "")
323
- .replace(/\s*paging-cookie="[^"]*"/g, "") // This works because the value is already XML-encoded (no raw quotes)
324
- .replace(/\s*count="[^"]*"/g, "");
325
-
326
- // Add new paging attributes
327
- fetchAttrs = fetchAttrs.trim();
328
-
329
- // Always add page number (even page 1 for explicit paging requests)
330
- fetchAttrs += ` page="${page}"`;
331
-
332
- // Add paging cookie if provided (required for reliable paging after page 1)
333
- if (pagingCookie) {
334
- // The paging cookie needs to be XML-escaped when inserted as an attribute value
335
- // Dataverse returns it as raw XML, we need to encode it for the attribute
336
- fetchAttrs += ` paging-cookie="${escapeXml(pagingCookie)}"`;
337
- }
338
-
339
- // Add count (page size) if specified
340
- if (count !== undefined) {
341
- fetchAttrs += ` count="${count}"`;
342
- }
343
-
344
- // Reconstruct the fetch element
345
- return fetchXml.replace(/<fetch[^>]*>/, `<fetch${fetchAttrs ? " " + fetchAttrs : ""}>`);
346
- }