@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,352 @@
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
+ } from '@finos/legend-art';
25
+ import { getNullableFirstElement, guaranteeType } from '@finos/legend-shared';
26
+ import { flowResult } from 'mobx';
27
+ import { observer, useLocalObservable } from 'mobx-react-lite';
28
+ import { useContext, useEffect } from 'react';
29
+ import {
30
+ generateMappingQueryCreatorRoute,
31
+ generateQuerySetupRoute,
32
+ } from '../stores/LegendQueryRouter.js';
33
+ import {
34
+ LATEST_VERSION_ALIAS,
35
+ SNAPSHOT_VERSION_ALIAS,
36
+ useDepotServerClient,
37
+ } from '@finos/legend-server-depot';
38
+ import {
39
+ useApplicationStore,
40
+ buildElementOption,
41
+ type PackageableElementOption,
42
+ } from '@finos/legend-application';
43
+ import { useLegendQueryApplicationStore } from './LegendQueryBaseStoreProvider.js';
44
+ import { CreateMappingQuerySetupStore } from '../stores/CreateMappingQuerySetupStore.js';
45
+ import {
46
+ BaseQuerySetup,
47
+ BaseQuerySetupStoreContext,
48
+ buildProjectOption,
49
+ buildVersionOption,
50
+ type ProjectOption,
51
+ type VersionOption,
52
+ } from './QuerySetup.js';
53
+ import { compareSemVerVersions } from '@finos/legend-storage';
54
+ import type { Mapping, PackageableRuntime } from '@finos/legend-graph';
55
+
56
+ const CreateMappingQuerySetupStoreProvider: React.FC<{
57
+ children: React.ReactNode;
58
+ }> = ({ children }) => {
59
+ const applicationStore = useLegendQueryApplicationStore();
60
+ const depotServerClient = useDepotServerClient();
61
+ const store = useLocalObservable(
62
+ () => new CreateMappingQuerySetupStore(applicationStore, depotServerClient),
63
+ );
64
+ return (
65
+ <BaseQuerySetupStoreContext.Provider value={store}>
66
+ {children}
67
+ </BaseQuerySetupStoreContext.Provider>
68
+ );
69
+ };
70
+
71
+ const useCreateMappingQuerySetupStore = (): CreateMappingQuerySetupStore =>
72
+ guaranteeType(
73
+ useContext(BaseQuerySetupStoreContext),
74
+ CreateMappingQuerySetupStore,
75
+ `Can't find query setup store in context`,
76
+ );
77
+
78
+ const CreateMappingQuerySetupContent = observer(() => {
79
+ const setupStore = useCreateMappingQuerySetupStore();
80
+ const applicationStore = useApplicationStore();
81
+
82
+ // actions
83
+ const back = (): void => {
84
+ applicationStore.navigator.goToLocation(generateQuerySetupRoute());
85
+ };
86
+ const next = (): void => {
87
+ if (
88
+ setupStore.currentProject &&
89
+ setupStore.currentVersionId &&
90
+ setupStore.currentMapping &&
91
+ setupStore.currentRuntime
92
+ ) {
93
+ applicationStore.navigator.goToLocation(
94
+ generateMappingQueryCreatorRoute(
95
+ setupStore.currentProject.groupId,
96
+ setupStore.currentProject.artifactId,
97
+ setupStore.currentVersionId,
98
+ setupStore.currentMapping.path,
99
+ setupStore.currentRuntime.path,
100
+ ),
101
+ );
102
+ }
103
+ };
104
+ const canProceed =
105
+ setupStore.currentProject &&
106
+ setupStore.currentVersionId &&
107
+ setupStore.currentMapping &&
108
+ setupStore.currentRuntime;
109
+
110
+ // project
111
+ const projectOptions = setupStore.projects.map(buildProjectOption);
112
+ const selectedProjectOption = setupStore.currentProject
113
+ ? buildProjectOption(setupStore.currentProject)
114
+ : null;
115
+ const projectSelectorPlaceholder = setupStore.loadProjectsState.isInProgress
116
+ ? 'Loading projects'
117
+ : setupStore.loadProjectsState.hasFailed
118
+ ? 'Error fetching projects'
119
+ : setupStore.projects.length
120
+ ? 'Choose a project'
121
+ : 'You have no projects, please create or acquire access for at least one';
122
+ const onProjectOptionChange = (option: ProjectOption | null): void => {
123
+ if (option?.value !== setupStore.currentProject) {
124
+ setupStore.setCurrentProject(option?.value);
125
+ // cascade
126
+ setupStore.setCurrentVersionId(undefined);
127
+ setupStore.setCurrentMapping(undefined);
128
+ setupStore.setCurrentRuntime(undefined);
129
+ }
130
+ };
131
+
132
+ // version
133
+ const versionOptions = [
134
+ LATEST_VERSION_ALIAS,
135
+ SNAPSHOT_VERSION_ALIAS,
136
+ ...(setupStore.currentProject?.versions ?? []),
137
+ ]
138
+ .slice()
139
+ .sort((v1, v2) => compareSemVerVersions(v2, v1))
140
+ .map(buildVersionOption);
141
+ const selectedVersionOption = setupStore.currentVersionId
142
+ ? buildVersionOption(setupStore.currentVersionId)
143
+ : null;
144
+ const versionSelectorPlaceholder = !setupStore.currentProject
145
+ ? 'No project selected'
146
+ : 'Choose a version';
147
+ const onVersionOptionChange = async (
148
+ option: VersionOption | null,
149
+ ): Promise<void> => {
150
+ if (option?.value !== setupStore.currentVersionId) {
151
+ setupStore.setCurrentVersionId(option?.value);
152
+ // cascade
153
+ setupStore.setCurrentMapping(undefined);
154
+ setupStore.setCurrentRuntime(undefined);
155
+ if (setupStore.currentProject && setupStore.currentVersionId) {
156
+ await flowResult(
157
+ setupStore.surveyMappingRuntimeCompatibility(
158
+ setupStore.currentProject,
159
+ setupStore.currentVersionId,
160
+ ),
161
+ ).catch(applicationStore.alertUnhandledError);
162
+ }
163
+ }
164
+ };
165
+
166
+ // mapping
167
+ const mappingOptions = setupStore.mappingRuntimeCompatibilitySurveyResult.map(
168
+ (result) => buildElementOption(result.mapping),
169
+ );
170
+ const selectedMappingOption = setupStore.currentMapping
171
+ ? {
172
+ label: setupStore.currentMapping.name,
173
+ value: setupStore.currentMapping,
174
+ }
175
+ : null;
176
+ const mappingSelectorPlaceholder = mappingOptions.length
177
+ ? 'Choose a mapping'
178
+ : 'No mapping available';
179
+ const onMappingOptionChange = (
180
+ option: PackageableElementOption<Mapping> | null,
181
+ ): void => {
182
+ setupStore.setCurrentMapping(option?.value);
183
+ // cascade
184
+ if (setupStore.currentMapping) {
185
+ setupStore.setCurrentRuntime(
186
+ getNullableFirstElement(setupStore.compatibleRuntimes),
187
+ );
188
+ } else {
189
+ setupStore.setCurrentRuntime(undefined);
190
+ }
191
+ };
192
+
193
+ // runtime
194
+ const runtimeOptions = setupStore.compatibleRuntimes.map(buildElementOption);
195
+ const selectedRuntimeOption = setupStore.currentRuntime
196
+ ? {
197
+ label: setupStore.currentRuntime.name,
198
+ value: setupStore.currentRuntime,
199
+ }
200
+ : null;
201
+ const runtimeSelectorPlaceholder = !setupStore.currentMapping
202
+ ? 'No mapping specified'
203
+ : runtimeOptions.length
204
+ ? 'Choose a runtime'
205
+ : 'No runtime available';
206
+ const onRuntimeOptionChange = (
207
+ option: PackageableElementOption<PackageableRuntime> | null,
208
+ ): void => {
209
+ setupStore.setCurrentRuntime(option?.value);
210
+ };
211
+
212
+ useEffect(() => {
213
+ flowResult(setupStore.loadProjects()).catch(
214
+ applicationStore.alertUnhandledError,
215
+ );
216
+ }, [setupStore, applicationStore]);
217
+
218
+ return (
219
+ <div className="query-setup__wizard query-setup__create-query">
220
+ <div className="query-setup__wizard__header query-setup__create-query__header">
221
+ <button
222
+ className="query-setup__wizard__header__btn"
223
+ onClick={back}
224
+ title="Back to Main Menu"
225
+ >
226
+ <ArrowLeftIcon />
227
+ </button>
228
+ <div className="query-setup__wizard__header__title">
229
+ Creating a new query...
230
+ </div>
231
+ <button
232
+ className={clsx('query-setup__wizard__header__btn', {
233
+ 'query-setup__wizard__header__btn--ready': canProceed,
234
+ })}
235
+ onClick={next}
236
+ disabled={!canProceed}
237
+ title="Create a new query"
238
+ >
239
+ <ArrowRightIcon />
240
+ </button>
241
+ </div>
242
+ <div className="query-setup__wizard__content">
243
+ <div className="query-setup__create-query__project">
244
+ <div className="query-setup__wizard__group">
245
+ <div className="query-setup__wizard__group__title">Project</div>
246
+ <CustomSelectorInput
247
+ className="query-setup__wizard__selector"
248
+ options={projectOptions}
249
+ disabled={
250
+ setupStore.loadProjectsState.isInProgress ||
251
+ !projectOptions.length
252
+ }
253
+ isLoading={setupStore.loadProjectsState.isInProgress}
254
+ onChange={onProjectOptionChange}
255
+ value={selectedProjectOption}
256
+ placeholder={projectSelectorPlaceholder}
257
+ isClearable={true}
258
+ escapeClearsValue={true}
259
+ darkMode={true}
260
+ />
261
+ </div>
262
+ <div className="query-setup__wizard__group">
263
+ <div className="query-setup__wizard__group__title">Version</div>
264
+ <CustomSelectorInput
265
+ className="query-setup__wizard__selector"
266
+ options={versionOptions}
267
+ disabled={!setupStore.currentProject}
268
+ onChange={onVersionOptionChange}
269
+ value={selectedVersionOption}
270
+ placeholder={versionSelectorPlaceholder}
271
+ isClearable={true}
272
+ escapeClearsValue={true}
273
+ darkMode={true}
274
+ />
275
+ </div>
276
+ </div>
277
+ <div className="query-setup__create-query__graph">
278
+ {(!setupStore.currentProject ||
279
+ !setupStore.currentVersionId ||
280
+ !setupStore.surveyMappingRuntimeCompatibilityState
281
+ .hasSucceeded) && (
282
+ <div className="query-setup__create-query__graph__loader">
283
+ <PanelLoadingIndicator
284
+ isLoading={
285
+ Boolean(setupStore.currentProject) &&
286
+ Boolean(setupStore.currentVersionId) &&
287
+ !setupStore.surveyMappingRuntimeCompatibilityState
288
+ .hasSucceeded
289
+ }
290
+ />
291
+ <BlankPanelContent>
292
+ {setupStore.surveyMappingRuntimeCompatibilityState.isInProgress
293
+ ? `Surveying runtime and mapping compatibility...`
294
+ : setupStore.surveyMappingRuntimeCompatibilityState.hasFailed
295
+ ? `Can't load runtime and mapping`
296
+ : 'Project and version must be specified'}
297
+ </BlankPanelContent>
298
+ </div>
299
+ )}
300
+ {setupStore.currentProject &&
301
+ setupStore.currentVersionId &&
302
+ setupStore.surveyMappingRuntimeCompatibilityState.hasSucceeded && (
303
+ <>
304
+ <div className="query-setup__wizard__group">
305
+ <div className="query-setup__wizard__group__title">
306
+ Mapping
307
+ </div>
308
+ <CustomSelectorInput
309
+ className="query-setup__wizard__selector"
310
+ options={mappingOptions}
311
+ disabled={!mappingOptions.length}
312
+ onChange={onMappingOptionChange}
313
+ value={selectedMappingOption}
314
+ placeholder={mappingSelectorPlaceholder}
315
+ isClearable={true}
316
+ escapeClearsValue={true}
317
+ darkMode={true}
318
+ />
319
+ </div>
320
+ <div className="query-setup__wizard__group">
321
+ <div className="query-setup__wizard__group__title">
322
+ Runtime
323
+ </div>
324
+ <CustomSelectorInput
325
+ className="query-setup__wizard__selector"
326
+ options={runtimeOptions}
327
+ disabled={
328
+ !mappingOptions.length || !setupStore.currentMapping
329
+ }
330
+ onChange={onRuntimeOptionChange}
331
+ value={selectedRuntimeOption}
332
+ placeholder={runtimeSelectorPlaceholder}
333
+ isClearable={true}
334
+ escapeClearsValue={true}
335
+ darkMode={true}
336
+ />
337
+ </div>
338
+ </>
339
+ )}
340
+ </div>
341
+ </div>
342
+ </div>
343
+ );
344
+ });
345
+
346
+ export const CreateMappingQuerySetup: React.FC = () => (
347
+ <CreateMappingQuerySetupStoreProvider>
348
+ <BaseQuerySetup>
349
+ <CreateMappingQuerySetupContent />
350
+ </BaseQuerySetup>
351
+ </CreateMappingQuerySetupStoreProvider>
352
+ );
@@ -0,0 +1,280 @@
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
+ type SelectComponent,
19
+ ArrowLeftIcon,
20
+ ArrowRightIcon,
21
+ BlankPanelContent,
22
+ clsx,
23
+ CustomSelectorInput,
24
+ PanelLoadingIndicator,
25
+ SearchIcon,
26
+ UserIcon,
27
+ } from '@finos/legend-art';
28
+ import { debounce, guaranteeType } from '@finos/legend-shared';
29
+ import { flowResult } from 'mobx';
30
+ import { observer, useLocalObservable } from 'mobx-react-lite';
31
+ import { useContext, useEffect, useMemo, useRef, useState } from 'react';
32
+ import {
33
+ generateExistingQueryEditorRoute,
34
+ generateQuerySetupRoute,
35
+ } from '../stores/LegendQueryRouter.js';
36
+ import { useDepotServerClient } from '@finos/legend-server-depot';
37
+ import {
38
+ useApplicationStore,
39
+ EDITOR_LANGUAGE,
40
+ TextInputEditor,
41
+ } from '@finos/legend-application';
42
+ import {
43
+ type QueryOption,
44
+ buildQueryOption,
45
+ } from '@finos/legend-query-builder';
46
+ import { useLegendQueryApplicationStore } from './LegendQueryBaseStoreProvider.js';
47
+ import { EditExistingQuerySetupStore } from '../stores/EditExistingQuerySetupStore.js';
48
+ import { BaseQuerySetup, BaseQuerySetupStoreContext } from './QuerySetup.js';
49
+
50
+ const EditExistingQuerySetupStoreProvider: React.FC<{
51
+ children: React.ReactNode;
52
+ }> = ({ children }) => {
53
+ const applicationStore = useLegendQueryApplicationStore();
54
+ const depotServerClient = useDepotServerClient();
55
+ const store = useLocalObservable(
56
+ () => new EditExistingQuerySetupStore(applicationStore, depotServerClient),
57
+ );
58
+ return (
59
+ <BaseQuerySetupStoreContext.Provider value={store}>
60
+ {children}
61
+ </BaseQuerySetupStoreContext.Provider>
62
+ );
63
+ };
64
+
65
+ const useEditExistingQuerySetupStore = (): EditExistingQuerySetupStore =>
66
+ guaranteeType(
67
+ useContext(BaseQuerySetupStoreContext),
68
+ EditExistingQuerySetupStore,
69
+ `Can't find query setup store in context`,
70
+ );
71
+
72
+ const EditExistingQuerySetupContent = observer(() => {
73
+ const setupStore = useEditExistingQuerySetupStore();
74
+ const applicationStore = useApplicationStore();
75
+ const querySearchRef = useRef<SelectComponent>(null);
76
+ const [searchText, setSearchText] = useState('');
77
+
78
+ // actions
79
+ const back = (): void => {
80
+ applicationStore.navigator.goToLocation(generateQuerySetupRoute());
81
+ };
82
+ const next = (): void => {
83
+ if (setupStore.currentQuery) {
84
+ applicationStore.navigator.goToLocation(
85
+ generateExistingQueryEditorRoute(setupStore.currentQuery.id),
86
+ );
87
+ }
88
+ };
89
+ const canProceed = setupStore.currentQuery;
90
+
91
+ // query
92
+ const queryOptions = setupStore.queries.map(buildQueryOption);
93
+ const selectedQueryOption = setupStore.currentQuery
94
+ ? buildQueryOption(setupStore.currentQuery)
95
+ : null;
96
+ const onQueryOptionChange = (option: QueryOption | null): void => {
97
+ if (option?.value !== setupStore.currentQuery) {
98
+ setupStore.setCurrentQuery(option?.value.id);
99
+ }
100
+ };
101
+ const formatQueryOptionLabel = (option: QueryOption): React.ReactNode => {
102
+ const deleteQuery: React.MouseEventHandler<HTMLButtonElement> = (event) => {
103
+ event.preventDefault();
104
+ event.stopPropagation();
105
+ setupStore.graphManagerState.graphManager
106
+ .deleteQuery(option.value.id)
107
+ .then(() =>
108
+ flowResult(setupStore.loadQueries('')).catch(
109
+ applicationStore.alertUnhandledError,
110
+ ),
111
+ )
112
+ .catch(applicationStore.alertUnhandledError);
113
+ };
114
+ if (option.value.id === setupStore.currentQuery?.id) {
115
+ return option.label;
116
+ }
117
+ return (
118
+ <div className="query-setup__existing-query__query-option">
119
+ <div
120
+ className="query-setup__existing-query__query-option__label"
121
+ title={option.label}
122
+ >
123
+ {option.label}
124
+ </div>
125
+ {setupStore.showCurrentUserQueriesOnly && (
126
+ <button
127
+ className="query-setup__existing-query__query-option__action"
128
+ tabIndex={-1}
129
+ onClick={deleteQuery}
130
+ >
131
+ Delete
132
+ </button>
133
+ )}
134
+ {!setupStore.showCurrentUserQueriesOnly && Boolean(option.value.owner) && (
135
+ <div
136
+ className={clsx('query-setup__existing-query__query-option__user', {
137
+ 'query-setup__existing-query__query-option__user--mine':
138
+ option.value.isCurrentUserQuery,
139
+ })}
140
+ >
141
+ {option.value.isCurrentUserQuery ? 'mine' : option.value.owner}
142
+ </div>
143
+ )}
144
+ </div>
145
+ );
146
+ };
147
+
148
+ // search text
149
+ const debouncedLoadQueries = useMemo(
150
+ () =>
151
+ debounce((input: string): void => {
152
+ flowResult(setupStore.loadQueries(input)).catch(
153
+ applicationStore.alertUnhandledError,
154
+ );
155
+ }, 500),
156
+ [applicationStore, setupStore],
157
+ );
158
+ const onSearchTextChange = (value: string): void => {
159
+ if (value !== searchText) {
160
+ setSearchText(value);
161
+ debouncedLoadQueries.cancel();
162
+ debouncedLoadQueries(value);
163
+ }
164
+ };
165
+
166
+ // show current user queries only
167
+ const toggleShowCurrentUserQueriesOnly = (): void => {
168
+ setupStore.setShowCurrentUserQueriesOnly(
169
+ !setupStore.showCurrentUserQueriesOnly,
170
+ );
171
+ debouncedLoadQueries.cancel();
172
+ debouncedLoadQueries(searchText);
173
+ };
174
+
175
+ useEffect(() => {
176
+ flowResult(setupStore.loadQueries('')).catch(
177
+ applicationStore.alertUnhandledError,
178
+ );
179
+ }, [setupStore, applicationStore]);
180
+
181
+ useEffect(() => {
182
+ querySearchRef.current?.focus();
183
+ }, []);
184
+
185
+ return (
186
+ <div className="query-setup__wizard query-setup__existing-query">
187
+ <div className="query-setup__wizard__header query-setup__existing-query__header">
188
+ <button
189
+ className="query-setup__wizard__header__btn"
190
+ onClick={back}
191
+ title="Back to Main Menu"
192
+ >
193
+ <ArrowLeftIcon />
194
+ </button>
195
+ <div className="query-setup__wizard__header__title">
196
+ Loading an existing query...
197
+ </div>
198
+ <button
199
+ className={clsx('query-setup__wizard__header__btn', {
200
+ 'query-setup__wizard__header__btn--ready': canProceed,
201
+ })}
202
+ onClick={next}
203
+ disabled={!canProceed}
204
+ title="Edit query"
205
+ >
206
+ <ArrowRightIcon />
207
+ </button>
208
+ </div>
209
+ <div className="query-setup__wizard__content">
210
+ <div className="query-setup__wizard__group query-setup__wizard__group--inline">
211
+ <div className="query-setup__wizard__group__title">
212
+ <SearchIcon />
213
+ </div>
214
+ <div className="query-setup__existing-query__input">
215
+ <CustomSelectorInput
216
+ ref={querySearchRef}
217
+ className="query-setup__wizard__selector"
218
+ options={queryOptions}
219
+ isLoading={setupStore.loadQueriesState.isInProgress}
220
+ onInputChange={onSearchTextChange}
221
+ inputValue={searchText}
222
+ onChange={onQueryOptionChange}
223
+ value={selectedQueryOption}
224
+ placeholder="Search for query by name..."
225
+ isClearable={true}
226
+ escapeClearsValue={true}
227
+ darkMode={true}
228
+ formatOptionLabel={formatQueryOptionLabel}
229
+ />
230
+ <button
231
+ className={clsx('query-setup__existing-query__btn', {
232
+ 'query-setup__existing-query__btn--active':
233
+ setupStore.showCurrentUserQueriesOnly,
234
+ })}
235
+ tabIndex={-1}
236
+ title={`[${
237
+ setupStore.showCurrentUserQueriesOnly ? 'on' : 'off'
238
+ }] Toggle show only queries of current user`}
239
+ onClick={toggleShowCurrentUserQueriesOnly}
240
+ >
241
+ <UserIcon />
242
+ </button>
243
+ </div>
244
+ </div>
245
+ <div className="query-setup__existing-query__preview">
246
+ <PanelLoadingIndicator
247
+ isLoading={setupStore.loadQueryState.isInProgress}
248
+ />
249
+ {setupStore.currentQuery && (
250
+ <>
251
+ {!setupStore.currentQueryInfo && (
252
+ <BlankPanelContent>{`Can't preview query`}</BlankPanelContent>
253
+ )}
254
+ {setupStore.currentQueryInfo && (
255
+ <TextInputEditor
256
+ inputValue={setupStore.currentQueryInfo.content}
257
+ isReadOnly={true}
258
+ language={EDITOR_LANGUAGE.PURE}
259
+ showMiniMap={false}
260
+ hideGutter={true}
261
+ />
262
+ )}
263
+ </>
264
+ )}
265
+ {!setupStore.currentQuery && (
266
+ <BlankPanelContent>No query to preview</BlankPanelContent>
267
+ )}
268
+ </div>
269
+ </div>
270
+ </div>
271
+ );
272
+ });
273
+
274
+ export const EditExistingQuerySetup: React.FC = () => (
275
+ <EditExistingQuerySetupStoreProvider>
276
+ <BaseQuerySetup>
277
+ <EditExistingQuerySetupContent />
278
+ </BaseQuerySetup>
279
+ </EditExistingQuerySetupStoreProvider>
280
+ );
@@ -16,7 +16,7 @@
16
16
 
