@finos/legend-application-query 8.1.2 → 9.0.0

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 (125) hide show
  1. package/lib/application/LegendQuery.d.ts.map +1 -1
  2. package/lib/application/LegendQuery.js +7 -9
  3. package/lib/application/LegendQuery.js.map +1 -1
  4. package/lib/application/LegendQueryApplicationConfig.d.ts +4 -0
  5. package/lib/application/LegendQueryApplicationConfig.d.ts.map +1 -1
  6. package/lib/application/LegendQueryApplicationConfig.js +4 -0
  7. package/lib/application/LegendQueryApplicationConfig.js.map +1 -1
  8. package/lib/components/{QuerySetupStoreProvider.d.ts → CloneQueryServiceSetup.d.ts} +2 -7
  9. package/lib/components/CloneQueryServiceSetup.d.ts.map +1 -0
  10. package/lib/components/CloneQueryServiceSetup.js +137 -0
  11. package/lib/components/CloneQueryServiceSetup.js.map +1 -0
  12. package/lib/components/Core_LegendQueryApplicationPlugin.d.ts +24 -0
  13. package/lib/components/Core_LegendQueryApplicationPlugin.d.ts.map +1 -0
  14. package/lib/components/Core_LegendQueryApplicationPlugin.js +144 -0
  15. package/lib/components/Core_LegendQueryApplicationPlugin.js.map +1 -0
  16. package/lib/components/CreateMappingQuerySetup.d.ts +18 -0
  17. package/lib/components/CreateMappingQuerySetup.d.ts.map +1 -0
  18. package/lib/components/CreateMappingQuerySetup.js +160 -0
  19. package/lib/components/CreateMappingQuerySetup.js.map +1 -0
  20. package/lib/components/EditExistingQuerySetup.d.ts +18 -0
  21. package/lib/components/EditExistingQuerySetup.d.ts.map +1 -0
  22. package/lib/components/EditExistingQuerySetup.js +107 -0
  23. package/lib/components/EditExistingQuerySetup.js.map +1 -0
  24. package/lib/components/LegendQueryApplication.d.ts.map +1 -1
  25. package/lib/components/LegendQueryApplication.js +4 -2
  26. package/lib/components/LegendQueryApplication.js.map +1 -1
  27. package/lib/components/LoadProjectServiceQuerySetup.d.ts +18 -0
  28. package/lib/components/LoadProjectServiceQuerySetup.d.ts.map +1 -0
  29. package/lib/components/LoadProjectServiceQuerySetup.js +63 -0
  30. package/lib/components/LoadProjectServiceQuerySetup.js.map +1 -0
  31. package/lib/components/QueryEditor.d.ts.map +1 -1
  32. package/lib/components/QueryEditor.js +31 -33
  33. package/lib/components/QueryEditor.js.map +1 -1
  34. package/lib/components/QueryProductionizerSetup.d.ts +18 -0
  35. package/lib/components/QueryProductionizerSetup.d.ts.map +1 -0
  36. package/lib/components/QueryProductionizerSetup.js +85 -0
  37. package/lib/components/QueryProductionizerSetup.js.map +1 -0
  38. package/lib/components/QuerySetup.d.ts +20 -5
  39. package/lib/components/QuerySetup.d.ts.map +1 -1
  40. package/lib/components/QuerySetup.js +69 -473
  41. package/lib/components/QuerySetup.js.map +1 -1
  42. package/lib/components/UpdateExistingServiceQuerySetup.d.ts +18 -0
  43. package/lib/components/UpdateExistingServiceQuerySetup.d.ts.map +1 -0
  44. package/lib/components/UpdateExistingServiceQuerySetup.js +69 -0
  45. package/lib/components/UpdateExistingServiceQuerySetup.js.map +1 -0
  46. package/lib/index.css +2 -2
  47. package/lib/index.css.map +1 -1
  48. package/lib/index.d.ts +2 -3
  49. package/lib/index.d.ts.map +1 -1
  50. package/lib/index.js +2 -3
  51. package/lib/index.js.map +1 -1
  52. package/lib/package.json +5 -8
  53. package/lib/stores/CloneServiceQuerySetupStore.d.ts +41 -0
  54. package/lib/stores/CloneServiceQuerySetupStore.d.ts.map +1 -0
  55. package/lib/stores/CloneServiceQuerySetupStore.js +98 -0
  56. package/lib/stores/CloneServiceQuerySetupStore.js.map +1 -0
  57. package/lib/stores/CreateMappingQuerySetupStore.d.ts +40 -0
  58. package/lib/stores/CreateMappingQuerySetupStore.d.ts.map +1 -0
  59. package/lib/stores/CreateMappingQuerySetupStore.js +97 -0
  60. package/lib/stores/CreateMappingQuerySetupStore.js.map +1 -0
  61. package/lib/stores/EditExistingQuerySetupStore.d.ts +33 -0
  62. package/lib/stores/EditExistingQuerySetupStore.d.ts.map +1 -0
  63. package/lib/stores/EditExistingQuerySetupStore.js +85 -0
  64. package/lib/stores/EditExistingQuerySetupStore.js.map +1 -0
  65. package/lib/stores/LegendQueryApplicationPlugin.d.ts +21 -16
  66. package/lib/stores/LegendQueryApplicationPlugin.d.ts.map +1 -1
  67. package/lib/stores/LegendQueryApplicationPlugin.js +4 -0
  68. package/lib/stores/LegendQueryApplicationPlugin.js.map +1 -1
  69. package/lib/stores/LegendQueryRouter.d.ts +28 -1
  70. package/lib/stores/LegendQueryRouter.d.ts.map +1 -1
  71. package/lib/stores/LegendQueryRouter.js +33 -3
  72. package/lib/stores/LegendQueryRouter.js.map +1 -1
  73. package/lib/stores/LoadProjectServiceQuerySetupStore.d.ts +27 -0
  74. package/lib/stores/LoadProjectServiceQuerySetupStore.d.ts.map +1 -0
  75. package/lib/stores/LoadProjectServiceQuerySetupStore.js +61 -0
  76. package/lib/stores/LoadProjectServiceQuerySetupStore.js.map +1 -0
  77. package/lib/stores/QueryEditorStore.d.ts +6 -2
  78. package/lib/stores/QueryEditorStore.d.ts.map +1 -1
  79. package/lib/stores/QueryEditorStore.js +35 -17
  80. package/lib/stores/QueryEditorStore.js.map +1 -1
  81. package/lib/stores/QueryEditorStoreTestUtils.d.ts.map +1 -1
  82. package/lib/stores/QueryEditorStoreTestUtils.js +3 -0
  83. package/lib/stores/QueryEditorStoreTestUtils.js.map +1 -1
  84. package/lib/stores/QueryProductionizerSetupStore.d.ts +32 -0
  85. package/lib/stores/QueryProductionizerSetupStore.d.ts.map +1 -0
  86. package/lib/stores/QueryProductionizerSetupStore.js +101 -0
  87. package/lib/stores/QueryProductionizerSetupStore.js.map +1 -0
  88. package/lib/stores/QuerySetupStore.d.ts +22 -85
  89. package/lib/stores/QuerySetupStore.d.ts.map +1 -1
  90. package/lib/stores/QuerySetupStore.js +78 -408
  91. package/lib/stores/QuerySetupStore.js.map +1 -1
  92. package/lib/stores/UpdateExistingServiceQuerySetupStore.d.ts +28 -0
  93. package/lib/stores/UpdateExistingServiceQuerySetupStore.d.ts.map +1 -0
  94. package/lib/stores/UpdateExistingServiceQuerySetupStore.js +73 -0
  95. package/lib/stores/UpdateExistingServiceQuerySetupStore.js.map +1 -0
  96. package/package.json +13 -16
  97. package/src/application/LegendQuery.tsx +7 -8
  98. package/src/application/LegendQueryApplicationConfig.ts +14 -0
  99. package/src/components/CloneQueryServiceSetup.tsx +312 -0
  100. package/src/components/Core_LegendQueryApplicationPlugin.tsx +184 -0
  101. package/src/components/CreateMappingQuerySetup.tsx +352 -0
  102. package/src/components/EditExistingQuerySetup.tsx +280 -0
  103. package/src/components/LegendQueryApplication.tsx +14 -2
  104. package/src/components/LoadProjectServiceQuerySetup.tsx +131 -0
  105. package/src/components/QueryEditor.tsx +127 -81
  106. package/src/components/QueryProductionizerSetup.tsx +206 -0
  107. package/src/components/QuerySetup.tsx +285 -1183
  108. package/src/components/UpdateExistingServiceQuerySetup.tsx +153 -0
  109. package/src/index.ts +3 -2
  110. package/src/stores/CloneServiceQuerySetupStore.ts +151 -0
  111. package/src/stores/CreateMappingQuerySetupStore.ts +155 -0
  112. package/src/stores/EditExistingQuerySetupStore.ts +111 -0
  113. package/src/stores/LegendQueryApplicationPlugin.ts +27 -27
  114. package/src/stores/LegendQueryRouter.ts +95 -12
  115. package/src/stores/LoadProjectServiceQuerySetupStore.ts +87 -0
  116. package/src/stores/QueryEditorStore.ts +90 -24
  117. package/src/stores/QueryEditorStoreTestUtils.ts +3 -0
  118. package/src/stores/QueryProductionizerSetupStore.ts +143 -0
  119. package/src/stores/QuerySetupStore.ts +111 -604
  120. package/src/stores/UpdateExistingServiceQuerySetupStore.ts +118 -0
  121. package/tsconfig.json +13 -1
  122. package/lib/components/QuerySetupStoreProvider.d.ts.map +0 -1
  123. package/lib/components/QuerySetupStoreProvider.js +0 -34
  124. package/lib/components/QuerySetupStoreProvider.js.map +0 -1
  125. package/src/components/QuerySetupStoreProvider.tsx +0 -56
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Copyright (c) 2020-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { ArrowLeftIcon, CustomSelectorInput } from '@finos/legend-art';
18
+ import { guaranteeType } from '@finos/legend-shared';
19
+ import { flowResult } from 'mobx';
20
+ import { observer, useLocalObservable } from 'mobx-react-lite';
21
+ import { useContext, useEffect } from 'react';
22
+ import { generateQuerySetupRoute } from '../stores/LegendQueryRouter.js';
23
+ import { useDepotServerClient } from '@finos/legend-server-depot';
24
+ import { useLegendQueryApplicationStore } from './LegendQueryBaseStoreProvider.js';
25
+ import { LoadProjectServiceQuerySetupStore } from '../stores/LoadProjectServiceQuerySetupStore.js';
26
+ import {
27
+ BaseQuerySetup,
28
+ BaseQuerySetupStoreContext,
29
+ buildProjectOption,
30
+ type ProjectOption,
31
+ } from './QuerySetup.js';
32
+ import {} from '@finos/legend-query-builder';
33
+ import { useApplicationStore } from '@finos/legend-application';
34
+
35
+ const LoadProjectServiceQuerySetupStoreProvider: React.FC<{
36
+ children: React.ReactNode;
37
+ }> = ({ children }) => {
38
+ const applicationStore = useLegendQueryApplicationStore();
39
+ const depotServerClient = useDepotServerClient();
40
+ const store = useLocalObservable(
41
+ () =>
42
+ new LoadProjectServiceQuerySetupStore(
43
+ applicationStore,
44
+ depotServerClient,
45
+ ),
46
+ );
47
+ return (
48
+ <BaseQuerySetupStoreContext.Provider value={store}>
49
+ {children}
50
+ </BaseQuerySetupStoreContext.Provider>
51
+ );
52
+ };
53
+
54
+ const useLoadProjectServiceQuerySetupStore =
55
+ (): LoadProjectServiceQuerySetupStore =>
56
+ guaranteeType(
57
+ useContext(BaseQuerySetupStoreContext),
58
+ LoadProjectServiceQuerySetupStore,
59
+ `Can't find query setup store in context`,
60
+ );
61
+
62
+ const LoadProjectServiceQuerySetupContent = observer(() => {
63
+ const applicationStore = useApplicationStore();
64
+ const setupStore = useLoadProjectServiceQuerySetupStore();
65
+
66
+ const back = (): void => {
67
+ applicationStore.navigator.goToLocation(generateQuerySetupRoute());
68
+ };
69
+
70
+ // project
71
+ const projectOptions = setupStore.projects.map(buildProjectOption);
72
+ const projectSelectorPlaceholder = setupStore.loadProjectsState.isInProgress
73
+ ? 'Loading projects'
74
+ : setupStore.loadProjectsState.hasFailed
75
+ ? 'Error fetching projects'
76
+ : setupStore.projects.length
77
+ ? 'Choose a project'
78
+ : 'You have no projects, please create or acquire access for at least one';
79
+ const onProjectOptionChange = (option: ProjectOption): void => {
80
+ setupStore
81
+ .loadProjectServiceUpdater(option.value)
82
+ .catch(applicationStore.alertUnhandledError);
83
+ };
84
+
85
+ useEffect(() => {
86
+ flowResult(setupStore.loadProjects()).catch(
87
+ applicationStore.alertUnhandledError,
88
+ );
89
+ }, [setupStore, applicationStore]);
90
+
91
+ return (
92
+ <div className="query-setup__wizard query-setup__existing-service-query">
93
+ <div className="query-setup__wizard__header query-setup__service-query__header">
94
+ <button
95
+ className="query-setup__wizard__header__btn"
96
+ onClick={back}
97
+ title="Back to Main Menu"
98
+ >
99
+ <ArrowLeftIcon />
100
+ </button>
101
+ <div className="query-setup__wizard__header__title">
102
+ Load service query from a project...
103
+ </div>
104
+ </div>
105
+ <div className="query-setup__wizard__content">
106
+ <div className="query-setup__wizard__group query-setup__wizard__group--inline query-setup__existing-service-query__search-bar">
107
+ <CustomSelectorInput
108
+ className="query-setup__wizard__selector"
109
+ options={projectOptions}
110
+ disabled={
111
+ setupStore.loadProjectsState.isInProgress ||
112
+ !projectOptions.length
113
+ }
114
+ isLoading={setupStore.loadProjectsState.isInProgress}
115
+ onChange={onProjectOptionChange}
116
+ placeholder={projectSelectorPlaceholder}
117
+ darkMode={true}
118
+ />
119
+ </div>
120
+ </div>
121
+ </div>
122
+ );
123
+ });
124
+
125
+ export const LoadProjectServiceQuerySetup: React.FC = () => (
126
+ <LoadProjectServiceQuerySetupStoreProvider>
127
+ <BaseQuerySetup>
128
+ <LoadProjectServiceQuerySetupContent />
129
+ </BaseQuerySetup>
130
+ </LoadProjectServiceQuerySetupStoreProvider>
131
+ );
@@ -17,10 +17,8 @@
17
17
  import {
18
18
  type SelectComponent,
19
19
  Dialog,
20
- ArrowLeftIcon,
21
20
  ExternalLinkSquareIcon,
22
21
  PanelLoadingIndicator,
23
- RobotIcon,
24
22
  SaveIcon,
25
23
  BlankPanelContent,
26
24
  clsx,
@@ -31,6 +29,15 @@ import {
31
29
  CheckSquareIcon,
32
30
  SquareIcon,
33
31
  ManageSearchIcon,
32
+ DropdownMenu,
33
+ MenuContent,
34
+ MenuContentItem,
35
+ CaretDownIcon,
36
+ MenuIcon,
37
+ MenuContentDivider,
38
+ MenuContentItemIcon,
39
+ CheckIcon,
40
+ MenuContentItemLabel,
34
41
  } from '@finos/legend-art';
35
42
  import { debounce, getQueryParameters } from '@finos/legend-shared';
36
43
  import { observer } from 'mobx-react-lite';
@@ -40,18 +47,17 @@ import {
40
47
  type ExistingQueryEditorPathParams,
41
48
  type ServiceQueryCreatorPathParams,
42
49
  type ServiceQueryCreatorQueryParams,
43
- LEGEND_QUERY_ROUTE_PATTERN,
44
50
  LEGEND_QUERY_QUERY_PARAM_TOKEN,
45
51
  LEGEND_QUERY_PATH_PARAM_TOKEN,
46
- EXTERNAL_APPLICATION_NAVIGATION__generateStudioProjectViewUrl,
47
52
  generateExistingQueryEditorRoute,
53
+ generateQuerySetupRoute,
48
54
  } from '../stores/LegendQueryRouter.js';
49
55
  import {
50
56
  type QueryEditorStore,
51
- MappingQueryCreatorStore,
52
57
  ExistingQueryEditorStore,
53
58
  QueryExportState,
54
- ServiceQueryCreatorStore,
59
+ createViewProjectHandler,
60
+ createViewSDLCProjectHandler,
55
61
  } from '../stores/QueryEditorStore.js';
56
62
  import { useApplicationStore, useParams } from '@finos/legend-application';
57
63
  import {
@@ -60,14 +66,12 @@ import {
60
66
  ServiceQueryCreatorStoreProvider,
61
67
  useQueryEditorStore,
62
68
  } from './QueryEditorStoreProvider.js';
63
- import {
64
- type RawLambda,
65
- extractElementNameFromPath,
66
- } from '@finos/legend-graph';
69
+ import type { RawLambda } from '@finos/legend-graph';
67
70
  import { flowResult } from 'mobx';
68
71
  import { useLegendQueryApplicationStore } from './LegendQueryBaseStoreProvider.js';
69
72
  import {
70
73
  QueryBuilder,
74
+ QueryBuilderNavigationBlocker,
71
75
  type QueryBuilderState,
72
76
  } from '@finos/legend-query-builder';
73
77
 
@@ -156,46 +160,6 @@ const QueryExport = observer(() => {
156
160
  );
157
161
  });
158
162
 
159
- const renderQueryEditorHeaderLabel = (
160
- editorStore: QueryEditorStore,
161
- ): React.ReactNode => {
162
- if (editorStore instanceof ExistingQueryEditorStore) {
163
- return (
164
- <div className="query-editor__header__label query-editor__header__label--existing-query">
165
- {editorStore.query.name}
166
- </div>
167
- );
168
- } else if (editorStore instanceof MappingQueryCreatorStore) {
169
- return (
170
- <div className="query-editor__header__label query-editor__header__label--create-query">
171
- New Query
172
- </div>
173
- );
174
- } else if (editorStore instanceof ServiceQueryCreatorStore) {
175
- return (
176
- <div className="query-editor__header__label query-editor__header__label--service-query">
177
- <RobotIcon className="query-editor__header__label__icon" />
178
- {extractElementNameFromPath(editorStore.servicePath)}
179
- {editorStore.executionKey && (
180
- <div className="query-editor__header__label__tag">
181
- {editorStore.executionKey}
182
- </div>
183
- )}
184
- </div>
185
- );
186
- }
187
- const extraQueryEditorHeaderLabelers = editorStore.pluginManager
188
- .getApplicationPlugins()
189
- .flatMap((plugin) => plugin.getExtraQueryEditorHeaderLabelers?.() ?? []);
190
- for (const labeler of extraQueryEditorHeaderLabelers) {
191
- const label = labeler(editorStore);
192
- if (label) {
193
- return label;
194
- }
195
- }
196
- return null;
197
- };
198
-
199
163
  const QueryLoader = observer(
200
164
  (props: {
201
165
  editorStore: QueryEditorStore;
@@ -253,8 +217,9 @@ const QueryLoader = observer(
253
217
  if (selectedQueryID) {
254
218
  queryBuilderState.changeDetectionState.alertUnsavedChanges(() => {
255
219
  editorStore.queryLoaderState.setIsQueryLoaderOpen(false);
256
- applicationStore.navigator.reloadToLocation(
220
+ applicationStore.navigator.goToLocation(
257
221
  generateExistingQueryEditorRoute(selectedQueryID),
222
+ { ignoreBlocking: true },
258
223
  );
259
224
  });
260
225
  }
@@ -418,16 +383,22 @@ const QueryEditorHeaderContent = observer(
418
383
  const openQueryLoader = (): void => {
419
384
  editorStore.queryLoaderState.setIsQueryLoaderOpen(true);
420
385
  };
421
- const viewQueryProject = (): void => {
386
+ const viewProject = (): void => {
422
387
  const { groupId, artifactId, versionId } = editorStore.getProjectInfo();
423
- applicationStore.navigator.visitAddress(
424
- EXTERNAL_APPLICATION_NAVIGATION__generateStudioProjectViewUrl(
425
- applicationStore.config.studioUrl,
426
- groupId,
427
- artifactId,
428
- versionId,
429
- undefined,
430
- ),
388
+ createViewProjectHandler(applicationStore)(
389
+ groupId,
390
+ artifactId,
391
+ versionId,
392
+ undefined,
393
+ );
394
+ };
395
+ const viewSDLCProject = (): void => {
396
+ const { groupId, artifactId } = editorStore.getProjectInfo();
397
+ createViewSDLCProjectHandler(
398
+ applicationStore,
399
+ editorStore.depotServerClient,
400
+ )(groupId, artifactId, undefined).catch(
401
+ applicationStore.alertUnhandledError,
431
402
  );
432
403
  };
433
404
  const toggleLightDarkMode = (): void =>
@@ -451,9 +422,7 @@ const QueryEditorHeaderContent = observer(
451
422
 
452
423
  return (
453
424
  <div className="query-editor__header__content">
454
- <div className="query-editor__header__content__main">
455
- {renderQueryEditorHeaderLabel(editorStore)}
456
- </div>
425
+ <div className="query-editor__header__content__main" />
457
426
  <div className="query-editor__header__actions">
458
427
  {editorStore instanceof ExistingQueryEditorStore &&
459
428
  applicationStore.pluginManager
@@ -482,14 +451,33 @@ const QueryEditorHeaderContent = observer(
482
451
  queryBuilderState={queryBuilderState}
483
452
  />
484
453
  )}
485
- <button
486
- className="query-editor__header__action btn--dark"
487
- tabIndex={-1}
488
- title="View project"
489
- onClick={viewQueryProject}
490
- >
491
- <ExternalLinkSquareIcon />
492
- </button>
454
+ <div className="query-editor__header__action query-editor__header__action__view-project">
455
+ <button
456
+ className="query-editor__header__action__view-project__btn btn--dark"
457
+ disabled={editorStore.isViewProjectActionDisabled}
458
+ tabIndex={-1}
459
+ title="View project"
460
+ onClick={viewProject}
461
+ >
462
+ <ExternalLinkSquareIcon />
463
+ </button>
464
+ <DropdownMenu
465
+ className="query-editor__header__action__view-project__dropdown-trigger btn--dark"
466
+ disabled={editorStore.isViewProjectActionDisabled}
467
+ content={
468
+ <MenuContent>
469
+ <MenuContentItem
470
+ disabled={editorStore.isViewProjectActionDisabled}
471
+ onClick={viewSDLCProject}
472
+ >
473
+ View SDLC project
474
+ </MenuContentItem>
475
+ </MenuContent>
476
+ }
477
+ >
478
+ <CaretDownIcon />
479
+ </DropdownMenu>
480
+ </div>
493
481
  {applicationStore.config.options.TEMPORARY__enableThemeSwitcher && (
494
482
  <button
495
483
  className="query-editor__header__action btn--dark"
@@ -507,6 +495,7 @@ const QueryEditorHeaderContent = observer(
507
495
  <button
508
496
  className="query-editor__header__action btn--dark"
509
497
  tabIndex={-1}
498
+ disabled={editorStore.isSaveActionDisabled}
510
499
  onClick={saveQuery}
511
500
  title="Save query"
512
501
  >
@@ -523,8 +512,27 @@ export const QueryEditor = observer(() => {
523
512
  const applicationStore = useApplicationStore();
524
513
  const editorStore = useQueryEditorStore();
525
514
  const isLoadingEditor = !editorStore.initState.hasCompleted;
526
- const backToMainMenu = (): void =>
527
- applicationStore.navigator.goToLocation(LEGEND_QUERY_ROUTE_PATTERN.SETUP);
515
+
516
+ // documentation
517
+ const appDocUrl = applicationStore.documentationService.url;
518
+ const goToDocumentation = (): void => {
519
+ if (appDocUrl) {
520
+ applicationStore.navigator.visitAddress(appDocUrl);
521
+ }
522
+ };
523
+ // go to setup page
524
+ const goToQuerySetup = (): void =>
525
+ applicationStore.navigator.visitAddress(
526
+ applicationStore.navigator.generateAddress(generateQuerySetupRoute()),
527
+ );
528
+ // settings
529
+ // NOTE: this is temporary until we find a better home for these settings in query builder
530
+ const engineConfig =
531
+ editorStore.graphManagerState.graphManager.TEMPORARY__getEngineConfig();
532
+ const toggleEngineClientRequestPayloadCompression = (): void =>
533
+ engineConfig.setUseClientRequestPayloadCompression(
534
+ !engineConfig.useClientRequestPayloadCompression,
535
+ );
528
536
 
529
537
  useEffect(() => {
530
538
  flowResult(editorStore.initialize()).catch(
@@ -550,13 +558,46 @@ export const QueryEditor = observer(() => {
550
558
  ])}
551
559
  >
552
560
  <div className="query-editor__header">
553
- <button
554
- className="query-editor__header__back-btn btn--dark"
555
- onClick={backToMainMenu}
556
- title="Back to Main Menu"
557
- >
558
- <ArrowLeftIcon />
559
- </button>
561
+ <div className="query-editor__header__menu">
562
+ <DropdownMenu
563
+ className="query-editor__header__menu-item"
564
+ menuProps={{
565
+ anchorOrigin: { vertical: 'top', horizontal: 'right' },
566
+ transformOrigin: { vertical: 'top', horizontal: 'left' },
567
+ elevation: 7,
568
+ }}
569
+ content={
570
+ <MenuContent>
571
+ {/* <MenuContentItem onClick={openHelp}>Help...</MenuContentItem> */}
572
+ <MenuContentItem
573
+ disabled={!appDocUrl}
574
+ onClick={goToDocumentation}
575
+ >
576
+ See Documentation
577
+ </MenuContentItem>
578
+ <MenuContentItem onClick={goToQuerySetup}>
579
+ Back to query setup
580
+ </MenuContentItem>
581
+ <MenuContentDivider />
582
+ <MenuContentItem disabled={true}>Settings</MenuContentItem>
583
+ <MenuContentItem
584
+ onClick={toggleEngineClientRequestPayloadCompression}
585
+ >
586
+ <MenuContentItemIcon>
587
+ {engineConfig.useClientRequestPayloadCompression ? (
588
+ <CheckIcon />
589
+ ) : null}
590
+ </MenuContentItemIcon>
591
+ <MenuContentItemLabel>
592
+ Compress request payload
593
+ </MenuContentItemLabel>
594
+ </MenuContentItem>
595
+ </MenuContent>
596
+ }
597
+ >
598
+ <MenuIcon />
599
+ </DropdownMenu>
600
+ </div>
560
601
  {!isLoadingEditor && editorStore.queryBuilderState && (
561
602
  <QueryEditorHeaderContent
562
603
  queryBuilderState={editorStore.queryBuilderState}
@@ -566,7 +607,12 @@ export const QueryEditor = observer(() => {
566
607
  <div className="query-editor__content">
567
608
  <PanelLoadingIndicator isLoading={isLoadingEditor} />
568
609
  {!isLoadingEditor && editorStore.queryBuilderState && (
569
- <QueryBuilder queryBuilderState={editorStore.queryBuilderState} />
610
+ <>
611
+ <QueryBuilderNavigationBlocker
612
+ queryBuilderState={editorStore.queryBuilderState}
613
+ />
614
+ <QueryBuilder queryBuilderState={editorStore.queryBuilderState} />
615
+ </>
570
616
  )}
571
617
  {(isLoadingEditor || !editorStore.queryBuilderState) && (
572
618
  <BlankPanelContent>
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Copyright (c) 2020-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import {
18
+ ArrowLeftIcon,
19
+ ArrowRightIcon,
20
+ BlankPanelContent,
21
+ clsx,
22
+ CustomSelectorInput,
23
+ PanelLoadingIndicator,
24
+ SearchIcon,
25
+ type SelectComponent,
26
+ } from '@finos/legend-art';
27
+ import { debounce, guaranteeType } from '@finos/legend-shared';
28
+ import { flowResult } from 'mobx';
29
+ import { observer, useLocalObservable } from 'mobx-react-lite';
30
+ import { useContext, useEffect, useMemo, useRef, useState } from 'react';
31
+ import { useDepotServerClient } from '@finos/legend-server-depot';
32
+ import {
33
+ EDITOR_LANGUAGE,
34
+ TextInputEditor,
35
+ useApplicationStore,
36
+ } from '@finos/legend-application';
37
+ import { useLegendQueryApplicationStore } from './LegendQueryBaseStoreProvider.js';
38
+ import { QueryProductionizerSetupStore } from '../stores/QueryProductionizerSetupStore.js';
39
+ import { BaseQuerySetup, BaseQuerySetupStoreContext } from './QuerySetup.js';
40
+ import {
41
+ buildQueryOption,
42
+ type QueryOption,
43
+ } from '@finos/legend-query-builder';
44
+ import { generateQuerySetupRoute } from '../stores/LegendQueryRouter.js';
45
+
46
+ const QueryProductionizerSetupStoreProvider: React.FC<{
47
+ children: React.ReactNode;
48
+ }> = ({ children }) => {
49
+ const applicationStore = useLegendQueryApplicationStore();
50
+ const depotServerClient = useDepotServerClient();
51
+ const store = useLocalObservable(
52
+ () =>
53
+ new QueryProductionizerSetupStore(applicationStore, depotServerClient),
54
+ );
55
+ return (
56
+ <BaseQuerySetupStoreContext.Provider value={store}>
57
+ {children}
58
+ </BaseQuerySetupStoreContext.Provider>
59
+ );
60
+ };
61
+
62
+ const useQueryProductionizerSetupStore = (): QueryProductionizerSetupStore =>
63
+ guaranteeType(
64
+ useContext(BaseQuerySetupStoreContext),
65
+ QueryProductionizerSetupStore,
66
+ `Can't find query setup store in context`,
67
+ );
68
+
69
+ const QueryProductionizerSetupContent = observer(() => {
70
+ const applicationStore = useApplicationStore();
71
+ const setupStore = useQueryProductionizerSetupStore();
72
+ const querySearchRef = useRef<SelectComponent>(null);
73
+ const [searchText, setSearchText] = useState('');
74
+
75
+ // actions
76
+ const back = (): void => {
77
+ applicationStore.navigator.goToLocation(generateQuerySetupRoute());
78
+ };
79
+ const next = (): void => {
80
+ if (setupStore.currentQuery) {
81
+ setupStore
82
+ .loadQueryProductionizer()
83
+ .catch(applicationStore.alertUnhandledError);
84
+ }
85
+ };
86
+ const canProceed = setupStore.currentQuery;
87
+
88
+ // query
89
+ const queryOptions = setupStore.queries.map(buildQueryOption);
90
+ const selectedQueryOption = setupStore.currentQuery
91
+ ? buildQueryOption(setupStore.currentQuery)
92
+ : null;
93
+ const onQueryOptionChange = (option: QueryOption | null): void => {
94
+ if (option?.value !== setupStore.currentQuery) {
95
+ setupStore.setCurrentQuery(option?.value.id);
96
+ }
97
+ };
98
+
99
+ // search text
100
+ const debouncedLoadQueries = useMemo(
101
+ () =>
102
+ debounce((input: string): void => {
103
+ flowResult(setupStore.loadQueries(input)).catch(
104
+ applicationStore.alertUnhandledError,
105
+ );
106
+ }, 500),
107
+ [applicationStore, setupStore],
108
+ );
109
+ const onSearchTextChange = (value: string): void => {
110
+ if (value !== searchText) {
111
+ setSearchText(value);
112
+ debouncedLoadQueries.cancel();
113
+ debouncedLoadQueries(value);
114
+ }
115
+ };
116
+
117
+ useEffect(() => {
118
+ flowResult(setupStore.loadQueries('')).catch(
119
+ applicationStore.alertUnhandledError,
120
+ );
121
+ }, [setupStore, applicationStore]);
122
+
123
+ useEffect(() => {
124
+ querySearchRef.current?.focus();
125
+ }, []);
126
+
127
+ return (
128
+ <div className="query-setup__wizard query-setup__productionize-query">
129
+ <div className="query-setup__wizard__header query-setup__productionize-query__header">
130
+ <button
131
+ className="query-setup__wizard__header__btn"
132
+ onClick={back}
133
+ title="Back to Main Menu"
134
+ >
135
+ <ArrowLeftIcon />
136
+ </button>
137
+ <div className="query-setup__wizard__header__title">
138
+ Productionizing an existing query...
139
+ </div>
140
+ <button
141
+ className={clsx('query-setup__wizard__header__btn', {
142
+ 'query-setup__wizard__header__btn--ready': canProceed,
143
+ })}
144
+ onClick={next}
145
+ disabled={!canProceed}
146
+ title="Productionize query"
147
+ >
148
+ <ArrowRightIcon />
149
+ </button>
150
+ </div>
151
+ <div className="query-setup__wizard__content">
152
+ <div className="query-setup__wizard__group query-setup__wizard__group--inline">
153
+ <div className="query-setup__wizard__group__title">
154
+ <SearchIcon />
155
+ </div>
156
+ <CustomSelectorInput
157
+ ref={querySearchRef}
158
+ className="query-setup__wizard__selector"
159
+ options={queryOptions}
160
+ isLoading={setupStore.loadQueriesState.isInProgress}
161
+ onInputChange={onSearchTextChange}
162
+ inputValue={searchText}
163
+ onChange={onQueryOptionChange}
164
+ value={selectedQueryOption}
165
+ placeholder="Search for query by name..."
166
+ isClearable={true}
167
+ escapeClearsValue={true}
168
+ darkMode={true}
169
+ />
170
+ </div>
171
+ <div className="query-setup__productionize-query__preview">
172
+ <PanelLoadingIndicator
173
+ isLoading={setupStore.loadQueryState.isInProgress}
174
+ />
175
+ {setupStore.currentQuery && (
176
+ <>
177
+ {!setupStore.currentQueryInfo && (
178
+ <BlankPanelContent>{`Can't preview query`}</BlankPanelContent>
179
+ )}
180
+ {setupStore.currentQueryInfo && (
181
+ <TextInputEditor
182
+ inputValue={setupStore.currentQueryInfo.content}
183
+ isReadOnly={true}
184
+ language={EDITOR_LANGUAGE.PURE}
185
+ showMiniMap={false}
186
+ hideGutter={true}
187
+ />
188
+ )}
189
+ </>
190
+ )}
191
+ {!setupStore.currentQuery && (
192
+ <BlankPanelContent>No query to preview</BlankPanelContent>
193
+ )}
194
+ </div>
195
+ </div>
196
+ </div>
197
+ );
198
+ });
199
+
200
+ export const QueryProductionizerSetup: React.FC = () => (
201
+ <QueryProductionizerSetupStoreProvider>
202
+ <BaseQuerySetup>
203
+ <QueryProductionizerSetupContent />
204
+ </BaseQuerySetup>
205
+ </QueryProductionizerSetupStoreProvider>
206
+ );