@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.
- package/LICENSE +661 -0
- package/README.md +1 -1
- package/dist/assets/index-DD9fzkbz.js +232 -0
- package/dist/index.html +70 -0
- package/index.html +0 -1
- package/npm-shrinkwrap.json +6329 -0
- package/package.json +95 -51
- package/CHANGELOG.md +0 -120
- package/eslint.config.js +0 -23
- package/public/vite.svg +0 -1
- package/src/app/AppShell.tsx +0 -1733
- package/src/assets/20px-Microsoft_Office_Excel_(2025/342/200/223present).svg.png +0 -0
- package/src/assets/react.svg +0 -1
- package/src/features/fetchxml/api/dataverseMetadata.ts +0 -192
- package/src/features/fetchxml/api/excelExport.ts +0 -318
- package/src/features/fetchxml/api/formattedValues.ts +0 -107
- package/src/features/fetchxml/api/pptbClient.ts +0 -2635
- package/src/features/fetchxml/model/displaySettings.ts +0 -18
- package/src/features/fetchxml/model/fetchxml.ts +0 -346
- package/src/features/fetchxml/model/fetchxmlIntellisense.ts +0 -583
- package/src/features/fetchxml/model/fetchxmlParser.ts +0 -708
- package/src/features/fetchxml/model/layoutxml.ts +0 -372
- package/src/features/fetchxml/model/nodes.ts +0 -255
- package/src/features/fetchxml/model/operators.ts +0 -624
- package/src/features/fetchxml/model/treeUtils.ts +0 -174
- package/src/features/fetchxml/state/builderStore.tsx +0 -1160
- package/src/features/fetchxml/state/cache.ts +0 -393
- package/src/features/fetchxml/ui/Dialogs/BulkDeleteDialog.tsx +0 -352
- package/src/features/fetchxml/ui/Dialogs/DeleteConfirmDialog.tsx +0 -396
- package/src/features/fetchxml/ui/Dialogs/SaveViewDialog.tsx +0 -612
- package/src/features/fetchxml/ui/Dialogs/SolutionPicker.tsx +0 -114
- package/src/features/fetchxml/ui/Dialogs/WorkflowPickerDialog.tsx +0 -437
- package/src/features/fetchxml/ui/LeftPane/PropertiesPanel/editors/AttributeEditor.tsx +0 -214
- package/src/features/fetchxml/ui/LeftPane/PropertiesPanel/editors/ConditionEditor.tsx +0 -533
- package/src/features/fetchxml/ui/LeftPane/PropertiesPanel/editors/FetchOptionsEditor.tsx +0 -296
- package/src/features/fetchxml/ui/LeftPane/PropertiesPanel/editors/FilterEditor.tsx +0 -106
- package/src/features/fetchxml/ui/LeftPane/PropertiesPanel/editors/LinkEntityEditor.tsx +0 -433
- package/src/features/fetchxml/ui/LeftPane/PropertiesPanel/editors/OrderEditor.tsx +0 -161
- package/src/features/fetchxml/ui/LeftPane/PropertiesPanel/editors/ValueOfPicker.tsx +0 -194
- package/src/features/fetchxml/ui/LeftPane/PropertiesPanel.tsx +0 -225
- package/src/features/fetchxml/ui/LeftPane/TreeView.tsx +0 -604
- package/src/features/fetchxml/ui/RightPane/AddColumnsPanel.tsx +0 -800
- package/src/features/fetchxml/ui/RightPane/ColumnConfigDialog.tsx +0 -170
- package/src/features/fetchxml/ui/RightPane/DataGridCellRenderers.tsx +0 -356
- package/src/features/fetchxml/ui/RightPane/EditColumnsPanel.tsx +0 -372
- package/src/features/fetchxml/ui/RightPane/FetchXmlEditor.tsx +0 -399
- package/src/features/fetchxml/ui/RightPane/FormattedValueUtils.ts +0 -189
- package/src/features/fetchxml/ui/RightPane/LayoutXmlViewer.tsx +0 -171
- package/src/features/fetchxml/ui/RightPane/PreviewTabs.tsx +0 -471
- package/src/features/fetchxml/ui/RightPane/ResultsCommandBar.tsx +0 -430
- package/src/features/fetchxml/ui/RightPane/ResultsGrid.tsx +0 -983
- package/src/features/fetchxml/ui/RightPane/XmlTextArea.tsx +0 -65
- package/src/features/fetchxml/ui/Settings/SettingsDrawer.tsx +0 -175
- package/src/features/fetchxml/ui/Toolbar/EntitySelector.tsx +0 -788
- package/src/features/fetchxml/ui/Toolbar/LoadViewPicker.tsx +0 -274
- package/src/features/fetchxml/ui/Toolbar/SaveViewButton.tsx +0 -174
- package/src/main.tsx +0 -11
- package/src/shared/components/AttributePicker.tsx +0 -138
- package/src/shared/components/BetweenInputPicker.tsx +0 -120
- package/src/shared/components/BooleanValuePicker.tsx +0 -165
- package/src/shared/components/DateTimeInputPicker.tsx +0 -170
- package/src/shared/components/DynamicValueList.tsx +0 -128
- package/src/shared/components/ExcelIcon.tsx +0 -30
- package/src/shared/components/MultiValuePicker.tsx +0 -178
- package/src/shared/components/NumericInputPicker.tsx +0 -156
- package/src/shared/components/OptionSetValuePicker.tsx +0 -150
- package/src/shared/components/RelationshipPicker.tsx +0 -268
- package/src/shared/contexts/ThemeContext.tsx +0 -19
- package/src/shared/hooks/useAccessMode.ts +0 -77
- package/src/shared/hooks/useLazyMetadata.ts +0 -132
- package/src/shared/hooks/usePptbContext.ts +0 -170
- package/src/shared/hooks/usePublisherFilter.ts +0 -214
- package/src/shared/hooks/useSolutionFilter.ts +0 -162
- package/src/shared/utils/debug.ts +0 -87
- package/tsconfig.app.json +0 -28
- package/tsconfig.json +0 -7
- package/tsconfig.node.json +0 -26
- package/vite.config.ts +0 -7
|
@@ -1,352 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bulk Delete Dialog
|
|
3
|
-
* Shows confirmation and progress for bulk delete operations
|
|
4
|
-
* Supports both selected records and "all records from view" scenarios
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { useCallback, useState } from "react";
|
|
8
|
-
import {
|
|
9
|
-
Dialog,
|
|
10
|
-
DialogSurface,
|
|
11
|
-
DialogTitle,
|
|
12
|
-
DialogBody,
|
|
13
|
-
DialogContent,
|
|
14
|
-
DialogActions,
|
|
15
|
-
Button,
|
|
16
|
-
Text,
|
|
17
|
-
Input,
|
|
18
|
-
Spinner,
|
|
19
|
-
Link,
|
|
20
|
-
makeStyles,
|
|
21
|
-
tokens,
|
|
22
|
-
Field,
|
|
23
|
-
MessageBar,
|
|
24
|
-
MessageBarBody,
|
|
25
|
-
MessageBarTitle,
|
|
26
|
-
Checkbox,
|
|
27
|
-
} from "@fluentui/react-components";
|
|
28
|
-
import {
|
|
29
|
-
Warning20Regular,
|
|
30
|
-
CheckmarkCircle20Regular,
|
|
31
|
-
Copy20Regular,
|
|
32
|
-
ErrorCircle20Regular,
|
|
33
|
-
} from "@fluentui/react-icons";
|
|
34
|
-
|
|
35
|
-
const useStyles = makeStyles({
|
|
36
|
-
content: {
|
|
37
|
-
display: "flex",
|
|
38
|
-
flexDirection: "column",
|
|
39
|
-
gap: tokens.spacingVerticalM,
|
|
40
|
-
},
|
|
41
|
-
warningRow: {
|
|
42
|
-
display: "flex",
|
|
43
|
-
alignItems: "center",
|
|
44
|
-
gap: tokens.spacingHorizontalS,
|
|
45
|
-
color: tokens.colorPaletteRedForeground1,
|
|
46
|
-
},
|
|
47
|
-
dangerWarning: {
|
|
48
|
-
backgroundColor: tokens.colorPaletteRedBackground1,
|
|
49
|
-
padding: tokens.spacingVerticalM,
|
|
50
|
-
borderRadius: tokens.borderRadiusMedium,
|
|
51
|
-
border: `1px solid ${tokens.colorPaletteRedBorder1}`,
|
|
52
|
-
},
|
|
53
|
-
successRow: {
|
|
54
|
-
display: "flex",
|
|
55
|
-
alignItems: "center",
|
|
56
|
-
gap: tokens.spacingHorizontalS,
|
|
57
|
-
color: tokens.colorPaletteGreenForeground1,
|
|
58
|
-
},
|
|
59
|
-
infoBox: {
|
|
60
|
-
backgroundColor: tokens.colorNeutralBackground3,
|
|
61
|
-
padding: tokens.spacingVerticalM,
|
|
62
|
-
borderRadius: tokens.borderRadiusMedium,
|
|
63
|
-
},
|
|
64
|
-
jobUrlRow: {
|
|
65
|
-
display: "flex",
|
|
66
|
-
alignItems: "center",
|
|
67
|
-
gap: tokens.spacingHorizontalS,
|
|
68
|
-
marginTop: tokens.spacingVerticalS,
|
|
69
|
-
},
|
|
70
|
-
errorMessage: {
|
|
71
|
-
color: tokens.colorPaletteRedForeground1,
|
|
72
|
-
marginTop: tokens.spacingVerticalS,
|
|
73
|
-
},
|
|
74
|
-
confirmCheckbox: {
|
|
75
|
-
marginTop: tokens.spacingVerticalS,
|
|
76
|
-
},
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
export interface BulkDeleteDialogProps {
|
|
80
|
-
/** Whether the dialog is open */
|
|
81
|
-
open: boolean;
|
|
82
|
-
/** Number of records to delete (0 means ALL from view) */
|
|
83
|
-
recordCount: number;
|
|
84
|
-
/** Entity display name */
|
|
85
|
-
entityDisplayName?: string;
|
|
86
|
-
/** Whether this is deleting ALL records from view (no selection) */
|
|
87
|
-
isAllRecords?: boolean;
|
|
88
|
-
/** Total records in view (when isAllRecords=true) */
|
|
89
|
-
totalViewRecords?: number;
|
|
90
|
-
/** Called when dialog is closed */
|
|
91
|
-
onClose: () => void;
|
|
92
|
-
/** Called when bulk delete is confirmed */
|
|
93
|
-
onConfirm: (jobName: string) => Promise<{ asyncOperationId: string; jobUrl: string }>;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
type DialogState = "confirm" | "submitting" | "success" | "error";
|
|
97
|
-
|
|
98
|
-
export function BulkDeleteDialog({
|
|
99
|
-
open,
|
|
100
|
-
recordCount,
|
|
101
|
-
entityDisplayName,
|
|
102
|
-
isAllRecords = false,
|
|
103
|
-
totalViewRecords: _totalViewRecords, // Intentionally unused - we don't show count for "all records"
|
|
104
|
-
onClose,
|
|
105
|
-
onConfirm,
|
|
106
|
-
}: BulkDeleteDialogProps) {
|
|
107
|
-
const styles = useStyles();
|
|
108
|
-
const [state, setState] = useState<DialogState>("confirm");
|
|
109
|
-
const [jobName, setJobName] = useState(
|
|
110
|
-
`Bulk Delete ${entityDisplayName || "Records"} - ${new Date().toLocaleString()}`
|
|
111
|
-
);
|
|
112
|
-
const [error, setError] = useState<string | null>(null);
|
|
113
|
-
const [result, setResult] = useState<{ asyncOperationId: string; jobUrl: string } | null>(null);
|
|
114
|
-
const [copied, setCopied] = useState(false);
|
|
115
|
-
const [confirmAllRecords, setConfirmAllRecords] = useState(false);
|
|
116
|
-
|
|
117
|
-
const handleConfirm = useCallback(async () => {
|
|
118
|
-
setState("submitting");
|
|
119
|
-
setError(null);
|
|
120
|
-
try {
|
|
121
|
-
const res = await onConfirm(jobName);
|
|
122
|
-
setResult(res);
|
|
123
|
-
setState("success");
|
|
124
|
-
} catch (err) {
|
|
125
|
-
setError(err instanceof Error ? err.message : "Failed to submit bulk delete job");
|
|
126
|
-
setState("error");
|
|
127
|
-
}
|
|
128
|
-
}, [onConfirm, jobName]);
|
|
129
|
-
|
|
130
|
-
const handleClose = useCallback(() => {
|
|
131
|
-
if (state !== "submitting") {
|
|
132
|
-
setState("confirm");
|
|
133
|
-
setError(null);
|
|
134
|
-
setResult(null);
|
|
135
|
-
setCopied(false);
|
|
136
|
-
setConfirmAllRecords(false);
|
|
137
|
-
onClose();
|
|
138
|
-
}
|
|
139
|
-
}, [state, onClose]);
|
|
140
|
-
|
|
141
|
-
const handleCopyUrl = useCallback(async () => {
|
|
142
|
-
if (result?.jobUrl) {
|
|
143
|
-
try {
|
|
144
|
-
await navigator.clipboard.writeText(result.jobUrl);
|
|
145
|
-
setCopied(true);
|
|
146
|
-
setTimeout(() => setCopied(false), 2000);
|
|
147
|
-
} catch {
|
|
148
|
-
// Fallback for older browsers
|
|
149
|
-
const textArea = document.createElement("textarea");
|
|
150
|
-
textArea.value = result.jobUrl;
|
|
151
|
-
document.body.appendChild(textArea);
|
|
152
|
-
textArea.select();
|
|
153
|
-
document.execCommand("copy");
|
|
154
|
-
document.body.removeChild(textArea);
|
|
155
|
-
setCopied(true);
|
|
156
|
-
setTimeout(() => setCopied(false), 2000);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}, [result]);
|
|
160
|
-
|
|
161
|
-
const handleOpenJob = useCallback(() => {
|
|
162
|
-
if (result?.jobUrl) {
|
|
163
|
-
window.open(result.jobUrl, "_blank");
|
|
164
|
-
}
|
|
165
|
-
}, [result]);
|
|
166
|
-
|
|
167
|
-
const renderContent = () => {
|
|
168
|
-
switch (state) {
|
|
169
|
-
case "confirm":
|
|
170
|
-
return (
|
|
171
|
-
<>
|
|
172
|
-
{isAllRecords ? (
|
|
173
|
-
<>
|
|
174
|
-
<MessageBar intent="error">
|
|
175
|
-
<MessageBarBody>
|
|
176
|
-
<MessageBarTitle>Danger Zone</MessageBarTitle>
|
|
177
|
-
This will delete ALL records matching your current FetchXML query!
|
|
178
|
-
</MessageBarBody>
|
|
179
|
-
</MessageBar>
|
|
180
|
-
|
|
181
|
-
<div className={styles.dangerWarning}>
|
|
182
|
-
<div className={styles.warningRow}>
|
|
183
|
-
<ErrorCircle20Regular />
|
|
184
|
-
<Text weight="semibold">
|
|
185
|
-
You have not selected any records. This will delete ALL records returned from
|
|
186
|
-
the view.
|
|
187
|
-
</Text>
|
|
188
|
-
</div>
|
|
189
|
-
<Text style={{ marginTop: tokens.spacingVerticalS, display: "block" }}>
|
|
190
|
-
This is a destructive operation that cannot be undone. The bulk delete job will
|
|
191
|
-
delete every record that matches your FetchXML query.
|
|
192
|
-
</Text>
|
|
193
|
-
</div>
|
|
194
|
-
|
|
195
|
-
<Checkbox
|
|
196
|
-
className={styles.confirmCheckbox}
|
|
197
|
-
checked={confirmAllRecords}
|
|
198
|
-
onChange={(_, data) => setConfirmAllRecords(data.checked === true)}
|
|
199
|
-
label="I understand that this will delete ALL records from the view"
|
|
200
|
-
/>
|
|
201
|
-
</>
|
|
202
|
-
) : (
|
|
203
|
-
<>
|
|
204
|
-
<div className={styles.warningRow}>
|
|
205
|
-
<Warning20Regular />
|
|
206
|
-
<Text weight="semibold">This action cannot be undone.</Text>
|
|
207
|
-
</div>
|
|
208
|
-
|
|
209
|
-
<Text>
|
|
210
|
-
You are about to delete {recordCount} {entityDisplayName || "records"}. This will
|
|
211
|
-
create a background job to process the deletion.
|
|
212
|
-
</Text>
|
|
213
|
-
</>
|
|
214
|
-
)}
|
|
215
|
-
|
|
216
|
-
<Field label="Job Name" required>
|
|
217
|
-
<Input
|
|
218
|
-
value={jobName}
|
|
219
|
-
onChange={(_, data) => setJobName(data.value)}
|
|
220
|
-
placeholder="Enter a name for the bulk delete job"
|
|
221
|
-
/>
|
|
222
|
-
</Field>
|
|
223
|
-
|
|
224
|
-
<Text size={200} style={{ color: tokens.colorNeutralForeground3 }}>
|
|
225
|
-
You can track the progress of the job in the System Jobs area.
|
|
226
|
-
</Text>
|
|
227
|
-
</>
|
|
228
|
-
);
|
|
229
|
-
|
|
230
|
-
case "submitting":
|
|
231
|
-
return (
|
|
232
|
-
<div className={styles.infoBox}>
|
|
233
|
-
<div style={{ display: "flex", alignItems: "center", gap: tokens.spacingHorizontalS }}>
|
|
234
|
-
<Spinner size="small" />
|
|
235
|
-
<Text>Submitting bulk delete job...</Text>
|
|
236
|
-
</div>
|
|
237
|
-
</div>
|
|
238
|
-
);
|
|
239
|
-
|
|
240
|
-
case "success":
|
|
241
|
-
return (
|
|
242
|
-
<>
|
|
243
|
-
<div className={styles.successRow}>
|
|
244
|
-
<CheckmarkCircle20Regular />
|
|
245
|
-
<Text weight="semibold">Bulk delete job submitted successfully!</Text>
|
|
246
|
-
</div>
|
|
247
|
-
|
|
248
|
-
<div className={styles.infoBox}>
|
|
249
|
-
<Text>
|
|
250
|
-
The bulk delete job has been submitted and will run in the background. You can track
|
|
251
|
-
its progress using the link below.
|
|
252
|
-
</Text>
|
|
253
|
-
|
|
254
|
-
{result?.jobUrl && (
|
|
255
|
-
<div className={styles.jobUrlRow}>
|
|
256
|
-
<Link onClick={handleOpenJob}>Open Job Status</Link>
|
|
257
|
-
<Button
|
|
258
|
-
appearance="subtle"
|
|
259
|
-
size="small"
|
|
260
|
-
icon={<Copy20Regular />}
|
|
261
|
-
onClick={handleCopyUrl}
|
|
262
|
-
>
|
|
263
|
-
{copied ? "Copied!" : "Copy URL"}
|
|
264
|
-
</Button>
|
|
265
|
-
</div>
|
|
266
|
-
)}
|
|
267
|
-
|
|
268
|
-
{result?.asyncOperationId && (
|
|
269
|
-
<Text
|
|
270
|
-
size={200}
|
|
271
|
-
style={{
|
|
272
|
-
marginTop: tokens.spacingVerticalS,
|
|
273
|
-
color: tokens.colorNeutralForeground3,
|
|
274
|
-
}}
|
|
275
|
-
>
|
|
276
|
-
Job ID: {result.asyncOperationId}
|
|
277
|
-
</Text>
|
|
278
|
-
)}
|
|
279
|
-
</div>
|
|
280
|
-
</>
|
|
281
|
-
);
|
|
282
|
-
|
|
283
|
-
case "error":
|
|
284
|
-
return (
|
|
285
|
-
<>
|
|
286
|
-
<div className={styles.warningRow}>
|
|
287
|
-
<Warning20Regular />
|
|
288
|
-
<Text weight="semibold">Failed to submit bulk delete job</Text>
|
|
289
|
-
</div>
|
|
290
|
-
|
|
291
|
-
<Text className={styles.errorMessage}>{error}</Text>
|
|
292
|
-
|
|
293
|
-
<Text size={200}>
|
|
294
|
-
Please check your permissions and try again. You need the prvBulkDelete privilege to
|
|
295
|
-
submit bulk delete jobs.
|
|
296
|
-
</Text>
|
|
297
|
-
</>
|
|
298
|
-
);
|
|
299
|
-
}
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
const renderActions = () => {
|
|
303
|
-
switch (state) {
|
|
304
|
-
case "confirm":
|
|
305
|
-
return (
|
|
306
|
-
<>
|
|
307
|
-
<Button appearance="secondary" onClick={handleClose}>
|
|
308
|
-
Cancel
|
|
309
|
-
</Button>
|
|
310
|
-
<Button
|
|
311
|
-
appearance="primary"
|
|
312
|
-
onClick={handleConfirm}
|
|
313
|
-
disabled={!jobName.trim() || (isAllRecords && !confirmAllRecords)}
|
|
314
|
-
>
|
|
315
|
-
Submit Bulk Delete
|
|
316
|
-
</Button>
|
|
317
|
-
</>
|
|
318
|
-
);
|
|
319
|
-
|
|
320
|
-
case "submitting":
|
|
321
|
-
return (
|
|
322
|
-
<Button appearance="secondary" disabled>
|
|
323
|
-
Cancel
|
|
324
|
-
</Button>
|
|
325
|
-
);
|
|
326
|
-
|
|
327
|
-
case "success":
|
|
328
|
-
case "error":
|
|
329
|
-
return (
|
|
330
|
-
<Button appearance="primary" onClick={handleClose}>
|
|
331
|
-
Close
|
|
332
|
-
</Button>
|
|
333
|
-
);
|
|
334
|
-
}
|
|
335
|
-
};
|
|
336
|
-
|
|
337
|
-
return (
|
|
338
|
-
<Dialog open={open} onOpenChange={(_, data) => !data.open && handleClose()}>
|
|
339
|
-
<DialogSurface>
|
|
340
|
-
<DialogTitle>
|
|
341
|
-
{isAllRecords
|
|
342
|
-
? `Bulk Delete ALL ${entityDisplayName || "Records"} from View`
|
|
343
|
-
: `Bulk Delete ${recordCount} Records`}
|
|
344
|
-
</DialogTitle>
|
|
345
|
-
<DialogBody>
|
|
346
|
-
<DialogContent className={styles.content}>{renderContent()}</DialogContent>
|
|
347
|
-
</DialogBody>
|
|
348
|
-
<DialogActions>{renderActions()}</DialogActions>
|
|
349
|
-
</DialogSurface>
|
|
350
|
-
</Dialog>
|
|
351
|
-
);
|
|
352
|
-
}
|