17
17
  import { observer } from 'mobx-react-lite';
18
18
  import { LEGEND_QUERY_ROUTE_PATTERN } from '../stores/LegendQueryRouter.js';
19
- import { QuerySetup } from './QuerySetup.js';
19
+ import { QuerySetupLandingPage } from './QuerySetup.js';
20
20
  import {
21
21
  MappingQueryCreator,
22
22
  ExistingQueryEditor,
@@ -35,6 +35,8 @@ import {
35
35
  LegendQueryBaseStoreProvider,
36
36
  useLegendQueryApplicationStore,
37
37
  } from './LegendQueryBaseStoreProvider.js';
38
+ import { EditExistingQuerySetup } from './EditExistingQuerySetup.js';
39
+ import { CreateMappingQuerySetup } from './CreateMappingQuerySetup.js';
38
40
 
39
41
  const LegendQueryApplicationRoot = observer(() => {
40
42
  const applicationStore = useLegendQueryApplicationStore();
@@ -48,7 +50,17 @@ const LegendQueryApplicationRoot = observer(() => {
48
50
  <Route
49
51
  exact={true}
50
52
  path={LEGEND_QUERY_ROUTE_PATTERN.SETUP}
51
- component={QuerySetup}
53
+ component={QuerySetupLandingPage}
54
+ />
55
+ <Route
56
+ exact={true}
57
+ path={LEGEND_QUERY_ROUTE_PATTERN.EDIT_EXISTING_QUERY_SETUP}
58
+ component={EditExistingQuerySetup}
59
+ />
60
+ <Route
61
+ exact={true}
62
+ path={LEGEND_QUERY_ROUTE_PATTERN.CREATE_MAPPING_QUERY_SETUP}
63
+ component={CreateMappingQuerySetup}
52
64
  />
53
65
  <Route
54
66
  exact={true}