@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,194 +0,0 @@
1
- /**
2
- * ValueOf Picker Component
3
- * Allows selecting a column for valueof comparison, including cross-entity columns
4
- * Implements Option B: select entity/alias first, then attribute
5
- */
6
-
7
- import { useState, useEffect, useMemo } from "react";
8
- import { Field, Dropdown, Option, Tooltip, makeStyles, tokens } from "@fluentui/react-components";
9
- import { Info16Regular } from "@fluentui/react-icons";
10
- import type { FetchNode } from "../../../../model/nodes";
11
- import { collectLinkEntityReferences } from "../../../../model/treeUtils";
12
- import { AttributePicker } from "../../../../../../shared/components/AttributePicker";
13
-
14
- const useStyles = makeStyles({
15
- container: {
16
- display: "flex",
17
- flexDirection: "column",
18
- gap: tokens.spacingVerticalS,
19
- },
20
- fieldWithTooltip: {
21
- display: "flex",
22
- alignItems: "center",
23
- gap: tokens.spacingHorizontalXS,
24
- },
25
- tooltipIcon: {
26
- color: tokens.colorNeutralForeground3,
27
- cursor: "help",
28
- },
29
- });
30
-
31
- interface ValueOfPickerProps {
32
- fetchQuery: FetchNode | null;
33
- rootEntityName: string;
34
- effectiveEntityName: string;
35
- valueof: string;
36
- compatibleTypes: string[];
37
- onChange: (valueof: string | undefined) => void;
38
- }
39
-
40
- /**
41
- * Parse a valueof string into entity alias and attribute parts
42
- * - "createdon" -> { alias: undefined, attribute: "createdon" }
43
- * - "contact_alias.modifiedon" -> { alias: "contact_alias", attribute: "modifiedon" }
44
- */
45
- function parseValueOf(valueof: string): { alias: string | undefined; attribute: string } {
46
- if (!valueof) {
47
- return { alias: undefined, attribute: "" };
48
- }
49
- const dotIndex = valueof.indexOf(".");
50
- if (dotIndex === -1) {
51
- return { alias: undefined, attribute: valueof };
52
- }
53
- return {
54
- alias: valueof.substring(0, dotIndex),
55
- attribute: valueof.substring(dotIndex + 1),
56
- };
57
- }
58
-
59
- /**
60
- * Build a valueof string from entity alias and attribute
61
- */
62
- function buildValueOf(alias: string | undefined, attribute: string): string {
63
- if (!attribute) return "";
64
- if (!alias) return attribute;
65
- return `${alias}.${attribute}`;
66
- }
67
-
68
- export function ValueOfPicker({
69
- fetchQuery,
70
- rootEntityName,
71
- effectiveEntityName,
72
- valueof,
73
- compatibleTypes,
74
- onChange,
75
- }: ValueOfPickerProps) {
76
- const styles = useStyles();
77
-
78
- // Collect all available entities (root + link-entities)
79
- const linkEntityReferences = useMemo(() => collectLinkEntityReferences(fetchQuery), [fetchQuery]);
80
-
81
- // Build entity options: root entity + all link-entities
82
- const entityOptions = useMemo(() => {
83
- const options: Array<{ value: string; label: string; entityName: string }> = [];
84
-
85
- // Add root entity (empty alias means root)
86
- if (rootEntityName) {
87
- options.push({
88
- value: "", // empty means root entity
89
- label: `${rootEntityName} (root)`,
90
- entityName: rootEntityName,
91
- });
92
- }
93
-
94
- // Add link-entities
95
- for (const ref of linkEntityReferences) {
96
- options.push({
97
- value: ref.identifier,
98
- label: ref.displayLabel,
99
- entityName: ref.entityName,
100
- });
101
- }
102
-
103
- return options;
104
- }, [rootEntityName, linkEntityReferences]);
105
-
106
- // Parse current valueof to get alias and attribute
107
- const parsed = useMemo(() => parseValueOf(valueof), [valueof]);
108
-
109
- // Track selected entity alias (empty string = root entity)
110
- const [selectedAlias, setSelectedAlias] = useState<string>(parsed.alias || "");
111
-
112
- // Get the entity name for the selected alias
113
- const selectedEntityName = useMemo(() => {
114
- if (selectedAlias === "") {
115
- return effectiveEntityName || rootEntityName;
116
- }
117
- const ref = linkEntityReferences.find((r) => r.identifier === selectedAlias);
118
- return ref?.entityName || "";
119
- }, [selectedAlias, effectiveEntityName, rootEntityName, linkEntityReferences]);
120
-
121
- // Sync selectedAlias when valueof changes externally (e.g., parsing FetchXML)
122
- useEffect(() => {
123
- setSelectedAlias(parsed.alias || "");
124
- }, [parsed.alias]);
125
-
126
- const handleEntityChange = (_ev: unknown, data: { optionValue?: string }) => {
127
- const newAlias = data.optionValue ?? "";
128
- setSelectedAlias(newAlias);
129
- // Clear the attribute when entity changes, but keep valueof defined (empty string)
130
- // to prevent the switch from reverting to literal mode
131
- onChange("");
132
- };
133
-
134
- const handleAttributeChange = (logicalName: string) => {
135
- const newValueof = buildValueOf(selectedAlias || undefined, logicalName);
136
- onChange(newValueof || undefined);
137
- };
138
-
139
- return (
140
- <div className={styles.container}>
141
- {/* Entity/Alias selector */}
142
- <Field label="Compare From Entity">
143
- <div className={styles.fieldWithTooltip}>
144
- <Dropdown
145
- value={
146
- entityOptions.find((o) => o.value === selectedAlias)?.label || "Select entity..."
147
- }
148
- selectedOptions={[selectedAlias]}
149
- onOptionSelect={handleEntityChange}
150
- placeholder="Select entity..."
151
- >
152
- {entityOptions.map((opt) => (
153
- <Option key={opt.value} value={opt.value}>
154
- {opt.label}
155
- </Option>
156
- ))}
157
- </Dropdown>
158
- <Tooltip
159
- content="Select which entity to pick the comparison column from. Use root entity for same-row comparison, or a linked entity for cross-table comparison."
160
- relationship="description"
161
- >
162
- <Info16Regular className={styles.tooltipIcon} />
163
- </Tooltip>
164
- </div>
165
- </Field>
166
-
167
- {/* Attribute picker for selected entity */}
168
- <Field label="Compare To Column">
169
- <div className={styles.fieldWithTooltip}>
170
- <AttributePicker
171
- entityLogicalName={selectedEntityName}
172
- value={parsed.attribute}
173
- onChange={handleAttributeChange}
174
- placeholder={selectedEntityName ? "Select column to compare" : "Select an entity first"}
175
- disabled={!selectedEntityName}
176
- filterByTypes={compatibleTypes.length > 0 ? compatibleTypes : undefined}
177
- />
178
- <Tooltip
179
- content={
180
- compatibleTypes.length > 0
181
- ? `Select a column of compatible type (${compatibleTypes.join(
182
- ", "
183
- )}) to compare against.`
184
- : "Select another column to compare against."
185
- }
186
- relationship="description"
187
- >
188
- <Info16Regular className={styles.tooltipIcon} />
189
- </Tooltip>
190
- </div>
191
- </Field>
192
- </div>
193
- );
194
- }
@@ -1,225 +0,0 @@
1
- /**
2
- * Properties panel that displays editors for the selected tree node
3
- */
4
-
5
- import { makeStyles, tokens } from "@fluentui/react-components";
6
- import { debugLog } from "../../../../shared/utils/debug";
7
- import type {
8
- FetchNode,
9
- EntityNode,
10
- AttributeNode,
11
- AllAttributesNode,
12
- OrderNode,
13
- FilterNode,
14
- ConditionNode,
15
- LinkEntityNode,
16
- NodeId,
17
- } from "../../model/nodes";
18
- import { FetchOptionsEditor } from "./PropertiesPanel/editors/FetchOptionsEditor";
19
- import { AttributeEditor } from "./PropertiesPanel/editors/AttributeEditor";
20
- import { OrderEditor } from "./PropertiesPanel/editors/OrderEditor";
21
- import { FilterEditor } from "./PropertiesPanel/editors/FilterEditor";
22
- import { ConditionEditor } from "./PropertiesPanel/editors/ConditionEditor";
23
- import { LinkEntityEditor } from "./PropertiesPanel/editors/LinkEntityEditor";
24
-
25
- const useStyles = makeStyles({
26
- container: {
27
- overflow: "auto",
28
- height: "100%",
29
- minHeight: 0, // Important for flex child with overflow
30
- scrollbarGutter: "stable",
31
- },
32
- emptyState: {
33
- padding: "16px",
34
- color: tokens.colorNeutralForeground3,
35
- fontSize: "13px",
36
- fontStyle: "italic",
37
- },
38
- section: {
39
- padding: "16px",
40
- },
41
- label: {
42
- display: "block",
43
- fontSize: "13px",
44
- fontWeight: 600,
45
- marginBottom: "8px",
46
- color: tokens.colorNeutralForeground2,
47
- },
48
- });
49
-
50
- type SelectedNode =
51
- | FetchNode
52
- | EntityNode
53
- | AttributeNode
54
- | AllAttributesNode
55
- | OrderNode
56
- | FilterNode
57
- | ConditionNode
58
- | LinkEntityNode
59
- | null;
60
-
61
- interface PropertiesPanelProps {
62
- selectedNode: SelectedNode;
63
- fetchQuery: FetchNode | null;
64
- onNodeUpdate: (nodeId: NodeId, updates: Record<string, unknown>) => void;
65
- }
66
-
67
- export function PropertiesPanel({ selectedNode, fetchQuery, onNodeUpdate }: PropertiesPanelProps) {
68
- const styles = useStyles();
69
-
70
- // Helper to find the parent entity/link-entity for a node
71
- const findParentEntity = (nodeId: NodeId): string | null => {
72
- if (!fetchQuery) return null;
73
-
74
- const findInNode = (
75
- node: unknown,
76
- targetId: NodeId,
77
- parentEntityName: string | null
78
- ): string | null => {
79
- if (!node || typeof node !== "object") return null;
80
-
81
- const n = node as Record<string, unknown>;
82
-
83
- // If this is the target node, return the current parent entity name
84
- if (n.id === targetId) {
85
- debugLog("propertiesPanel", "Found target node:", {
86
- targetId,
87
- nodeType: n.type,
88
- nodeName: n.name,
89
- resolvedParentEntityName: parentEntityName,
90
- });
91
- return parentEntityName;
92
- }
93
-
94
- // If this is an entity or link-entity, update parent name
95
- // BUT only if it has a valid entity name (not a placeholder like "new_entity")
96
- let currentEntityName = parentEntityName;
97
- if (n.type === "entity" || n.type === "link-entity") {
98
- const entityName = n.name as string;
99
- // Only use the entity name if it's not a placeholder
100
- if (entityName && entityName !== "new_entity") {
101
- currentEntityName = entityName;
102
- debugLog("propertiesPanel", "Traversing entity/link-entity:", {
103
- nodeId: n.id,
104
- nodeType: n.type,
105
- entityName,
106
- updatedCurrentEntityName: currentEntityName,
107
- });
108
- }
109
- }
110
-
111
- // Search in arrays
112
- const arrayProps = ["attributes", "orders", "filters", "conditions", "subfilters", "links"];
113
- for (const prop of arrayProps) {
114
- if (Array.isArray(n[prop])) {
115
- for (const item of n[prop]) {
116
- const found = findInNode(item, targetId, currentEntityName);
117
- if (found) return found;
118
- }
119
- }
120
- }
121
-
122
- // Search in nested objects
123
- if (n.entity) {
124
- const found = findInNode(n.entity, targetId, currentEntityName);
125
- if (found) return found;
126
- }
127
-
128
- return null;
129
- };
130
-
131
- return findInNode(fetchQuery, nodeId, fetchQuery.entity.name);
132
- };
133
-
134
- if (!selectedNode) {
135
- return (
136
- <div className={styles.container}>
137
- <div className={styles.emptyState}>Select a node in the tree to view its properties</div>
138
- </div>
139
- );
140
- }
141
-
142
- const renderProperties = () => {
143
- switch (selectedNode.type) {
144
- case "fetch":
145
- return (
146
- <FetchOptionsEditor
147
- node={selectedNode}
148
- onUpdate={(updates) => onNodeUpdate(selectedNode.id, updates)}
149
- />
150
- );
151
-
152
- case "entity":
153
- return (
154
- <div className={styles.section}>
155
- <div className={styles.label}>Entity: {selectedNode.name}</div>
156
- <div className={styles.emptyState}>
157
- Entity properties editor (Phase 5)
158
- <br />- enableprefiltering, prefilterparametername
159
- </div>
160
- </div>
161
- );
162
-
163
- case "all-attributes":
164
- return (
165
- <div className={styles.section}>
166
- <div className={styles.label}>All Attributes</div>
167
- <div className={styles.emptyState}>All-attributes toggle (Phase 5)</div>
168
- </div>
169
- );
170
-
171
- case "attribute":
172
- return (
173
- <AttributeEditor
174
- node={selectedNode}
175
- entityName={findParentEntity(selectedNode.id) ?? ""}
176
- onUpdate={(updates) => onNodeUpdate(selectedNode.id, updates)}
177
- isAggregateQuery={fetchQuery?.options.aggregate ?? false}
178
- />
179
- );
180
-
181
- case "order":
182
- return (
183
- <OrderEditor
184
- node={selectedNode}
185
- entityName={findParentEntity(selectedNode.id) ?? ""}
186
- fetchQuery={fetchQuery ?? null}
187
- onUpdate={(updates) => onNodeUpdate(selectedNode.id, updates)}
188
- />
189
- );
190
-
191
- case "filter":
192
- return (
193
- <FilterEditor
194
- node={selectedNode}
195
- onUpdate={(updates) => onNodeUpdate(selectedNode.id, updates)}
196
- />
197
- );
198
-
199
- case "condition":
200
- return (
201
- <ConditionEditor
202
- node={selectedNode}
203
- entityName={findParentEntity(selectedNode.id) ?? ""}
204
- onUpdate={(updates) => onNodeUpdate(selectedNode.id, updates)}
205
- isAggregateQuery={fetchQuery?.options.aggregate ?? false}
206
- fetchQuery={fetchQuery}
207
- />
208
- );
209
-
210
- case "link-entity":
211
- return (
212
- <LinkEntityEditor
213
- node={selectedNode}
214
- parentEntityName={findParentEntity(selectedNode.id) ?? ""}
215
- onUpdate={(updates) => onNodeUpdate(selectedNode.id, updates)}
216
- />
217
- );
218
-
219
- default:
220
- return <div className={styles.emptyState}>Unknown node type</div>;
221
- }
222
- };
223
-
224
- return <div className={styles.container}>{renderProperties()}</div>;
225
- }