@finos/legend-application-query 12.0.7 → 12.0.9

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 (52) hide show
  1. package/lib/__lib__/LegendQueryEvent.d.ts +3 -1
  2. package/lib/__lib__/LegendQueryEvent.d.ts.map +1 -1
  3. package/lib/__lib__/LegendQueryEvent.js +2 -0
  4. package/lib/__lib__/LegendQueryEvent.js.map +1 -1
  5. package/lib/__lib__/LegendQueryTelemetryHelper.d.ts +2 -0
  6. package/lib/__lib__/LegendQueryTelemetryHelper.d.ts.map +1 -1
  7. package/lib/__lib__/LegendQueryTelemetryHelper.js +6 -0
  8. package/lib/__lib__/LegendQueryTelemetryHelper.js.map +1 -1
  9. package/lib/application/LegendQueryDocumentation.d.ts +19 -0
  10. package/lib/application/LegendQueryDocumentation.d.ts.map +1 -0
  11. package/lib/application/LegendQueryDocumentation.js +20 -0
  12. package/lib/application/LegendQueryDocumentation.js.map +1 -0
  13. package/lib/components/CloneQueryServiceSetup.d.ts.map +1 -1
  14. package/lib/components/CloneQueryServiceSetup.js +1 -2
  15. package/lib/components/CloneQueryServiceSetup.js.map +1 -1
  16. package/lib/components/CreateMappingQuerySetup.d.ts.map +1 -1
  17. package/lib/components/CreateMappingQuerySetup.js +1 -2
  18. package/lib/components/CreateMappingQuerySetup.js.map +1 -1
  19. package/lib/components/LegendQueryWebApplication.d.ts +0 -1
  20. package/lib/components/LegendQueryWebApplication.d.ts.map +1 -1
  21. package/lib/components/QueryEditor.d.ts +0 -1
  22. package/lib/components/QueryEditor.d.ts.map +1 -1
  23. package/lib/components/QueryEditor.js +113 -28
  24. package/lib/components/QueryEditor.js.map +1 -1
  25. package/lib/components/QuerySetup.d.ts +1 -1
  26. package/lib/components/QuerySetup.d.ts.map +1 -1
  27. package/lib/components/QuerySetup.js +13 -4
  28. package/lib/components/QuerySetup.js.map +1 -1
  29. package/lib/index.css +2 -2
  30. package/lib/index.css.map +1 -1
  31. package/lib/index.d.ts +1 -1
  32. package/lib/index.d.ts.map +1 -1
  33. package/lib/package.json +4 -4
  34. package/lib/stores/LegendQueryApplicationPlugin.d.ts +15 -5
  35. package/lib/stores/LegendQueryApplicationPlugin.d.ts.map +1 -1
  36. package/lib/stores/LegendQueryApplicationPlugin.js.map +1 -1
  37. package/lib/stores/QueryEditorStore.d.ts +52 -11
  38. package/lib/stores/QueryEditorStore.d.ts.map +1 -1
  39. package/lib/stores/QueryEditorStore.js +228 -21
  40. package/lib/stores/QueryEditorStore.js.map +1 -1
  41. package/package.json +11 -11
  42. package/src/__lib__/LegendQueryEvent.ts +3 -0
  43. package/src/__lib__/LegendQueryTelemetryHelper.ts +15 -0
  44. package/src/application/LegendQueryDocumentation.ts +19 -0
  45. package/src/components/CloneQueryServiceSetup.tsx +1 -5
  46. package/src/components/CreateMappingQuerySetup.tsx +1 -5
  47. package/src/components/QueryEditor.tsx +332 -73
  48. package/src/components/QuerySetup.tsx +17 -5
  49. package/src/index.ts +1 -1
  50. package/src/stores/LegendQueryApplicationPlugin.ts +22 -5
  51. package/src/stores/QueryEditorStore.ts +315 -31
  52. package/tsconfig.json +1 -0
