@mohsinonxrm/pptb-fetchxml-studio 1.0.0 → 1.0.2

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 (78) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +1 -1
  3. package/dist/assets/index-DD9fzkbz.js +232 -0
  4. package/dist/index.html +70 -0
  5. package/index.html +0 -1
  6. package/npm-shrinkwrap.json +6329 -0
  7. package/package.json +95 -51
  8. package/CHANGELOG.md +0 -120
  9. package/eslint.config.js +0 -23
  10. package/public/vite.svg +0 -1
  11. package/src/app/AppShell.tsx +0 -1733
  12. package/src/assets/20px-Microsoft_Office_Excel_(2025/342/200/223present).svg.png +0 -0
  13. package/src/assets/react.svg +0 -1
  14. package/src/features/fetchxml/api/dataverseMetadata.ts +0 -192
  15. package/src/features/fetchxml/api/excelExport.ts +0 -318
  16. package/src/features/fetchxml/api/formattedValues.ts +0 -107
  17. package/src/features/fetchxml/api/pptbClient.ts +0 -2635
  18. package/src/features/fetchxml/model/displaySettings.ts +0 -18
  19. package/src/features/fetchxml/model/fetchxml.ts +0 -346
  20. package/src/features/fetchxml/model/fetchxmlIntellisense.ts +0 -583
  21. package/src/features/fetchxml/model/fetchxmlParser.ts +0 -708
  22. package/src/features/fetchxml/model/layoutxml.ts +0 -372
  23. package/src/features/fetchxml/model/nodes.ts +0 -255
  24. package/src/features/fetchxml/model/operators.ts +0 -624
  25. package/src/features/fetchxml/model/treeUtils.ts +0 -174
  26. package/src/features/fetchxml/state/builderStore.tsx +0 -1160
  27. package/src/features/fetchxml/state/cache.ts +0 -393
  28. package/src/features/fetchxml/ui/Dialogs/BulkDeleteDialog.tsx +0 -352
  29. package/src/features/fetchxml/ui/Dialogs/DeleteConfirmDialog.tsx +0 -396
  30. package/src/features/fetchxml/ui/Dialogs/SaveViewDialog.tsx +0 -612
  31. package/src/features/fetchxml/ui/Dialogs/SolutionPicker.tsx +0 -114
  32. package/src/features/fetchxml/ui/Dialogs/WorkflowPickerDialog.tsx +0 -437
  33. package/src/features/fetchxml/ui/LeftPane/PropertiesPanel/editors/AttributeEditor.tsx +0 -214
  34. package/src/features/fetchxml/ui/LeftPane/PropertiesPanel/editors/ConditionEditor.tsx +0 -533
  35. package/src/features/fetchxml/ui/LeftPane/PropertiesPanel/editors/FetchOptionsEditor.tsx +0 -296
  36. package/src/features/fetchxml/ui/LeftPane/PropertiesPanel/editors/FilterEditor.tsx +0 -106
  37. package/src/features/fetchxml/ui/LeftPane/PropertiesPanel/editors/LinkEntityEditor.tsx +0 -433
  38. package/src/features/fetchxml/ui/LeftPane/PropertiesPanel/editors/OrderEditor.tsx +0 -161
  39. package/src/features/fetchxml/ui/LeftPane/PropertiesPanel/editors/ValueOfPicker.tsx +0 -194
  40. package/src/features/fetchxml/ui/LeftPane/PropertiesPanel.tsx +0 -225
  41. package/src/features/fetchxml/ui/LeftPane/TreeView.tsx +0 -604
  42. package/src/features/fetchxml/ui/RightPane/AddColumnsPanel.tsx +0 -800
  43. package/src/features/fetchxml/ui/RightPane/ColumnConfigDialog.tsx +0 -170
  44. package/src/features/fetchxml/ui/RightPane/DataGridCellRenderers.tsx +0 -356
  45. package/src/features/fetchxml/ui/RightPane/EditColumnsPanel.tsx +0 -372
  46. package/src/features/fetchxml/ui/RightPane/FetchXmlEditor.tsx +0 -399
  47. package/src/features/fetchxml/ui/RightPane/FormattedValueUtils.ts +0 -189
  48. package/src/features/fetchxml/ui/RightPane/LayoutXmlViewer.tsx +0 -171
  49. package/src/features/fetchxml/ui/RightPane/PreviewTabs.tsx +0 -471
  50. package/src/features/fetchxml/ui/RightPane/ResultsCommandBar.tsx +0 -430
  51. package/src/features/fetchxml/ui/RightPane/ResultsGrid.tsx +0 -983
  52. package/src/features/fetchxml/ui/RightPane/XmlTextArea.tsx +0 -65
  53. package/src/features/fetchxml/ui/Settings/SettingsDrawer.tsx +0 -175
  54. package/src/features/fetchxml/ui/Toolbar/EntitySelector.tsx +0 -788
  55. package/src/features/fetchxml/ui/Toolbar/LoadViewPicker.tsx +0 -274
  56. package/src/features/fetchxml/ui/Toolbar/SaveViewButton.tsx +0 -174
  57. package/src/main.tsx +0 -11
  58. package/src/shared/components/AttributePicker.tsx +0 -138
  59. package/src/shared/components/BetweenInputPicker.tsx +0 -120
  60. package/src/shared/components/BooleanValuePicker.tsx +0 -165
  61. package/src/shared/components/DateTimeInputPicker.tsx +0 -170
  62. package/src/shared/components/DynamicValueList.tsx +0 -128
  63. package/src/shared/components/ExcelIcon.tsx +0 -30
  64. package/src/shared/components/MultiValuePicker.tsx +0 -178
  65. package/src/shared/components/NumericInputPicker.tsx +0 -156
  66. package/src/shared/components/OptionSetValuePicker.tsx +0 -150
  67. package/src/shared/components/RelationshipPicker.tsx +0 -268
  68. package/src/shared/contexts/ThemeContext.tsx +0 -19
  69. package/src/shared/hooks/useAccessMode.ts +0 -77
  70. package/src/shared/hooks/useLazyMetadata.ts +0 -132
  71. package/src/shared/hooks/usePptbContext.ts +0 -170
  72. package/src/shared/hooks/usePublisherFilter.ts +0 -214
  73. package/src/shared/hooks/useSolutionFilter.ts +0 -162
  74. package/src/shared/utils/debug.ts +0 -87
  75. package/tsconfig.app.json +0 -28
  76. package/tsconfig.json +0 -7
  77. package/tsconfig.node.json +0 -26
  78. 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
- }