@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,612 +0,0 @@
1
- /**
2
- * SaveViewDialog - Dialog for saving views to Dataverse
3
- * Handles validation, name/description input, and view type selection
4
- */
5
-
6
- import { useState, useEffect, useCallback, useMemo } from "react";
7
- import {
8
- Dialog,
9
- DialogSurface,
10
- DialogBody,
11
- DialogTitle,
12
- DialogContent,
13
- DialogActions,
14
- Button,
15
- Input,
16
- Textarea,
17
- Field,
18
- Radio,
19
- RadioGroup,
20
- MessageBar,
21
- MessageBarTitle,
22
- MessageBarBody,
23
- Spinner,
24
- makeStyles,
25
- tokens,
26
- Divider,
27
- Text,
28
- } from "@fluentui/react-components";
29
- import {
30
- DismissRegular,
31
- InfoRegular,
32
- WarningRegular,
33
- ErrorCircleRegular,
34
- CheckmarkCircleRegular,
35
- } from "@fluentui/react-icons";
36
- import {
37
- validateFetchXmlExpression,
38
- checkSavedQueryPrivileges,
39
- checkUserQueryPrivileges,
40
- createSavedQuery,
41
- updateSavedQuery,
42
- createUserQuery,
43
- updateUserQuery,
44
- publishSavedQuery,
45
- addSolutionComponent,
46
- getUnmanagedSolutions,
47
- COMPONENT_TYPE_SAVEDQUERY,
48
- type ValidatorIssue,
49
- type Solution,
50
- } from "../../api/pptbClient";
51
- import { SolutionPicker } from "./SolutionPicker";
52
-
53
- const useStyles = makeStyles({
54
- surface: {
55
- maxWidth: "500px",
56
- width: "100%",
57
- },
58
- content: {
59
- display: "flex",
60
- flexDirection: "column",
61
- gap: "16px",
62
- },
63
- fieldGroup: {
64
- display: "flex",
65
- flexDirection: "column",
66
- gap: "12px",
67
- },
68
- validationSection: {
69
- display: "flex",
70
- flexDirection: "column",
71
- gap: "8px",
72
- },
73
- validationHeader: {
74
- display: "flex",
75
- alignItems: "center",
76
- gap: "8px",
77
- },
78
- validationList: {
79
- display: "flex",
80
- flexDirection: "column",
81
- gap: "6px",
82
- maxHeight: "150px",
83
- overflowY: "auto",
84
- paddingLeft: "4px",
85
- },
86
- validationItem: {
87
- display: "flex",
88
- alignItems: "flex-start",
89
- gap: "8px",
90
- fontSize: "13px",
91
- },
92
- severityIcon: {
93
- flexShrink: 0,
94
- marginTop: "2px",
95
- },
96
- severityLow: {
97
- color: tokens.colorPaletteBlueBackground2,
98
- },
99
- severityMedium: {
100
- color: tokens.colorPaletteYellowForeground1,
101
- },
102
- severityHigh: {
103
- color: tokens.colorPaletteRedForeground1,
104
- },
105
- severityCritical: {
106
- color: tokens.colorPaletteRedForeground1,
107
- },
108
- solutionInfo: {
109
- display: "flex",
110
- flexDirection: "column",
111
- gap: "4px",
112
- padding: "8px 12px",
113
- backgroundColor: tokens.colorNeutralBackground3,
114
- borderRadius: "4px",
115
- marginTop: "8px",
116
- },
117
- solutionLabel: {
118
- fontSize: "12px",
119
- color: tokens.colorNeutralForeground3,
120
- },
121
- solutionValue: {
122
- fontSize: "13px",
123
- fontWeight: 500,
124
- },
125
- overwriteWarning: {
126
- marginTop: "4px",
127
- },
128
- radioDescription: {
129
- fontSize: "12px",
130
- color: tokens.colorNeutralForeground3,
131
- marginLeft: "28px",
132
- marginTop: "-4px",
133
- },
134
- publishNote: {
135
- fontSize: "12px",
136
- color: tokens.colorNeutralForeground3,
137
- fontStyle: "italic",
138
- },
139
- });
140
-
141
- export type SaveViewDialogMode = "save" | "saveAs";
142
-
143
- interface SaveViewDialogProps {
144
- /** Whether the dialog is open */
145
- open: boolean;
146
- /** Close callback */
147
- onClose: () => void;
148
- /** Save complete callback */
149
- onSaveComplete: (viewId: string, viewType: "system" | "personal", viewName: string) => void;
150
- /** Dialog mode: save (overwrite) or saveAs (new) */
151
- mode: SaveViewDialogMode;
152
- /** Whether to publish after save (system views only) */
153
- shouldPublish: boolean;
154
- /** FetchXML to save */
155
- fetchXml: string;
156
- /** LayoutXML configuration */
157
- layoutXml: string;
158
- /** Entity logical name */
159
- entityLogicalName: string;
160
- /** Entity object type code */
161
- objectTypeCode: number;
162
- /** Primary ID attribute */
163
- primaryIdAttribute: string;
164
- /** Currently loaded view (for Save mode) */
165
- loadedView: {
166
- id: string;
167
- type: "system" | "personal";
168
- name: string;
169
- } | null;
170
- }
171
-
172
- interface ValidationState {
173
- status: "idle" | "validating" | "valid" | "warning" | "error";
174
- messages: ValidatorIssue[];
175
- errorMessage?: string;
176
- }
177
-
178
- export function SaveViewDialog({
179
- open,
180
- onClose,
181
- onSaveComplete,
182
- mode,
183
- shouldPublish,
184
- fetchXml,
185
- layoutXml,
186
- entityLogicalName,
187
- objectTypeCode: _objectTypeCode, // Reserved for future use
188
- primaryIdAttribute: _primaryIdAttribute, // Reserved for future use
189
- loadedView,
190
- }: SaveViewDialogProps) {
191
- const styles = useStyles();
192
-
193
- // Form state
194
- const [name, setName] = useState("");
195
- const [description, setDescription] = useState("");
196
- const [viewType, setViewType] = useState<"system" | "personal">("personal");
197
-
198
- // Solution state (for system views)
199
- const [selectedSolution, setSelectedSolution] = useState<Solution | null>(null);
200
- const [unmanagedSolutions, setUnmanagedSolutions] = useState<Solution[]>([]);
201
- const [solutionsLoading, setSolutionsLoading] = useState(false);
202
-
203
- // Privileges state - systemView privileges from checkSavedQueryPrivileges
204
- const [systemPrivileges, setSystemPrivileges] = useState<{
205
- canWrite: boolean;
206
- canPublish: boolean;
207
- } | null>(null);
208
- // Personal view privileges from checkUserQueryPrivileges
209
- const [personalPrivileges, setPersonalPrivileges] = useState<{
210
- canWrite: boolean;
211
- } | null>(null);
212
-
213
- // Validation state
214
- const [validation, setValidation] = useState<ValidationState>({
215
- status: "idle",
216
- messages: [],
217
- });
218
-
219
- // Save state
220
- const [isSaving, setIsSaving] = useState(false);
221
- const [saveError, setSaveError] = useState<string | null>(null);
222
-
223
- // Check if this is a "Save" to existing view
224
- const isOverwriteMode = mode === "save" && loadedView !== null;
225
-
226
- // Initialize form when dialog opens
227
- useEffect(() => {
228
- if (open) {
229
- if (isOverwriteMode && loadedView) {
230
- setName(loadedView.name);
231
- setViewType(loadedView.type);
232
- } else {
233
- setName("");
234
- setViewType("personal");
235
- }
236
- setDescription("");
237
- setSaveError(null);
238
- setValidation({ status: "idle", messages: [] });
239
- }
240
- }, [open, isOverwriteMode, loadedView]);
241
-
242
- // Check privileges when dialog opens
243
- useEffect(() => {
244
- if (open) {
245
- // Check both system and personal view privileges
246
- checkSavedQueryPrivileges().then(setSystemPrivileges);
247
- checkUserQueryPrivileges().then(setPersonalPrivileges);
248
- }
249
- }, [open]);
250
-
251
- // Load unmanaged solutions when system view is selected
252
- useEffect(() => {
253
- if (!open || viewType !== "system") {
254
- return;
255
- }
256
-
257
- async function loadSolutionInfo() {
258
- setSolutionsLoading(true);
259
- try {
260
- // Always load unmanaged solutions for the picker
261
- const solutions = await getUnmanagedSolutions();
262
- setUnmanagedSolutions(solutions);
263
- } catch (error) {
264
- console.error("Failed to load solution info:", error);
265
- setUnmanagedSolutions([]);
266
- } finally {
267
- setSolutionsLoading(false);
268
- }
269
- }
270
-
271
- loadSolutionInfo();
272
- }, [open, viewType]);
273
-
274
- // Auto-validate when dialog opens
275
- useEffect(() => {
276
- if (!open || !fetchXml) {
277
- return;
278
- }
279
-
280
- async function runValidation() {
281
- setValidation({ status: "validating", messages: [] });
282
-
283
- try {
284
- const result = await validateFetchXmlExpression(fetchXml);
285
- const messages = result.ValidationResults?.Messages || [];
286
-
287
- if (messages.length === 0) {
288
- setValidation({ status: "valid", messages: [] });
289
- } else {
290
- // Check for critical/high severity
291
- const hasCritical = messages.some((m) => m.Severity >= 2);
292
- setValidation({
293
- status: hasCritical ? "warning" : "warning",
294
- messages,
295
- });
296
- }
297
- } catch (error) {
298
- // Validation API call failed - this doesn't mean the FetchXML is invalid
299
- // Just skip validation and allow the user to proceed
300
- console.warn("FetchXML validation unavailable:", error);
301
- setValidation({
302
- status: "warning",
303
- messages: [],
304
- errorMessage: "Validation unavailable - FetchXML will be validated when saved",
305
- });
306
- }
307
- }
308
-
309
- runValidation();
310
- }, [open, fetchXml]);
311
-
312
- // Get severity icon
313
- const getSeverityIcon = useCallback(
314
- (severity: number) => {
315
- switch (severity) {
316
- case 0:
317
- return <InfoRegular className={`${styles.severityIcon} ${styles.severityLow}`} />;
318
- case 1:
319
- return <WarningRegular className={`${styles.severityIcon} ${styles.severityMedium}`} />;
320
- case 2:
321
- case 3:
322
- return <ErrorCircleRegular className={`${styles.severityIcon} ${styles.severityHigh}`} />;
323
- default:
324
- return <InfoRegular className={`${styles.severityIcon} ${styles.severityLow}`} />;
325
- }
326
- },
327
- [styles]
328
- );
329
-
330
- // Can save check
331
- const canSave = useMemo(() => {
332
- if (!name.trim()) return false;
333
- if (isSaving) return false;
334
- if (viewType === "system") {
335
- // System views require canWrite privilege (covers both create and update)
336
- if (!systemPrivileges?.canWrite) return false;
337
- } else {
338
- // Personal views require canWrite privilege
339
- if (!personalPrivileges?.canWrite) return false;
340
- }
341
- return true;
342
- }, [name, isSaving, viewType, systemPrivileges, personalPrivileges]);
343
-
344
- // Handle save
345
- const handleSave = useCallback(async () => {
346
- if (!canSave) return;
347
-
348
- setIsSaving(true);
349
- setSaveError(null);
350
-
351
- try {
352
- let viewId: string;
353
-
354
- if (viewType === "personal") {
355
- // Personal view
356
- if (isOverwriteMode && loadedView) {
357
- await updateUserQuery(loadedView.id, {
358
- name: name.trim(),
359
- fetchxml: fetchXml,
360
- layoutxml: layoutXml,
361
- description: description.trim() || undefined,
362
- });
363
- viewId = loadedView.id;
364
- } else {
365
- viewId = await createUserQuery({
366
- name: name.trim(),
367
- fetchxml: fetchXml,
368
- layoutxml: layoutXml,
369
- returnedtypecode: entityLogicalName,
370
- description: description.trim() || undefined,
371
- querytype: 0,
372
- });
373
- }
374
- } else {
375
- // System view
376
- if (isOverwriteMode && loadedView) {
377
- await updateSavedQuery(loadedView.id, {
378
- name: name.trim(),
379
- fetchxml: fetchXml,
380
- layoutxml: layoutXml,
381
- description: description.trim() || undefined,
382
- });
383
- viewId = loadedView.id;
384
- } else {
385
- viewId = await createSavedQuery({
386
- name: name.trim(),
387
- fetchxml: fetchXml,
388
- layoutxml: layoutXml,
389
- returnedtypecode: entityLogicalName,
390
- description: description.trim() || undefined,
391
- querytype: 0,
392
- });
393
-
394
- // Add to solution if selected
395
- if (selectedSolution) {
396
- await addSolutionComponent(
397
- viewId,
398
- COMPONENT_TYPE_SAVEDQUERY,
399
- selectedSolution.uniquename,
400
- false
401
- );
402
- }
403
- }
404
-
405
- // Publish if requested
406
- if (shouldPublish) {
407
- await publishSavedQuery(viewId);
408
- }
409
- }
410
-
411
- onSaveComplete(viewId, viewType, name.trim());
412
- } catch (error) {
413
- console.error("Failed to save view:", error);
414
- setSaveError(error instanceof Error ? error.message : "Failed to save view");
415
- } finally {
416
- setIsSaving(false);
417
- }
418
- }, [
419
- canSave,
420
- viewType,
421
- isOverwriteMode,
422
- loadedView,
423
- name,
424
- fetchXml,
425
- layoutXml,
426
- description,
427
- entityLogicalName,
428
- selectedSolution,
429
- shouldPublish,
430
- onSaveComplete,
431
- ]);
432
-
433
- // Dialog title
434
- const dialogTitle = useMemo(() => {
435
- if (isOverwriteMode) {
436
- return shouldPublish ? "Save and Publish View" : "Save View";
437
- }
438
- return "Save As New View";
439
- }, [isOverwriteMode, shouldPublish]);
440
-
441
- return (
442
- <Dialog open={open} onOpenChange={(_, data) => !data.open && onClose()}>
443
- <DialogSurface className={styles.surface}>
444
- <DialogBody>
445
- <DialogTitle
446
- action={
447
- <Button
448
- appearance="subtle"
449
- aria-label="Close"
450
- icon={<DismissRegular />}
451
- onClick={onClose}
452
- />
453
- }
454
- >
455
- {dialogTitle}
456
- </DialogTitle>
457
- <DialogContent className={styles.content}>
458
- {/* Validation Section */}
459
- <div className={styles.validationSection}>
460
- <div className={styles.validationHeader}>
461
- {validation.status === "validating" && (
462
- <>
463
- <Spinner size="tiny" />
464
- <Text>Validating FetchXML...</Text>
465
- </>
466
- )}
467
- {validation.status === "valid" && (
468
- <>
469
- <CheckmarkCircleRegular
470
- style={{ color: tokens.colorPaletteGreenForeground1 }}
471
- />
472
- <Text>FetchXML is valid</Text>
473
- </>
474
- )}
475
- {validation.status === "warning" && (
476
- <>
477
- <WarningRegular style={{ color: tokens.colorPaletteYellowForeground1 }} />
478
- <Text>
479
- {validation.messages.length} validation{" "}
480
- {validation.messages.length === 1 ? "message" : "messages"}
481
- </Text>
482
- </>
483
- )}
484
- {validation.status === "error" && (
485
- <>
486
- <ErrorCircleRegular style={{ color: tokens.colorPaletteRedForeground1 }} />
487
- <Text>{validation.errorMessage}</Text>
488
- </>
489
- )}
490
- </div>
491
-
492
- {validation.messages.length > 0 && (
493
- <div className={styles.validationList}>
494
- {validation.messages.map((msg, idx) => (
495
- <div key={idx} className={styles.validationItem}>
496
- {getSeverityIcon(msg.Severity)}
497
- <span>{msg.LocalizedMessageText}</span>
498
- </div>
499
- ))}
500
- </div>
501
- )}
502
- </div>
503
-
504
- <Divider />
505
-
506
- {/* Form Fields */}
507
- <div className={styles.fieldGroup}>
508
- <Field label="Name" required>
509
- <Input
510
- value={name}
511
- onChange={(_, data) => setName(data.value)}
512
- placeholder="Enter view name"
513
- disabled={isSaving}
514
- />
515
- </Field>
516
- <Field label="Description">
517
- <Textarea
518
- value={description}
519
- onChange={(_, data) => setDescription(data.value)}
520
- placeholder="Optional description"
521
- disabled={isSaving}
522
- rows={2}
523
- />
524
- </Field>
525
- {/* View Type Selection - only for Save As */}
526
- {!isOverwriteMode && (
527
- <Field label="View Type">
528
- <RadioGroup
529
- value={viewType}
530
- onChange={(_, data) => setViewType(data.value as "system" | "personal")}
531
- disabled={isSaving}
532
- >
533
- <Radio
534
- value="personal"
535
- label="Personal View"
536
- disabled={!personalPrivileges?.canWrite}
537
- />
538
- <div className={styles.radioDescription}>
539
- {personalPrivileges?.canWrite
540
- ? "Only visible to you"
541
- : "You don't have permission to create personal views"}
542
- </div>
543
- <Radio
544
- value="system"
545
- label="System View"
546
- disabled={!systemPrivileges?.canWrite}
547
- />
548
- <div className={styles.radioDescription}>
549
- {systemPrivileges?.canWrite
550
- ? "Visible to all users with access to this entity"
551
- : "You don't have permission to create system views"}
552
- </div>
553
- </RadioGroup>
554
- </Field>
555
- )}
556
- {/* Solution Picker for System Views */}
557
- {viewType === "system" && !isOverwriteMode && (
558
- <>
559
- {solutionsLoading ? (
560
- <div className={styles.solutionInfo}>
561
- <Spinner size="tiny" label="Loading solutions..." />
562
- </div>
563
- ) : (
564
- <SolutionPicker
565
- solutions={unmanagedSolutions}
566
- selectedSolution={selectedSolution}
567
- onSolutionChange={setSelectedSolution}
568
- disabled={isSaving}
569
- />
570
- )}
571
- </>
572
- )}{" "}
573
- {/* Overwrite Warning */}
574
- {isOverwriteMode && loadedView && (
575
- <MessageBar intent="warning" className={styles.overwriteWarning}>
576
- <MessageBarBody>
577
- <MessageBarTitle>Overwrite Warning</MessageBarTitle>
578
- This will overwrite the existing view "{loadedView.name}".
579
- </MessageBarBody>
580
- </MessageBar>
581
- )}
582
- {/* Publish Note */}
583
- {shouldPublish && viewType === "system" && (
584
- <Text className={styles.publishNote}>
585
- The view will be published after saving, making it immediately available to users.
586
- </Text>
587
- )}
588
- </div>
589
-
590
- {/* Save Error */}
591
- {saveError && (
592
- <MessageBar intent="error">
593
- <MessageBarBody>
594
- <MessageBarTitle>Save Failed</MessageBarTitle>
595
- {saveError}
596
- </MessageBarBody>
597
- </MessageBar>
598
- )}
599
- </DialogContent>
600
- <DialogActions>
601
- <Button appearance="secondary" onClick={onClose} disabled={isSaving}>
602
- Cancel
603
- </Button>
604
- <Button appearance="primary" onClick={handleSave} disabled={!canSave}>
605
- {isSaving ? <Spinner size="tiny" /> : shouldPublish ? "Save and Publish" : "Save"}
606
- </Button>
607
- </DialogActions>
608
- </DialogBody>
609
- </DialogSurface>
610
- </Dialog>
611
- );
612
- }