@@ -18,7 +18,6 @@ import {
18
18
  type SelectComponent,
19
19
  Dialog,
20
20
  PanelLoadingIndicator,
21
- SaveIcon,
22
21
  BlankPanelContent,
23
22
  clsx,
24
23
  SearchIcon,
@@ -43,10 +42,15 @@ import {
43
42
  ManageSearchIcon,
44
43
  LightBulbIcon,
45
44
  EmptyLightBulbIcon,
45
+ SaveCurrIcon,
46
+ SaveAsIcon,
47
+ ExclamationTriangleIcon,
48
+ PanelListItem,
49
+ Button,
46
50
  } from '@finos/legend-art';
47
51
  import { debounce } from '@finos/legend-shared';
48
52
  import { observer } from 'mobx-react-lite';
49
- import { Fragment, useEffect, useMemo, useRef, useState } from 'react';
53
+ import React, { Fragment, useEffect, useMemo, useRef, useState } from 'react';
50
54
  import {
51
55
  type MappingQueryCreatorPathParams,
52
56
  type ExistingQueryEditorPathParams,
@@ -58,10 +62,11 @@ import {
58
62
  } from '../__lib__/LegendQueryNavigation.js';
59
63
  import {
60
64
  type QueryEditorStore,
61
- ExistingQueryEditorStore,
62
- QueryExportState,
65
+ QuerySaveAsState,
63
66
  createViewProjectHandler,
64
67
  createViewSDLCProjectHandler,
68
+ QuerySaveState,
69
+ QueryRenameState,
65
70
  } from '../stores/QueryEditorStore.js';
66
71
  import {
67
72
  LEGEND_APPLICATION_COLOR_THEME,
@@ -82,68 +87,105 @@ import {
82
87
  QueryBuilderNavigationBlocker,
83
88
  type QueryBuilderState,
84
89
  } from '@finos/legend-query-builder';
90
+ import type { QueryEditorHelpMenuEntry } from '../stores/LegendQueryApplicationPlugin.js';
91
+ import { QUERY_DOCUMENTATION_KEY } from '../application/LegendQueryDocumentation.js';
92
+ import { LegendQueryTelemetryHelper } from '../__lib__/LegendQueryTelemetryHelper.js';
85
93
 
86
- const QueryExportDialogContent = observer(
87
- (props: { exportState: QueryExportState }) => {
88
- const { exportState } = props;
94
+ const QuerySaveAsDialogContent = observer(
95
+ (props: { saveAsState: QuerySaveAsState }) => {
96
+ const { saveAsState } = props;
89
97
  const applicationStore = useApplicationStore();
90
- const allowCreate = exportState.allowPersist;
91
- const allowSave = exportState.allowPersist && exportState.allowUpdate;
92
- const create = applicationStore.guardUnhandledError(() =>
93
- exportState.persistQuery(true),
94
- );
95
- const save = applicationStore.guardUnhandledError(() =>
96
- exportState.persistQuery(false),
97
- );
98
+ const create = (): void => {
99
+ flowResult(saveAsState.createQuery(true)).catch(
100
+ applicationStore.alertUnhandledError,
101
+ );
102
+ };
103
+
104
+ const isExistingQueryName = saveAsState.editorStore.existingQueryName;
98
105
 
99
106
  // name
100
107
  const nameInputRef = useRef<HTMLInputElement>(null);
101
- const changeName: React.ChangeEventHandler<HTMLInputElement> = (event) =>
102
- exportState.setQueryName(event.target.value);
108
+
109
+ const debouncedLoadQueries = useMemo(
110
+ () =>
111
+ debounce((input: string): void => {
112
+ flowResult(
113
+ saveAsState.editorStore.searchExistingQueryName(input),
114
+ ).catch(applicationStore.alertUnhandledError);
115
+ }, 500),
116
+ [applicationStore, saveAsState.editorStore],
117
+ );
118
+
119
+ const changeName: React.ChangeEventHandler<HTMLInputElement> = (event) => {
120
+ saveAsState.setQueryName(event.target.value);
121
+ };
103
122
 
104
123
  useEffect(() => {
105
124
  nameInputRef.current?.focus();
106
125
  }, []);
107
126
 
127
+ useEffect(() => {
128
+ const searchText = saveAsState.queryName;
129
+ debouncedLoadQueries.cancel();
130
+ debouncedLoadQueries(searchText);
131
+ }, [
132
+ saveAsState.queryName,
133
+ debouncedLoadQueries,
134
+ saveAsState.editorStore.queryLoaderState.queries,
135
+ ]);
136
+
108
137
  return (
109
138
  <>
110
139
  <ModalBody>
111
140
  <PanelLoadingIndicator
112
- isLoading={exportState.persistQueryState.isInProgress}
113
- />
114
- <input
115
- ref={nameInputRef}
116
- className="input input--dark"
117
- spellCheck={false}
118
- value={exportState.queryName}
119
- onChange={changeName}
141
+ isLoading={saveAsState.createQueryState.isInProgress}
120
142
  />
143
+ <PanelListItem>
144
+ <div className="input--with-validation">
145
+ <input
146
+ ref={nameInputRef}
147
+ className={clsx('input input--dark', {
148
+ 'input--caution': isExistingQueryName,
149
+ })}
150
+ spellCheck={false}
151
+ value={saveAsState.queryName}
152
+ onChange={changeName}
153
+ />
154
+ {isExistingQueryName && (
155
+ <div
156
+ className="input--with-validation__caution"
157
+ title={`Query named '${isExistingQueryName}' already exists`}
158
+ >
159
+ <ExclamationTriangleIcon className="input--with-validation__caution__indicator" />
160
+ </div>
161
+ )}
162
+ </div>
163
+ </PanelListItem>
121
164
  </ModalBody>
122
165
  <ModalFooter>
123
- {allowSave && <ModalFooterButton text="Save" onClick={save} />}
124
- <button
125
- className="btn modal__footer__close-btn btn--dark"
126
- // TODO?: we should probably annotate here why,
127
- // when we disable this action
128
- disabled={!allowCreate}
166
+ <ModalFooterButton
167
+ text="Create Query"
168
+ title={
169
+ !saveAsState.allowPersist
170
+ ? `Cannot create new query`
171
+ : 'Create new query'
172
+ }
129
173
  onClick={create}
130
- >
131
- Create
132
- </button>
174
+ />
133
175
  </ModalFooter>
134
176
  </>
135
177
  );
136
178
  },
137
179
  );
138
180
 
139
- const QueryExport = observer(() => {
181
+ const QuerySaveDialog = observer(() => {
140
182
  const editorStore = useQueryEditorStore();
141
- const exportState = editorStore.exportState;
142
- const close = (): void => editorStore.setExportState(undefined);
183
+ const saveAsState = editorStore.saveAsState;
184
+ const close = (): void => editorStore.setSaveAsState(undefined);
143
185
 
144
186
  return (
145
187
  <Dialog
146
- open={Boolean(exportState)}
188
+ open={Boolean(saveAsState)}
147
189
  onClose={close}
148
190
  classes={{
149
191
  root: 'editor-modal__root-container',
@@ -152,8 +194,8 @@ const QueryExport = observer(() => {
152
194
  }}
153
195
  >
154
196
  <Modal darkMode={true} className="query-export">
155
- <ModalHeader title="save query" />
156
- {exportState && <QueryExportDialogContent exportState={exportState} />}
197
+ <ModalHeader title={'Create New Query'} />
198
+ {saveAsState && <QuerySaveAsDialogContent saveAsState={saveAsState} />}
157
199
  </Modal>
158
200
  </Dialog>
159
201
  );
@@ -166,6 +208,7 @@ const QueryLoader = observer(
166
208
  }) => {
167
209
  const { editorStore, queryBuilderState } = props;
168
210
  const applicationStore = useApplicationStore();
211
+
169
212
  const queryFinderRef = useRef<SelectComponent>(null);
170
213
  const searchInputRef = useRef<HTMLInputElement>(null);
171
214
  const [selectedQueryID, setSelectedQueryID] = useState('');
@@ -372,10 +415,22 @@ const QueryLoader = observer(
372
415
  },
373
416
  );
374
417
 
418
+ const HelpMenuContentItemRenderer = (
419
+ props: QueryEditorHelpMenuEntry,
420
+ ): React.ReactElement => (
421
+ <MenuContentItem title={props.title ?? ''} onClick={props.onClick}>
422
+ {props.icon && <MenuContentItemIcon>{props.icon}</MenuContentItemIcon>}
423
+ <MenuContentItemLabel className="query-builder__sub-header__menu-content">
424
+ {props.label}
425
+ </MenuContentItemLabel>
426
+ </MenuContentItem>
427
+ );
428
+
375
429
  const QueryEditorHeaderContent = observer(
376
430
  (props: { queryBuilderState: QueryBuilderState }) => {
377
431
  const { queryBuilderState } = props;
378
432
  const editorStore = useQueryEditorStore();
433
+ const renameState = editorStore.renameState;
379
434
  const applicationStore = useLegendQueryApplicationStore();
380
435
 
381
436
  // actions
@@ -383,6 +438,9 @@ const QueryEditorHeaderContent = observer(
383
438
  editorStore.queryLoaderState.setIsQueryLoaderOpen(true);
384
439
  };
385
440
  const viewProject = (): void => {
441
+ LegendQueryTelemetryHelper.logEvent_QueryViewProjectLaunched(
442
+ editorStore.applicationStore.telemetryService,
443
+ );
386
444
  const { groupId, artifactId, versionId } = editorStore.getProjectInfo();
387
445
  createViewProjectHandler(applicationStore)(
388
446
  groupId,
@@ -392,6 +450,9 @@ const QueryEditorHeaderContent = observer(
392
450
  );
393
451
  };
394
452
  const viewSDLCProject = (): void => {
453
+ LegendQueryTelemetryHelper.logEvent_QueryViewSdlcProjectLaunched(
454
+ editorStore.applicationStore.telemetryService,
455
+ );
395
456
  const { groupId, artifactId } = editorStore.getProjectInfo();
396
457
  createViewSDLCProjectHandler(
397
458
  applicationStore,
@@ -408,11 +469,58 @@ const QueryEditorHeaderContent = observer(
408
469
  { persist: true },
409
470
  );
410
471
  };
472
+
473
+ const renameRef = useRef<HTMLInputElement>(null);
474
+
475
+ const updateQuery = (): void => {
476
+ queryBuilderState
477
+ .saveQuery(async (lambda: RawLambda) => {
478
+ editorStore.setRenameState(
479
+ new QueryRenameState(
480
+ editorStore,
481
+ queryBuilderState,
482
+ lambda,
483
+ await editorStore.getExportConfiguration(lambda, {
484
+ update: true,
485
+ }),
486
+ ),
487
+ );
488
+ })
489
+ .then(() => {
490
+ renameRef.current?.select();
491
+ })
492
+ .catch(applicationStore.alertUnhandledError);
493
+ };
494
+
495
+ const renameQuery = applicationStore.guardUnhandledError(async () => {
496
+ await renameState?.renameQuery();
497
+ });
498
+
411
499
  const saveQuery = (): void => {
412
500
  queryBuilderState
413
501
  .saveQuery(async (lambda: RawLambda) => {
414
- editorStore.setExportState(
415
- new QueryExportState(
502
+ editorStore.setSaveState(
503
+ new QuerySaveState(
504
+ editorStore,
505
+ queryBuilderState,
506
+ lambda,
507
+ await editorStore.getExportConfiguration(lambda, {
508
+ update: true,
509
+ }),
510
+ ),
511
+ );
512
+ flowResult(editorStore.saveState?.saveQuery()).catch(
513
+ applicationStore.alertUnhandledError,
514
+ );
515
+ })
516
+ .catch(applicationStore.alertUnhandledError);
517
+ };
518
+
519
+ const saveAsQuery = (): void => {
520
+ queryBuilderState
521
+ .saveQuery(async (lambda: RawLambda) => {
522
+ editorStore.setSaveAsState(
523
+ new QuerySaveAsState(
416
524
  editorStore,
417
525
  queryBuilderState,
418
526
  lambda,
@@ -423,26 +531,123 @@ const QueryEditorHeaderContent = observer(
423
531
  .catch(applicationStore.alertUnhandledError);
424
532
  };
425
533
 
534
+ const toggleAssistant = (): void =>
535
+ applicationStore.assistantService.toggleAssistant();
536
+
537
+ const queryDocEntry = applicationStore.documentationService.getDocEntry(
538
+ QUERY_DOCUMENTATION_KEY.TUTORIAL_QUERY_BUILDER,
539
+ );
540
+
541
+ const openQueryTutorial = (): void => {
542
+ if (queryDocEntry?.url) {
543
+ applicationStore.navigationService.navigator.visitAddress(
544
+ queryDocEntry.url,
545
+ );
546
+ }
547
+ };
548
+
549
+ const changeQueryName: React.ChangeEventHandler<HTMLInputElement> = (
550
+ event,
551
+ ) => renameState?.setQueryName(event.target.value);
552
+
553
+ const onEnter: React.KeyboardEventHandler<HTMLInputElement> = (event) => {
554
+ if (event.code === 'Enter') {
555
+ renameQuery();
556
+ }
557
+ };
558
+
559
+ const debouncedLoadQueries = useMemo(
560
+ () =>
561
+ debounce((input: string): void => {
562
+ flowResult(
563
+ renameState?.editorStore.searchExistingQueryName(input),
564
+ ).catch(applicationStore.alertUnhandledError);
565
+ }, 500),
566
+ [applicationStore, renameState?.editorStore],
567
+ );
568
+
569
+ useEffect(() => {
570
+ if (renameState && renameState.queryName !== editorStore.title) {
571
+ const searchText = renameState.queryName;
572
+ debouncedLoadQueries.cancel();
573
+ debouncedLoadQueries(searchText);
574
+ }
575
+ }, [
576
+ renameState,
577
+ debouncedLoadQueries,
578
+ editorStore.title,
579
+ renameState?.queryName,
580
+ ]);
581
+
582
+ const isExistingQueryName = renameState?.editorStore.existingQueryName;
583
+
584
+ const extraHelpMenuContentItems = applicationStore.pluginManager
585
+ .getApplicationPlugins()
586
+ .flatMap((plugin) => plugin.getExtraQueryEditorHelpMenuEntries?.() ?? [])
587
+ .map((item) => <>{HelpMenuContentItemRenderer(item)}</>);
588
+
426
589
  return (
427
590
  <div className="query-editor__header__content">
428
- <div className="query-editor__header__content__main" />
591
+ {renameState ? (
592
+ <div className="query-editor__header__content__main query-editor__header__content__title">
593
+ <PanelListItem>
594
+ <div className="input--with-validation">
595
+ <input
596
+ title="Query title rename"
597
+ ref={renameRef}
598
+ className={clsx('input input--dark', {
599
+ 'input--caution': isExistingQueryName,
600
+ })}
601
+ onChange={changeQueryName}
602
+ onKeyDown={onEnter}
603
+ value={renameState.queryName}
604
+ placeholder="Search"
605
+ />
606
+ {isExistingQueryName && (
607
+ <div
608
+ className="input--with-validation__caution"
609
+ title={`Query named '${isExistingQueryName}' already exists`}
610
+ >
611
+ <ExclamationTriangleIcon className="input--with-validation__caution__indicator" />
612
+ </div>
613
+ )}
614
+ </div>
615
+ <button
616
+ className={clsx('input__btn', {
617
+ 'btn--icon__caution': isExistingQueryName,
618
+ })}
619
+ onClick={renameQuery}
620
+ title="Rename Query"
621
+ >
622
+ <CheckIcon />
623
+ </button>
624
+ </PanelListItem>
625
+ </div>
626
+ ) : (
627
+ <div
628
+ className="query-editor__header__content__main query-editor__header__content__title"
629
+ title="Query title"
630
+ >
631
+ {editorStore.title}
632
+ </div>
633
+ )}
634
+
429
635
  <div className="query-editor__header__actions">
430
- {editorStore instanceof ExistingQueryEditorStore &&
431
- applicationStore.pluginManager
432
- .getApplicationPlugins()
433
- .flatMap(
434
- (plugin) =>
435
- plugin.getExtraExistingQueryActionRendererConfiguration?.() ??
436
- [],
437
- )
438
- .map((actionConfig) => (
439
- <Fragment key={actionConfig.key}>
440
- {actionConfig.renderer(editorStore, queryBuilderState)}
441
- </Fragment>
442
- ))}
443
- <button
636
+ {applicationStore.pluginManager
637
+ .getApplicationPlugins()
638
+ .flatMap(
639
+ (plugin) =>
640
+ plugin.getExtraQueryEditorActionConfigurations?.(editorStore) ??
641
+ [],
642
+ )
643
+ .map((actionConfig) => (
644
+ <Fragment key={actionConfig.key}>
645
+ {actionConfig.renderer(editorStore, queryBuilderState)}
646
+ </Fragment>
647
+ ))}
648
+
649
+ <Button
444
650
  className="query-editor__header__action btn--dark"
445
- tabIndex={-1}
446
651
  onClick={openQueryLoader}
447
652
  title="Load query..."
448
653
  >
@@ -450,22 +655,69 @@ const QueryEditorHeaderContent = observer(
450
655
  <div className="query-editor__header__action__label">
451
656
  Load Query
452
657
  </div>
453
- </button>
658
+ </Button>
454
659
 
455
- <button
660
+ <Button
456
661
  className="query-editor__header__action btn--dark"
457
- tabIndex={-1}
458
- disabled={editorStore.isSaveActionDisabled}
662
+ disabled={
663
+ editorStore.isSaveActionDisabled ||
664
+ !editorStore.title ||
665
+ queryBuilderState.saveQueryProgressState.isInProgress
666
+ }
459
667
  onClick={saveQuery}
460
668
  title="Save query"
461
669
  >
462
- <SaveIcon />
463
-
670
+ <SaveCurrIcon />
671
+ <div className="query-editor__header__action__label">Save</div>
672
+ </Button>
673
+ <Button
674
+ className="query-editor__header__action btn--dark"
675
+ disabled={editorStore.isSaveActionDisabled}
676
+ onClick={saveAsQuery}
677
+ title="Save as new query"
678
+ >
679
+ <SaveAsIcon />
464
680
  <div className="query-editor__header__action__label">
465
- Save Query
681
+ Save As...
466
682
  </div>
467
- </button>
683
+ </Button>
684
+
685
+ <DropdownMenu
686
+ className="query-editor__header__action btn--dark"
687
+ disabled={editorStore.isViewProjectActionDisabled}
688
+ content={
689
+ <MenuContent>
690
+ {extraHelpMenuContentItems}
691
+ {queryDocEntry && (
692
+ <MenuContentItem onClick={openQueryTutorial}>
693
+ <MenuContentItemIcon>{null}</MenuContentItemIcon>
694
+ <MenuContentItemLabel className="query-builder__sub-header__menu-content">
695
+ Open Documentation
696
+ </MenuContentItemLabel>
697
+ </MenuContentItem>
698
+ )}
468
699
 
700
+ <MenuContentItem onClick={toggleAssistant}>
701
+ <MenuContentItemIcon>
702
+ {!applicationStore.assistantService.isHidden ? (
703
+ <CheckIcon />
704
+ ) : null}
705
+ </MenuContentItemIcon>
706
+ <MenuContentItemLabel className="query-builder__sub-header__menu-content">
707
+ Show Virtual Assistant
708
+ </MenuContentItemLabel>
709
+ </MenuContentItem>
710
+ </MenuContent>
711
+ }
712
+ >
713
+ <div
714
+ className=" query-editor__header__action__label"
715
+ title="See more options"
716
+ >
717
+ Help
718
+ </div>
719
+ <CaretDownIcon />
720
+ </DropdownMenu>
469
721
  {editorStore.queryLoaderState.isQueryLoaderOpen && (
470
722
  <QueryLoader
471
723
  editorStore={editorStore}
@@ -499,14 +751,21 @@ const QueryEditorHeaderContent = observer(
499
751
  disabled={editorStore.isViewProjectActionDisabled}
500
752
  onClick={viewProject}
501
753
  >
502
- Project
754
+ Go to Project
503
755
  </MenuContentItem>
504
756
  <MenuContentItem
505
757
  className="query-editor__header__action__options"
506
758
  disabled={editorStore.isViewProjectActionDisabled}
507
759
  onClick={viewSDLCProject}
508
760
  >
509
- SDLC project
761
+ Go to SDLC project
762
+ </MenuContentItem>
763
+ <MenuContentItem
764
+ className="query-editor__header__action__options"
765
+ onClick={updateQuery}
766
+ disabled={!editorStore.title}
767
+ >
768
+ Rename Query
510
769
  </MenuContentItem>
511
770
  </MenuContent>
512
771
  }
@@ -515,12 +774,12 @@ const QueryEditorHeaderContent = observer(
515
774
  className="query-editor__header__action__label"
516
775
  title="See more options"
517
776
  >
518
- Go to...
777
+ More Actions...
519
778
  </div>
520
779
  <CaretDownIcon />
521
780
  </DropdownMenu>
522
781
 
523
- {editorStore.exportState && <QueryExport />}
782
+ {editorStore.saveAsState && <QuerySaveDialog />}
524
783
  </div>
525
784
  </div>
526
785
  );
@@ -574,15 +833,15 @@ export const QueryEditor = observer(() => {
574
833
  }}
575
834
  content={
576
835
  <MenuContent>
836
+ <MenuContentItem onClick={goToQuerySetup}>
837
+ Back to query setup
838
+ </MenuContentItem>
577
839
  <MenuContentItem
578
840
  disabled={!appDocUrl}
579
841
  onClick={goToDocumentation}
580
842
  >
581
843
  See Documentation
582
844
  </MenuContentItem>
583
- <MenuContentItem onClick={goToQuerySetup}>
584
- Back to query setup
585
- </MenuContentItem>
586
845
  <MenuContentDivider />
587
846
  <MenuContentItem disabled={true}>Settings</MenuContentItem>
588
847
  <MenuContentItem
@@ -40,7 +40,11 @@ import {
40
40
  QuerySetupLandingPageStore,
41
41
  type BaseQuerySetupStore,
42
42
  } from '../stores/QuerySetupStore.js';
43
- import type { StoreProjectData } from '@finos/legend-server-depot';
43
+ import {
44
+ MASTER_SNAPSHOT_ALIAS,
45
+ SNAPSHOT_VERSION_ALIAS,
46
+ type StoreProjectData,
47
+ } from '@finos/legend-server-depot';
44
48
  import { useApplicationStore } from '@finos/legend-application';
45
49
  import { useLegendQueryApplicationStore } from './LegendQueryFrameworkProvider.js';
46
50
  import type { QuerySetupActionConfiguration } from '../stores/LegendQueryApplicationPlugin.js';
@@ -54,10 +58,18 @@ export const buildProjectOption = (
54
58
  });
55
59
 
56
60
  export type VersionOption = { label: string; value: string };
57
- export const buildVersionOption = (version: string): VersionOption => ({
58
- label: version,
59
- value: version,
60
- });
61
+ export const buildVersionOption = (version: string): VersionOption => {
62
+ if (version === MASTER_SNAPSHOT_ALIAS) {
63
+ return {
64
+ label: SNAPSHOT_VERSION_ALIAS,
65
+ value: version,
66
+ };
67
+ }
68
+ return {
69
+ label: version,
70
+ value: version,
71
+ };
72
+ };
61
73
 
62
74
  const QuerySetupLandingPageStoreContext = createContext<
63
75
  QuerySetupLandingPageStore | undefined
package/src/index.ts CHANGED
@@ -37,7 +37,7 @@ export {
37
37
  createViewSDLCProjectHandler,
38
38
  QueryEditorStore,
39
39
  ExistingQueryEditorStore,
40
- type QueryExportConfiguration,
40
+ type QueryPersistConfiguration,
41
41
  } from './stores/QueryEditorStore.js';
42
42
 
43
43
  // components
@@ -19,7 +19,10 @@ import type { Query } from '@finos/legend-graph';
19
19
  import type { QueryBuilderState } from '@finos/legend-query-builder';
20
20
  import type React from 'react';
21
21
  import type { LegendQueryPluginManager } from '../application/LegendQueryPluginManager.js';
22
- import type { ExistingQueryEditorStore } from './QueryEditorStore.js';
22
+ import type {
23
+ ExistingQueryEditorStore,
24
+ QueryEditorStore,
25
+ } from './QueryEditorStore.js';
23
26
  import type { QuerySetupLandingPageStore } from './QuerySetupStore.js';
24
27
 
25
28
  export enum QuerySetupActionTag {
@@ -53,14 +56,21 @@ export type ExistingQueryEditorStateBuilder = (
53
56
  editorStore: ExistingQueryEditorStore,
54
57
  ) => QueryBuilderState | undefined;
55
58
 
56
- export type ExistingQueryEditorActionRendererConfiguration = {
59
+ export type QueryEditorActionConfiguration = {
57
60
  key: string;
58
61
  renderer: (
59
- editorStore: ExistingQueryEditorStore,
62
+ editorStore: QueryEditorStore,
60
63
  queryBuilderState: QueryBuilderState,
61
64
  ) => React.ReactNode | undefined;
62
65
  };
63
66
 
67
+ export type QueryEditorHelpMenuEntry = {
68
+ title?: string;
69
+ label: string;
70
+ onClick: () => void;
71
+ icon?: React.ReactNode;
72
+ };
73
+
64
74
  export abstract class LegendQueryApplicationPlugin extends LegendApplicationPlugin {
65
75
  /**
66
76
  * This helps to better type-check for this empty abtract type
@@ -77,13 +87,20 @@ export abstract class LegendQueryApplicationPlugin extends LegendApplicationPlug
77
87
  */
78
88
  getExtraQuerySetupActionConfigurations?(): QuerySetupActionConfiguration[];
79
89
 
90
+ /**
91
+ * Get the list of help items for query.
92
+ */
93
+ getExtraQueryEditorHelpMenuEntries?(): QueryEditorHelpMenuEntry[];
94
+
80
95
  /**
81
96
  * Get the list of existing query editor state builders.
82
97
  */
83
98
  getExtraExistingQueryEditorStateBuilders?(): ExistingQueryEditorStateBuilder[];
84
99
 
85
100
  /**
86
- * Get the list of renderer configurations for existing query editor actions.
101
+ * Get the list of query editor action renderer configurations.
87
102
  */
88
- getExtraExistingQueryActionRendererConfiguration?(): ExistingQueryEditorActionRendererConfiguration[];
103
+ getExtraQueryEditorActionConfigurations?(
104
+ editorStore: QueryEditorStore,
105
+ ): QueryEditorActionConfiguration[];
89
106
  }