@finos/legend-application-query 8.1.3 → 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
@@ -15,573 +15,230 @@
15
15
  */
16
16
 
17
17
  import {
18
- type SelectComponent,
19
- ArrowLeftIcon,
20
- ArrowRightIcon,
21
- BlankPanelContent,
22
18
  clsx,
23
- CustomSelectorInput,
24
19
  PanelLoadingIndicator,
25
- PlusIcon,
26
- RobotIcon,
27
- SearchIcon,
28
- UserIcon,
29
20
  QuestionCircleIcon,
30
- DroidIcon,
31
- ManageSearchIcon,
32
- ArrowCirceUpIcon,
21
+ CogIcon,
22
+ MoreHorizontalIcon,
23
+ DropdownMenu,
24
+ PencilIcon,
25
+ ChevronDownThinIcon,
26
+ CircleIcon,
27
+ MenuContent,
28
+ MenuContentItem,
29
+ MenuContentItemIcon,
30
+ MenuContentItemLabel,
31
+ CheckIcon,
32
+ MenuContentDivider,
33
33
  } from '@finos/legend-art';
34
+ import { getQueryParameters, guaranteeNonNullable } from '@finos/legend-shared';
35
+ import { observer, useLocalObservable } from 'mobx-react-lite';
36
+ import React, { createContext, useContext, useEffect } from 'react';
34
37
  import {
35
- debounce,
36
- getNullableFirstElement,
37
- isNonNullable,
38
- } from '@finos/legend-shared';
39
- import { flowResult } from 'mobx';
40
- import { observer } from 'mobx-react-lite';
41
- import { Fragment, useEffect, useMemo, useRef, useState } from 'react';
42
- import {
43
- generateMappingQueryCreatorRoute,
44
- generateExistingQueryEditorRoute,
45
- generateServiceQueryCreatorRoute,
38
+ LEGEND_QUERY_SETUP_QUERY_PARAM_TOKEN,
39
+ type QuerySetupQueryParams,
46
40
  } from '../stores/LegendQueryRouter.js';
47
41
  import {
48
- type QuerySetupState,
49
- type ServiceExecutionOption,
50
- CreateMappingQuerySetupState,
51
- EditExistingQuerySetupState,
52
- CloneServiceQuerySetupState,
53
- UpdateExistingServiceQuerySetupState,
54
- LoadProjectServiceQuerySetupState,
55
- QueryProductionizationSetupState,
42
+ QuerySetupLandingPageStore,
43
+ type BaseQuerySetupStore,
56
44
  } from '../stores/QuerySetupStore.js';
57
- import {
58
- useQuerySetupStore,
59
- withQuerySetupStore,
60
- } from './QuerySetupStoreProvider.js';
61
- import {
62
- type ProjectData,
63
- LATEST_VERSION_ALIAS,
64
- SNAPSHOT_VERSION_ALIAS,
65
- } from '@finos/legend-server-depot';
66
- import { compareSemVerVersions } from '@finos/legend-storage';
67
- import type { Mapping, PackageableRuntime } from '@finos/legend-graph';
68
- import {
69
- type PackageableElementOption,
70
- useApplicationStore,
71
- buildElementOption,
72
- EDITOR_LANGUAGE,
73
- TextInputEditor,
74
- } from '@finos/legend-application';
75
- import {
76
- type ServiceOption,
77
- type QueryOption,
78
- buildServiceOption,
79
- formatServiceOptionLabel,
80
- buildQueryOption,
81
- } from '@finos/legend-query-builder';
45
+ import type { ProjectData } from '@finos/legend-server-depot';
46
+ import { useApplicationStore } from '@finos/legend-application';
82
47
  import { useLegendQueryApplicationStore } from './LegendQueryBaseStoreProvider.js';
48
+ import type { QuerySetupActionConfiguration } from '../stores/LegendQueryApplicationPlugin.js';
83
49
 
84
- const EditExistingQuerySetup = observer(
85
- (props: { querySetupState: EditExistingQuerySetupState }) => {
86
- const { querySetupState } = props;
87
- const applicationStore = useApplicationStore();
88
- const setupStore = useQuerySetupStore();
89
- const querySearchRef = useRef<SelectComponent>(null);
90
- const [searchText, setSearchText] = useState('');
91
-
92
- // actions
93
- const back = (): void => {
94
- setupStore.setSetupState(undefined);
95
- };
96
- const next = (): void => {
97
- if (querySetupState.currentQuery) {
98
- applicationStore.navigator.goToLocation(
99
- generateExistingQueryEditorRoute(querySetupState.currentQuery.id),
100
- );
101
- }
102
- };
103
- const canProceed = querySetupState.currentQuery;
104
-
105
- // query
106
- const queryOptions = querySetupState.queries.map(buildQueryOption);
107
- const selectedQueryOption = querySetupState.currentQuery
108
- ? buildQueryOption(querySetupState.currentQuery)
109
- : null;
110
- const onQueryOptionChange = (option: QueryOption | null): void => {
111
- if (option?.value !== querySetupState.currentQuery) {
112
- querySetupState.setCurrentQuery(option?.value.id);
113
- }
114
- };
115
- const formatQueryOptionLabel = (option: QueryOption): React.ReactNode => {
116
- const deleteQuery: React.MouseEventHandler<HTMLButtonElement> = (
117
- event,
118
- ) => {
119
- event.preventDefault();
120
- event.stopPropagation();
121
- setupStore.graphManagerState.graphManager
122
- .deleteQuery(option.value.id)
123
- .then(() =>
124
- flowResult(querySetupState.loadQueries('')).catch(
125
- applicationStore.alertUnhandledError,
126
- ),
127
- )
128
- .catch(applicationStore.alertUnhandledError);
129
- };
130
- if (option.value.id === querySetupState.currentQuery?.id) {
131
- return option.label;
132
- }
133
- return (
134
- <div className="query-setup__existing-query__query-option">
135
- <div
136
- className="query-setup__existing-query__query-option__label"
137
- title={option.label}
138
- >
139
- {option.label}
140
- </div>
141
- {querySetupState.showCurrentUserQueriesOnly && (
142
- <button
143
- className="query-setup__existing-query__query-option__action"
144
- tabIndex={-1}
145
- onClick={deleteQuery}
146
- >
147
- Delete
148
- </button>
149
- )}
150
- {!querySetupState.showCurrentUserQueriesOnly &&
151
- Boolean(option.value.owner) && (
152
- <div
153
- className={clsx(
154
- 'query-setup__existing-query__query-option__user',
155
- {
156
- 'query-setup__existing-query__query-option__user--mine':
157
- option.value.isCurrentUserQuery,
158
- },
159
- )}
160
- >
161
- {option.value.isCurrentUserQuery ? 'mine' : option.value.owner}
162
- </div>
163
- )}
164
- </div>
165
- );
166
- };
50
+ export type ProjectOption = { label: string; value: ProjectData };
51
+ export const buildProjectOption = (project: ProjectData): ProjectOption => ({
52
+ label: `${project.groupId}.${project.artifactId}`,
53
+ value: project,
54
+ });
167
55
 
168
- // search text
169
- const debouncedLoadQueries = useMemo(
170
- () =>
171
- debounce((input: string): void => {
172
- flowResult(querySetupState.loadQueries(input)).catch(
173
- applicationStore.alertUnhandledError,
174
- );
175
- }, 500),
176
- [applicationStore, querySetupState],
177
- );
178
- const onSearchTextChange = (value: string): void => {
179
- if (value !== searchText) {
180
- setSearchText(value);
181
- debouncedLoadQueries.cancel();
182
- debouncedLoadQueries(value);
183
- }
184
- };
56
+ export type VersionOption = { label: string; value: string };
57
+ export const buildVersionOption = (version: string): VersionOption => ({
58
+ label: version,
59
+ value: version,
60
+ });
185
61
 
186
- // show current user queries only
187
- const toggleShowCurrentUserQueriesOnly = (): void => {
188
- querySetupState.setShowCurrentUserQueriesOnly(
189
- !querySetupState.showCurrentUserQueriesOnly,
190
- );
191
- debouncedLoadQueries.cancel();
192
- debouncedLoadQueries(searchText);
193
- };
62
+ const QuerySetupLandingPageStoreContext = createContext<
63
+ QuerySetupLandingPageStore | undefined
64
+ >(undefined);
194
65
 
195
- useEffect(() => {
196
- flowResult(querySetupState.loadQueries('')).catch(
197
- applicationStore.alertUnhandledError,
198
- );
199
- }, [querySetupState, applicationStore]);
66
+ const QuerySetupLandingPageStoreProvider: React.FC<{
67
+ children: React.ReactNode;
68
+ }> = ({ children }) => {
69
+ const applicationStore = useLegendQueryApplicationStore();
70
+ const store = useLocalObservable(
71
+ () => new QuerySetupLandingPageStore(applicationStore),
72
+ );
73
+ return (
74
+ <QuerySetupLandingPageStoreContext.Provider value={store}>
75
+ {children}
76
+ </QuerySetupLandingPageStoreContext.Provider>
77
+ );
78
+ };
200
79
 
201
- useEffect(() => {
202
- querySearchRef.current?.focus();
203
- }, []);
80
+ export const useQuerySetupLandingPageStore = (): QuerySetupLandingPageStore =>
81
+ guaranteeNonNullable(
82
+ useContext(QuerySetupLandingPageStoreContext),
83
+ `Can't find query setup landing page store in context`,
84
+ );
204
85
 
86
+ export const withQuerySetupLandingPageStore = (
87
+ WrappedComponent: React.FC,
88
+ ): React.FC =>
89
+ function WithQuerySetupLandingPageStore() {
205
90
  return (
206
- <div className="query-setup__wizard query-setup__existing-query">
207
- <div className="query-setup__wizard__header query-setup__existing-query__header">
208
- <button
209
- className="query-setup__wizard__header__btn"
210
- onClick={back}
211
- title="Back to Main Menu"
212
- >
213
- <ArrowLeftIcon />
214
- </button>
215
- <div className="query-setup__wizard__header__title">
216
- Loading an existing query...
217
- </div>
218
- <button
219
- className={clsx('query-setup__wizard__header__btn', {
220
- 'query-setup__wizard__header__btn--ready': canProceed,
221
- })}
222
- onClick={next}
223
- disabled={!canProceed}
224
- title="Edit query"
225
- >
226
- <ArrowRightIcon />
227
- </button>
228
- </div>
229
- <div className="query-setup__wizard__content">
230
- <div className="query-setup__wizard__group query-setup__wizard__group--inline">
231
- <div className="query-setup__wizard__group__title">
232
- <SearchIcon />
233
- </div>
234
- <div className="query-setup__existing-query__input">
235
- <CustomSelectorInput
236
- ref={querySearchRef}
237
- className="query-setup__wizard__selector"
238
- options={queryOptions}
239
- isLoading={querySetupState.loadQueriesState.isInProgress}
240
- onInputChange={onSearchTextChange}
241
- inputValue={searchText}
242
- onChange={onQueryOptionChange}
243
- value={selectedQueryOption}
244
- placeholder="Search for query by name..."
245
- isClearable={true}
246
- escapeClearsValue={true}
247
- darkMode={true}
248
- formatOptionLabel={formatQueryOptionLabel}
249
- />
250
- <button
251
- className={clsx('query-setup__existing-query__btn', {
252
- 'query-setup__existing-query__btn--active':
253
- querySetupState.showCurrentUserQueriesOnly,
254
- })}
255
- tabIndex={-1}
256
- title={`[${
257
- querySetupState.showCurrentUserQueriesOnly ? 'on' : 'off'
258
- }] Toggle show only queries of current user`}
259
- onClick={toggleShowCurrentUserQueriesOnly}
260
- >
261
- <UserIcon />
262
- </button>
263
- </div>
264
- </div>
265
- <div className="query-setup__existing-query__preview">
266
- <PanelLoadingIndicator
267
- isLoading={querySetupState.loadQueryState.isInProgress}
268
- />
269
- {querySetupState.currentQuery && (
270
- <>
271
- {!querySetupState.currentQueryInfo && (
272
- <BlankPanelContent>{`Can't preview query`}</BlankPanelContent>
273
- )}
274
- {querySetupState.currentQueryInfo && (
275
- <TextInputEditor
276
- inputValue={querySetupState.currentQueryInfo.content}
277
- isReadOnly={true}
278
- language={EDITOR_LANGUAGE.PURE}
279
- showMiniMap={false}
280
- hideGutter={true}
281
- />
282
- )}
283
- </>
284
- )}
285
- {!querySetupState.currentQuery && (
286
- <BlankPanelContent>No query to preview</BlankPanelContent>
287
- )}
288
- </div>
289
- </div>
290
- </div>
91
+ <QuerySetupLandingPageStoreProvider>
92
+ <WrappedComponent />
93
+ </QuerySetupLandingPageStoreProvider>
291
94
  );
292
- },
293
- );
95
+ };
294
96
 
295
- const QueryProductionizationSetup = observer(
296
- (props: { querySetupState: QueryProductionizationSetupState }) => {
297
- const { querySetupState } = props;
97
+ const QuerySetupAction = observer(
98
+ (props: { action: QuerySetupActionConfiguration }) => {
99
+ const { action } = props;
100
+ const setupStore = useQuerySetupLandingPageStore();
298
101
  const applicationStore = useApplicationStore();
299
- const setupStore = useQuerySetupStore();
300
- const querySearchRef = useRef<SelectComponent>(null);
301
- const [searchText, setSearchText] = useState('');
302
-
303
- // actions
304
- const back = (): void => {
305
- setupStore.setSetupState(undefined);
306
- };
307
- const next = (): void => {
308
- if (querySetupState.currentQuery) {
309
- querySetupState
310
- .loadQueryProductionizer()
311
- .catch(applicationStore.alertUnhandledError);
312
- }
102
+ const onClick = (): void => {
103
+ action.action(setupStore).catch(applicationStore.alertUnhandledError);
313
104
  };
314
- const canProceed = querySetupState.currentQuery;
315
-
316
- // query
317
- const queryOptions = querySetupState.queries.map(buildQueryOption);
318
- const selectedQueryOption = querySetupState.currentQuery
319
- ? buildQueryOption(querySetupState.currentQuery)
320
- : null;
321
- const onQueryOptionChange = (option: QueryOption | null): void => {
322
- if (option?.value !== querySetupState.currentQuery) {
323
- querySetupState.setCurrentQuery(option?.value.id);
324
- }
325
- };
326
-
327
- // search text
328
- const debouncedLoadQueries = useMemo(
329
- () =>
330
- debounce((input: string): void => {
331
- flowResult(querySetupState.loadQueries(input)).catch(
332
- applicationStore.alertUnhandledError,
333
- );
334
- }, 500),
335
- [applicationStore, querySetupState],
336
- );
337
- const onSearchTextChange = (value: string): void => {
338
- if (value !== searchText) {
339
- setSearchText(value);
340
- debouncedLoadQueries.cancel();
341
- debouncedLoadQueries(value);
342
- }
343
- };
344
-
345
- useEffect(() => {
346
- flowResult(querySetupState.loadQueries('')).catch(
347
- applicationStore.alertUnhandledError,
348
- );
349
- }, [querySetupState, applicationStore]);
350
-
351
- useEffect(() => {
352
- querySearchRef.current?.focus();
353
- }, []);
354
105
 
106
+ if (!setupStore.showAdvancedActions && action.isAdvanced) {
107
+ return null;
108
+ }
355
109
  return (
356
- <div className="query-setup__wizard query-setup__productionize-query">
357
- <div className="query-setup__wizard__header query-setup__productionize-query__header">
358
- <button
359
- className="query-setup__wizard__header__btn"
360
- onClick={back}
361
- title="Back to Main Menu"
362
- >
363
- <ArrowLeftIcon />
364
- </button>
365
- <div className="query-setup__wizard__header__title">
366
- Productionizing an existing query...
367
- </div>
368
- <button
369
- className={clsx('query-setup__wizard__header__btn', {
370
- 'query-setup__wizard__header__btn--ready': canProceed,
371
- })}
372
- onClick={next}
373
- disabled={!canProceed}
374
- title="Productionize query"
375
- >
376
- <ArrowRightIcon />
377
- </button>
110
+ <button
111
+ className={clsx('query-setup__landing-page__action', action.className, {
112
+ 'query-setup__landing-page__action--advanced': action.isAdvanced,
113
+ })}
114
+ tabIndex={-1}
115
+ onClick={onClick}
116
+ >
117
+ <div className="query-setup__landing-page__action__icon">
118
+ {action.icon}
378
119
  </div>
379
- <div className="query-setup__wizard__content">
380
- <div className="query-setup__wizard__group query-setup__wizard__group--inline">
381
- <div className="query-setup__wizard__group__title">
382
- <SearchIcon />
383
- </div>
384
- <CustomSelectorInput
385
- ref={querySearchRef}
386
- className="query-setup__wizard__selector"
387
- options={queryOptions}
388
- isLoading={querySetupState.loadQueriesState.isInProgress}
389
- onInputChange={onSearchTextChange}
390
- inputValue={searchText}
391
- onChange={onQueryOptionChange}
392
- value={selectedQueryOption}
393
- placeholder="Search for query by name..."
394
- isClearable={true}
395
- escapeClearsValue={true}
396
- darkMode={true}
397
- />
398
- </div>
399
- <div className="query-setup__productionize-query__preview">
400
- <PanelLoadingIndicator
401
- isLoading={querySetupState.loadQueryState.isInProgress}
402
- />
403
- {querySetupState.currentQuery && (
404
- <>
405
- {!querySetupState.currentQueryInfo && (
406
- <BlankPanelContent>{`Can't preview query`}</BlankPanelContent>
407
- )}
408
- {querySetupState.currentQueryInfo && (
409
- <TextInputEditor
410
- inputValue={querySetupState.currentQueryInfo.content}
411
- isReadOnly={true}
412
- language={EDITOR_LANGUAGE.PURE}
413
- showMiniMap={false}
414
- hideGutter={true}
415
- />
416
- )}
417
- </>
418
- )}
419
- {!querySetupState.currentQuery && (
420
- <BlankPanelContent>No query to preview</BlankPanelContent>
421
- )}
422
- </div>
120
+ <div className="query-setup__landing-page__action__label">
121
+ {action.label}
423
122
  </div>
424
- </div>
123
+ </button>
425
124
  );
426
125
  },
427
126
  );
428
127
 
429
- export const UpdateExistingServiceQuerySetup = observer(
430
- (props: { querySetupState: UpdateExistingServiceQuerySetupState }) => {
431
- const { querySetupState } = props;
432
- const applicationStore = useLegendQueryApplicationStore();
433
- const setupStore = useQuerySetupStore();
434
- const serviceSearchRef = useRef<SelectComponent>(null);
435
- const [searchText, setSearchText] = useState('');
436
-
437
- const back = (): void => {
438
- setupStore.setSetupState(undefined);
439
- };
440
-
441
- const serviceOptions = querySetupState.services.map(buildServiceOption);
442
- const onServiceOptionChange = (option: ServiceOption): void => {
443
- querySetupState
444
- .loadServiceUpdater(option.value)
445
- .catch(applicationStore.alertUnhandledError);
446
- };
128
+ const QuerySetupActionGroupConfigMenu = observer(() => {
129
+ const setupStore = useQuerySetupLandingPageStore();
130
+ const toggleShowAdvancedActions = (): void =>
131
+ setupStore.setShowAdvancedActions(!setupStore.showAdvancedActions);
132
+ const toggleShowAllGroups = (): void =>
133
+ setupStore.setShowAllGroups(!setupStore.showAllGroups);
134
+ const reset = (): void => setupStore.resetConfig();
447
135
 
448
- // search text
449
- const debouncedLoadServices = useMemo(
450
- () =>
451
- debounce((input: string): void => {
452
- flowResult(querySetupState.loadServices(input)).catch(
453
- applicationStore.alertUnhandledError,
454
- );
455
- }, 500),
456
- [applicationStore, querySetupState],
457
- );
458
- const onSearchTextChange = (value: string): void => {
459
- if (value !== searchText) {
460
- setSearchText(value);
461
- debouncedLoadServices.cancel();
462
- debouncedLoadServices(value);
463
- }
464
- };
465
-
466
- useEffect(() => {
467
- flowResult(querySetupState.loadServices('')).catch(
468
- applicationStore.alertUnhandledError,
469
- );
470
- }, [querySetupState, applicationStore]);
136
+ return (
137
+ <MenuContent className="query-setup__landing-page__config-menu">
138
+ <MenuContentItem onClick={toggleShowAdvancedActions}>
139
+ <MenuContentItemIcon>
140
+ {setupStore.showAdvancedActions ? <CheckIcon /> : null}
141
+ </MenuContentItemIcon>
142
+ <MenuContentItemLabel>Show advanced actions</MenuContentItemLabel>
143
+ </MenuContentItem>
144
+ <MenuContentItem onClick={toggleShowAllGroups}>
145
+ <MenuContentItemIcon>
146
+ {setupStore.showAllGroups ? <CheckIcon /> : null}
147
+ </MenuContentItemIcon>
148
+ <MenuContentItemLabel>Show all action groups</MenuContentItemLabel>
149
+ </MenuContentItem>
150
+ <MenuContentDivider />
151
+ <MenuContentItem disabled={true}>Focus on action group:</MenuContentItem>
152
+ <MenuContentItem onClick={() => setupStore.setTagToFocus(undefined)}>
153
+ <MenuContentItemIcon>
154
+ {!setupStore.tagToFocus ? <CheckIcon /> : null}
155
+ </MenuContentItemIcon>
156
+ <MenuContentItemLabel>(none)</MenuContentItemLabel>
157
+ </MenuContentItem>
158
+ {setupStore.tags.map((groupKey) => (
159
+ <MenuContentItem
160
+ key={groupKey}
161
+ onClick={() => setupStore.setTagToFocus(groupKey)}
162
+ >
163
+ <MenuContentItemIcon>
164
+ {setupStore.tagToFocus === groupKey ? <CheckIcon /> : null}
165
+ </MenuContentItemIcon>
166
+ <MenuContentItemLabel>{groupKey}</MenuContentItemLabel>
167
+ </MenuContentItem>
168
+ ))}
169
+ <MenuContentDivider />
170
+ <MenuContentItem onClick={reset} disabled={!setupStore.isCustomized}>
171
+ Reset
172
+ </MenuContentItem>
173
+ </MenuContent>
174
+ );
175
+ });
471
176
 
472
- useEffect(() => {
473
- serviceSearchRef.current?.focus();
474
- }, []);
177
+ const QuerySetupActionGroup = observer(
178
+ (props: { tag?: string | undefined }) => {
179
+ const { tag } = props;
180
+ const setupStore = useQuerySetupLandingPageStore();
181
+ const actions = setupStore.actions.filter((action) => action.tag === tag);
182
+ const createActions = actions.filter((action) => action.isCreateAction);
183
+ const editActions = actions.filter((action) => !action.isCreateAction);
184
+ const showAdvancedActions = (): void =>
185
+ setupStore.setShowAdvancedActions(true);
475
186
 
476
187
  return (
477
- <div className="query-setup__wizard query-setup__existing-service-query">
478
- <div className="query-setup__wizard__header query-setup__existing-service-query__header">
479
- <button
480
- className="query-setup__wizard__header__btn"
481
- onClick={back}
482
- title="Back to Main Menu"
483
- >
484
- <ArrowLeftIcon />
485
- </button>
486
- <div className="query-setup__wizard__header__title">
487
- Updating an existing service query...
188
+ <div
189
+ className={clsx('query-setup__landing-page__action-group', {
190
+ 'query-setup__landing-page__action-group--with-tag': Boolean(tag),
191
+ })}
192
+ >
193
+ {tag && (
194
+ <div className="query-setup__landing-page__action-group__tag">
195
+ {tag}
488
196
  </div>
197
+ )}
198
+ <div className="query-setup__landing-page__action-group__header">
199
+ {(!tag || setupStore.tagToFocus === tag) && (
200
+ <DropdownMenu
201
+ className="query-setup__landing-page__action-group__config"
202
+ title="Show settings..."
203
+ content={<QuerySetupActionGroupConfigMenu />}
204
+ menuProps={{
205
+ anchorOrigin: { vertical: 'bottom', horizontal: 'left' },
206
+ transformOrigin: { vertical: 'top', horizontal: 'left' },
207
+ }}
208
+ >
209
+ <CogIcon />
210
+ {setupStore.isCustomized && (
211
+ <div className="query-setup__landing-page__action-group__config__status">
212
+ <CircleIcon />
213
+ </div>
214
+ )}
215
+ </DropdownMenu>
216
+ )}
489
217
  </div>
490
- <div className="query-setup__wizard__content">
491
- <div className="query-setup__wizard__group query-setup__wizard__group--inline query-setup__existing-service-query__search-bar">
492
- <div className="query-setup__wizard__group__title">
493
- <SearchIcon />
494
- </div>
495
- <CustomSelectorInput
496
- ref={serviceSearchRef}
497
- className="query-setup__wizard__selector"
498
- options={serviceOptions}
499
- isLoading={querySetupState.loadServicesState.isInProgress}
500
- onInputChange={onSearchTextChange}
501
- inputValue={searchText}
502
- onChange={onServiceOptionChange}
503
- placeholder="Search for service..."
504
- darkMode={true}
505
- formatOptionLabel={formatServiceOptionLabel}
506
- />
218
+ <div className="query-setup__landing-page__action-group__body">
219
+ <div className="query-setup__landing-page__action-group__body__column">
220
+ {editActions.map((action) => (
221
+ <QuerySetupAction key={action.key} action={action} />
222
+ ))}
507
223
  </div>
508
- </div>
509
- </div>
510
- );
511
- },
512
- );
513
-
514
- type ProjectOption = { label: string; value: ProjectData };
515
- const buildProjectOption = (project: ProjectData): ProjectOption => ({
516
- label: `${project.groupId}.${project.artifactId}`,
517
- value: project,
518
- });
519
-
520
- type VersionOption = { label: string; value: string };
521
- const buildVersionOption = (version: string): VersionOption => ({
522
- label: version,
523
- value: version,
524
- });
525
-
526
- const LoadProjectServiceQuerySetup = observer(
527
- (props: { querySetupState: LoadProjectServiceQuerySetupState }) => {
528
- const { querySetupState } = props;
529
- const applicationStore = useApplicationStore();
530
- const setupStore = useQuerySetupStore();
531
- const back = (): void => {
532
- setupStore.setSetupState(undefined);
533
- };
534
-
535
- // project
536
- const projectOptions = querySetupState.projects.map(buildProjectOption);
537
- const projectSelectorPlaceholder = querySetupState.loadProjectsState
538
- .isInProgress
539
- ? 'Loading projects'
540
- : querySetupState.loadProjectsState.hasFailed
541
- ? 'Error fetching projects'
542
- : querySetupState.projects.length
543
- ? 'Choose a project'
544
- : 'You have no projects, please create or acquire access for at least one';
545
- const onProjectOptionChange = (option: ProjectOption): void => {
546
- querySetupState
547
- .loadProjectServiceUpdater(option.value)
548
- .catch(applicationStore.alertUnhandledError);
549
- };
550
-
551
- useEffect(() => {
552
- flowResult(querySetupState.loadProjects()).catch(
553
- applicationStore.alertUnhandledError,
554
- );
555
- }, [querySetupState, applicationStore]);
556
-
557
- return (
558
- <div className="query-setup__wizard query-setup__existing-service-query">
559
- <div className="query-setup__wizard__header query-setup__service-query__header">
560
- <button
561
- className="query-setup__wizard__header__btn"
562
- onClick={back}
563
- title="Back to Main Menu"
564
- >
565
- <ArrowLeftIcon />
566
- </button>
567
- <div className="query-setup__wizard__header__title">
568
- Load service query from a project...
224
+ <div className="query-setup__landing-page__action-group__body__column">
225
+ {createActions.map((action) => (
226
+ <QuerySetupAction key={action.key} action={action} />
227
+ ))}
569
228
  </div>
570
229
  </div>
571
- <div className="query-setup__wizard__content">
572
- <div className="query-setup__wizard__group query-setup__wizard__group--inline query-setup__existing-service-query__search-bar">
573
- <CustomSelectorInput
574
- className="query-setup__wizard__selector"
575
- options={projectOptions}
576
- disabled={
577
- querySetupState.loadProjectsState.isInProgress ||
578
- !projectOptions.length
579
- }
580
- isLoading={querySetupState.loadProjectsState.isInProgress}
581
- onChange={onProjectOptionChange}
582
- placeholder={projectSelectorPlaceholder}
583
- darkMode={true}
584
- />
230
+ <div className="query-setup__landing-page__action-group__footer">
231
+ <div className="query-setup__landing-page__action-group__footer__content">
232
+ {!setupStore.showAdvancedActions && (
233
+ <button
234
+ className="query-setup__landing-page__action-group__footer__btn"
235
+ onClick={showAdvancedActions}
236
+ tabIndex={-1}
237
+ title="Show advanced actions"
238
+ >
239
+ <MoreHorizontalIcon />
240
+ </button>
241
+ )}
585
242
  </div>
586
243
  </div>
587
244
  </div>
@@ -589,680 +246,125 @@ const LoadProjectServiceQuerySetup = observer(
589
246
  },
590
247
  );
591
248
 
592
- const CloneServiceQuerySetup = observer(
593
- (props: { querySetupState: CloneServiceQuerySetupState }) => {
594
- const { querySetupState } = props;
595
- const applicationStore = useApplicationStore();
596
- const setupStore = useQuerySetupStore();
597
-
598
- // actions
599
- const back = (): void => {
600
- setupStore.setSetupState(undefined);
601
- };
602
- const next = (): void => {
603
- if (
604
- querySetupState.currentProject &&
605
- querySetupState.currentVersionId &&
606
- querySetupState.currentServiceExecutionOption
607
- ) {
608
- applicationStore.navigator.goToLocation(
609
- generateServiceQueryCreatorRoute(
610
- querySetupState.currentProject.groupId,
611
- querySetupState.currentProject.artifactId,
612
- querySetupState.currentVersionId,
613
- querySetupState.currentServiceExecutionOption.service.path,
614
- querySetupState.currentServiceExecutionOption.key,
615
- ),
616
- );
617
- }
618
- };
619
- const canProceed =
620
- querySetupState.currentProject &&
621
- querySetupState.currentVersionId &&
622
- querySetupState.currentServiceExecutionOption;
623
-
624
- // project
625
- const projectOptions = querySetupState.projects.map(buildProjectOption);
626
- const selectedProjectOption = querySetupState.currentProject
627
- ? buildProjectOption(querySetupState.currentProject)
628
- : null;
629
- const projectSelectorPlaceholder = querySetupState.loadProjectsState
630
- .isInProgress
631
- ? 'Loading projects'
632
- : querySetupState.loadProjectsState.hasFailed
633
- ? 'Error fetching projects'
634
- : querySetupState.projects.length
635
- ? 'Choose a project'
636
- : 'You have no projects, please create or acquire access for at least one';
637
- const onProjectOptionChange = (option: ProjectOption | null): void => {
638
- if (option?.value !== querySetupState.currentProject) {
639
- querySetupState.setCurrentProject(option?.value);
640
- // cascade
641
- querySetupState.setCurrentVersionId(undefined);
642
- querySetupState.setCurrentServiceExecutionOption(undefined);
643
- }
644
- };
645
-
646
- // version
647
- const versionOptions = [
648
- LATEST_VERSION_ALIAS,
649
- SNAPSHOT_VERSION_ALIAS,
650
- ...(querySetupState.currentProject?.versions ?? []),
651
- ]
652
- .slice()
653
- .sort((v1, v2) => compareSemVerVersions(v2, v1))
654
- .map(buildVersionOption);
655
- const selectedVersionOption = querySetupState.currentVersionId
656
- ? buildVersionOption(querySetupState.currentVersionId)
657
- : null;
658
- const versionSelectorPlaceholder = !querySetupState.currentProject
659
- ? 'No project selected'
660
- : 'Choose a version';
661
- const onVersionOptionChange = async (
662
- option: VersionOption | null,
663
- ): Promise<void> => {
664
- if (option?.value !== querySetupState.currentVersionId) {
665
- querySetupState.setCurrentVersionId(option?.value);
666
- // cascade
667
- querySetupState.setCurrentServiceExecutionOption(undefined);
668
- if (
669
- querySetupState.currentProject &&
670
- querySetupState.currentVersionId
671
- ) {
672
- await flowResult(
673
- querySetupState.loadServiceExecutionOptions(
674
- querySetupState.currentProject,
675
- querySetupState.currentVersionId,
676
- ),
677
- ).catch(applicationStore.alertUnhandledError);
678
- }
679
- }
680
- };
681
-
682
- // service and key
683
- const serviceExecutionOptions = querySetupState.serviceExecutionOptions.map(
684
- (option) => ({
685
- label: `${option.service.name}${option.key ? ` [${option.key}]` : ''}`,
686
- value: option,
687
- }),
249
+ export const QuerySetupLandingPage = withQuerySetupLandingPageStore(
250
+ observer(() => {
251
+ const setupStore = useQuerySetupLandingPageStore();
252
+ const applicationStore = useLegendQueryApplicationStore();
253
+ const params = getQueryParameters<QuerySetupQueryParams>(
254
+ applicationStore.navigator.getCurrentAddress(),
255
+ true,
688
256
  );
689
- const selectedServiceExecutionOption =
690
- querySetupState.currentServiceExecutionOption
691
- ? {
692
- label: `${
693
- querySetupState.currentServiceExecutionOption.service.name
694
- }${
695
- querySetupState.currentServiceExecutionOption.key
696
- ? ` [${querySetupState.currentServiceExecutionOption.key}]`
697
- : ''
698
- }`,
699
- value: querySetupState.currentServiceExecutionOption,
700
- }
701
- : null;
702
- const serviceExecutionSelectorPlaceholder = serviceExecutionOptions.length
703
- ? 'Choose a service'
704
- : 'No service available';
705
- const onServiceExecutionOptionChange = (
706
- option: { value: ServiceExecutionOption } | null,
707
- ): void => {
708
- querySetupState.setCurrentServiceExecutionOption(
709
- option?.value ?? undefined,
257
+ const showAdvancedActions =
258
+ params[LEGEND_QUERY_SETUP_QUERY_PARAM_TOKEN.SHOW_ADVANCED_ACTIONS];
259
+ const showAllGroups =
260
+ params[LEGEND_QUERY_SETUP_QUERY_PARAM_TOKEN.SHOW_ALL_GROUPS];
261
+ const tagToFocus = params[LEGEND_QUERY_SETUP_QUERY_PARAM_TOKEN.TAG];
262
+ const goToStudio = (): void =>
263
+ applicationStore.navigator.visitAddress(
264
+ applicationStore.config.studioUrl,
710
265
  );
711
- };
266
+ const showAllActionGroup = (): void => setupStore.setShowAllGroups(true);
712
267
 
713
268
  useEffect(() => {
714
- flowResult(querySetupState.loadProjects()).catch(
715
- applicationStore.alertUnhandledError,
716
- );
717
- }, [querySetupState, applicationStore]);
269
+ setupStore.initialize(showAdvancedActions, showAllGroups, tagToFocus);
270
+ }, [setupStore, showAdvancedActions, showAllGroups, tagToFocus]);
718
271
 
719
272
  return (
720
- <div className="query-setup__wizard query-setup__service-query">
721
- <div className="query-setup__wizard__header query-setup__service-query__header">
722
- <button
723
- className="query-setup__wizard__header__btn"
724
- onClick={back}
725
- title="Back to Main Menu"
726
- >
727
- <ArrowLeftIcon />
728
- </button>
729
- <div className="query-setup__wizard__header__title">
730
- Clone an existing service query...
731
- </div>
732
- <button
733
- className={clsx('query-setup__wizard__header__btn', {
734
- 'query-setup__wizard__header__btn--ready': canProceed,
735
- })}
736
- onClick={next}
737
- disabled={!canProceed}
738
- title="Create a new query"
739
- >
740
- <ArrowRightIcon />
741
- </button>
742
- </div>
743
- <div className="query-setup__wizard__content">
744
- <div className="query-setup__service-query__project">
745
- <div className="query-setup__wizard__group">
746
- <div className="query-setup__wizard__group__title">Project</div>
747
- <CustomSelectorInput
748
- className="query-setup__wizard__selector"
749
- options={projectOptions}
750
- disabled={
751
- querySetupState.loadProjectsState.isInProgress ||
752
- !projectOptions.length
753
- }
754
- isLoading={querySetupState.loadProjectsState.isInProgress}
755
- onChange={onProjectOptionChange}
756
- value={selectedProjectOption}
757
- placeholder={projectSelectorPlaceholder}
758
- isClearable={true}
759
- escapeClearsValue={true}
760
- darkMode={true}
761
- />
762
- </div>
763
- <div className="query-setup__wizard__group">
764
- <div className="query-setup__wizard__group__title">Version</div>
765
- <CustomSelectorInput
766
- className="query-setup__wizard__selector"
767
- options={versionOptions}
768
- disabled={!querySetupState.currentProject}
769
- onChange={onVersionOptionChange}
770
- value={selectedVersionOption}
771
- placeholder={versionSelectorPlaceholder}
772
- isClearable={true}
773
- escapeClearsValue={true}
774
- darkMode={true}
775
- />
776
- </div>
777
- </div>
778
- <div className="query-setup__service-query__graph">
779
- {(!querySetupState.currentProject ||
780
- !querySetupState.currentVersionId ||
781
- !querySetupState.loadServiceExecutionsState.hasSucceeded) && (
782
- <div className="query-setup__service-query__graph__loader">
783
- <PanelLoadingIndicator
784
- isLoading={
785
- Boolean(querySetupState.currentProject) &&
786
- Boolean(querySetupState.currentVersionId) &&
787
- !querySetupState.loadServiceExecutionsState.isInProgress
788
- }
273
+ <div className="query-setup">
274
+ <div className="query-setup__landing-page">
275
+ {setupStore.initState.hasCompleted && (
276
+ <>
277
+ <div className="query-setup__landing-page__title">
278
+ What do you want to do today
279
+ <QuestionCircleIcon
280
+ className="query-setup__landing-page__title__question-mark"
281
+ title="Choose one of the option below to start"
789
282
  />
790
- <BlankPanelContent>
791
- {querySetupState.loadServiceExecutionsState.isInProgress
792
- ? `Surveying service executions...`
793
- : querySetupState.loadServiceExecutionsState.hasFailed
794
- ? `Can't load service executions`
795
- : 'Project and version must be specified'}
796
- </BlankPanelContent>
797
283
  </div>
798
- )}
799
- {querySetupState.currentProject &&
800
- querySetupState.currentVersionId &&
801
- querySetupState.loadServiceExecutionsState.hasSucceeded && (
802
- <>
803
- <div className="query-setup__wizard__group">
804
- <div className="query-setup__wizard__group__title">
805
- Service
806
- </div>
807
- <CustomSelectorInput
808
- className="query-setup__wizard__selector"
809
- options={serviceExecutionOptions}
810
- disabled={!serviceExecutionOptions.length}
811
- onChange={onServiceExecutionOptionChange}
812
- value={selectedServiceExecutionOption}
813
- placeholder={serviceExecutionSelectorPlaceholder}
814
- isClearable={true}
815
- escapeClearsValue={true}
816
- darkMode={true}
817
- />
818
- </div>
819
- </>
820
- )}
821
- </div>
284
+ <div className="query-setup__landing-page__actions">
285
+ {setupStore.tagToFocus && (
286
+ <QuerySetupActionGroup tag={setupStore.tagToFocus} />
287
+ )}
288
+ {!setupStore.tagToFocus && (
289
+ <>
290
+ <QuerySetupActionGroup />
291
+ {setupStore.showAllGroups && (
292
+ <>
293
+ {setupStore.tags.map((tag) => (
294
+ <QuerySetupActionGroup key={tag} tag={tag} />
295
+ ))}
296
+ <div className="query-setup__landing-page__action-group query-setup__landing-page__action-group--studio">
297
+ <div className="query-setup__landing-page__action-group__tag">
298
+ Developer Workstation
299
+ </div>
300
+ <div className="query-setup__landing-page__action-group__header" />
301
+ <div className="query-setup__landing-page__action-group__body">
302
+ <button
303
+ className="query-setup__landing-page__action query-setup__landing-page__action--studio"
304
+ onClick={goToStudio}
305
+ tabIndex={-1}
306
+ >
307
+ <div className="query-setup__landing-page__action__icon">
308
+ <PencilIcon />
309
+ </div>
310
+ <div className="query-setup__landing-page__action__label">
311
+ Open Legend Studio
312
+ </div>
313
+ </button>
314
+ </div>
315
+ <div className="query-setup__landing-page__action-group__footer" />
316
+ </div>
317
+ </>
318
+ )}
319
+ {!setupStore.showAllGroups && (
320
+ <div className="query-setup__landing-page__footer">
321
+ <button
322
+ className="query-setup__landing-page__footer__more-btn"
323
+ onClick={showAllActionGroup}
324
+ tabIndex={-1}
325
+ title="Show all action groups"
326
+ >
327
+ <ChevronDownThinIcon />
328
+ </button>
329
+ </div>
330
+ )}
331
+ </>
332
+ )}
333
+ </div>
334
+ </>
335
+ )}
822
336
  </div>
823
337
  </div>
824
338
  );
825
- },
339
+ }),
826
340
  );
827
341
 
828
- const CreateMappingQuerySetup = observer(
829
- (props: { querySetupState: CreateMappingQuerySetupState }) => {
830
- const { querySetupState } = props;
831
- const applicationStore = useApplicationStore();
832
- const setupStore = useQuerySetupStore();
833
-
834
- // actions
835
- const back = (): void => {
836
- setupStore.setSetupState(undefined);
837
- };
838
- const next = (): void => {
839
- if (
840
- querySetupState.currentProject &&
841
- querySetupState.currentVersionId &&
842
- querySetupState.currentMapping &&
843
- querySetupState.currentRuntime
844
- ) {
845
- applicationStore.navigator.goToLocation(
846
- generateMappingQueryCreatorRoute(
847
- querySetupState.currentProject.groupId,
848
- querySetupState.currentProject.artifactId,
849
- querySetupState.currentVersionId,
850
- querySetupState.currentMapping.path,
851
- querySetupState.currentRuntime.path,
852
- ),
853
- );
854
- }
855
- };
856
- const canProceed =
857
- querySetupState.currentProject &&
858
- querySetupState.currentVersionId &&
859
- querySetupState.currentMapping &&
860
- querySetupState.currentRuntime;
861
-
862
- // project
863
- const projectOptions = querySetupState.projects.map(buildProjectOption);
864
- const selectedProjectOption = querySetupState.currentProject
865
- ? buildProjectOption(querySetupState.currentProject)
866
- : null;
867
- const projectSelectorPlaceholder = querySetupState.loadProjectsState
868
- .isInProgress
869
- ? 'Loading projects'
870
- : querySetupState.loadProjectsState.hasFailed
871
- ? 'Error fetching projects'
872
- : querySetupState.projects.length
873
- ? 'Choose a project'
874
- : 'You have no projects, please create or acquire access for at least one';
875
- const onProjectOptionChange = (option: ProjectOption | null): void => {
876
- if (option?.value !== querySetupState.currentProject) {
877
- querySetupState.setCurrentProject(option?.value);
878
- // cascade
879
- querySetupState.setCurrentVersionId(undefined);
880
- querySetupState.setCurrentMapping(undefined);
881
- querySetupState.setCurrentRuntime(undefined);
882
- }
883
- };
884
-
885
- // version
886
- const versionOptions = [
887
- LATEST_VERSION_ALIAS,
888
- SNAPSHOT_VERSION_ALIAS,
889
- ...(querySetupState.currentProject?.versions ?? []),
890
- ]
891
- .slice()
892
- .sort((v1, v2) => compareSemVerVersions(v2, v1))
893
- .map(buildVersionOption);
894
- const selectedVersionOption = querySetupState.currentVersionId
895
- ? buildVersionOption(querySetupState.currentVersionId)
896
- : null;
897
- const versionSelectorPlaceholder = !querySetupState.currentProject
898
- ? 'No project selected'
899
- : 'Choose a version';
900
- const onVersionOptionChange = async (
901
- option: VersionOption | null,
902
- ): Promise<void> => {
903
- if (option?.value !== querySetupState.currentVersionId) {
904
- querySetupState.setCurrentVersionId(option?.value);
905
- // cascade
906
- querySetupState.setCurrentMapping(undefined);
907
- querySetupState.setCurrentRuntime(undefined);
908
- if (
909
- querySetupState.currentProject &&
910
- querySetupState.currentVersionId
911
- ) {
912
- await flowResult(
913
- querySetupState.surveyMappingRuntimeCompatibility(
914
- querySetupState.currentProject,
915
- querySetupState.currentVersionId,
916
- ),
917
- ).catch(applicationStore.alertUnhandledError);
918
- }
919
- }
920
- };
342
+ export const BaseQuerySetupStoreContext = createContext<
343
+ BaseQuerySetupStore | undefined
344
+ >(undefined);
921
345
 
922
- // mapping
923
- const mappingOptions =
924
- querySetupState.mappingRuntimeCompatibilitySurveyResult.map((result) =>
925
- buildElementOption(result.mapping),
926
- );
927
- const selectedMappingOption = querySetupState.currentMapping
928
- ? {
929
- label: querySetupState.currentMapping.name,
930
- value: querySetupState.currentMapping,
931
- }
932
- : null;
933
- const mappingSelectorPlaceholder = mappingOptions.length
934
- ? 'Choose a mapping'
935
- : 'No mapping available';
936
- const onMappingOptionChange = (
937
- option: PackageableElementOption<Mapping> | null,
938
- ): void => {
939
- querySetupState.setCurrentMapping(option?.value);
940
- // cascade
941
- if (querySetupState.currentMapping) {
942
- querySetupState.setCurrentRuntime(
943
- getNullableFirstElement(querySetupState.compatibleRuntimes),
944
- );
945
- } else {
946
- querySetupState.setCurrentRuntime(undefined);
947
- }
948
- };
346
+ export const useBaseQuerySetupStore = (): BaseQuerySetupStore =>
347
+ guaranteeNonNullable(
348
+ useContext(BaseQuerySetupStoreContext),
349
+ `Can't find base query setup store in context`,
350
+ );
949
351
 
950
- // runtime
951
- const runtimeOptions =
952
- querySetupState.compatibleRuntimes.map(buildElementOption);
953
- const selectedRuntimeOption = querySetupState.currentRuntime
954
- ? {
955
- label: querySetupState.currentRuntime.name,
956
- value: querySetupState.currentRuntime,
957
- }
958
- : null;
959
- const runtimeSelectorPlaceholder = !querySetupState.currentMapping
960
- ? 'No mapping specified'
961
- : runtimeOptions.length
962
- ? 'Choose a runtime'
963
- : 'No runtime available';
964
- const onRuntimeOptionChange = (
965
- option: PackageableElementOption<PackageableRuntime> | null,
966
- ): void => {
967
- querySetupState.setCurrentRuntime(option?.value);
968
- };
352
+ export const BaseQuerySetup = observer(
353
+ (props: { children: React.ReactNode }) => {
354
+ const { children } = props;
355
+ const setupStore = useBaseQuerySetupStore();
969
356
 
970
357
  useEffect(() => {
971
- flowResult(querySetupState.loadProjects()).catch(
972
- applicationStore.alertUnhandledError,
973
- );
974
- }, [querySetupState, applicationStore]);
358
+ setupStore.initialize();
359
+ }, [setupStore]);
975
360
 
976
361
  return (
977
- <div className="query-setup__wizard query-setup__create-query">
978
- <div className="query-setup__wizard__header query-setup__create-query__header">
979
- <button
980
- className="query-setup__wizard__header__btn"
981
- onClick={back}
982
- title="Back to Main Menu"
983
- >
984
- <ArrowLeftIcon />
985
- </button>
986
- <div className="query-setup__wizard__header__title">
987
- Creating a new query...
988
- </div>
989
- <button
990
- className={clsx('query-setup__wizard__header__btn', {
991
- 'query-setup__wizard__header__btn--ready': canProceed,
992
- })}
993
- onClick={next}
994
- disabled={!canProceed}
995
- title="Create a new query"
996
- >
997
- <ArrowRightIcon />
998
- </button>
999
- </div>
1000
- <div className="query-setup__wizard__content">
1001
- <div className="query-setup__create-query__project">
1002
- <div className="query-setup__wizard__group">
1003
- <div className="query-setup__wizard__group__title">Project</div>
1004
- <CustomSelectorInput
1005
- className="query-setup__wizard__selector"
1006
- options={projectOptions}
1007
- disabled={
1008
- querySetupState.loadProjectsState.isInProgress ||
1009
- !projectOptions.length
1010
- }
1011
- isLoading={querySetupState.loadProjectsState.isInProgress}
1012
- onChange={onProjectOptionChange}
1013
- value={selectedProjectOption}
1014
- placeholder={projectSelectorPlaceholder}
1015
- isClearable={true}
1016
- escapeClearsValue={true}
1017
- darkMode={true}
1018
- />
1019
- </div>
1020
- <div className="query-setup__wizard__group">
1021
- <div className="query-setup__wizard__group__title">Version</div>
1022
- <CustomSelectorInput
1023
- className="query-setup__wizard__selector"
1024
- options={versionOptions}
1025
- disabled={!querySetupState.currentProject}
1026
- onChange={onVersionOptionChange}
1027
- value={selectedVersionOption}
1028
- placeholder={versionSelectorPlaceholder}
1029
- isClearable={true}
1030
- escapeClearsValue={true}
1031
- darkMode={true}
1032
- />
1033
- </div>
1034
- </div>
1035
- <div className="query-setup__create-query__graph">
1036
- {(!querySetupState.currentProject ||
1037
- !querySetupState.currentVersionId ||
1038
- !querySetupState.surveyMappingRuntimeCompatibilityState
1039
- .hasSucceeded) && (
1040
- <div className="query-setup__create-query__graph__loader">
1041
- <PanelLoadingIndicator
1042
- isLoading={
1043
- Boolean(querySetupState.currentProject) &&
1044
- Boolean(querySetupState.currentVersionId) &&
1045
- !querySetupState.surveyMappingRuntimeCompatibilityState
1046
- .hasSucceeded
1047
- }
1048
- />
1049
- <BlankPanelContent>
1050
- {querySetupState.surveyMappingRuntimeCompatibilityState
1051
- .isInProgress
1052
- ? `Surveying runtime and mapping compatibility...`
1053
- : querySetupState.surveyMappingRuntimeCompatibilityState
1054
- .hasFailed
1055
- ? `Can't load runtime and mapping`
1056
- : 'Project and version must be specified'}
1057
- </BlankPanelContent>
1058
- </div>
1059
- )}
1060
- {querySetupState.currentProject &&
1061
- querySetupState.currentVersionId &&
1062
- querySetupState.surveyMappingRuntimeCompatibilityState
1063
- .hasSucceeded && (
1064
- <>
1065
- <div className="query-setup__wizard__group">
1066
- <div className="query-setup__wizard__group__title">
1067
- Mapping
1068
- </div>
1069
- <CustomSelectorInput
1070
- className="query-setup__wizard__selector"
1071
- options={mappingOptions}
1072
- disabled={!mappingOptions.length}
1073
- onChange={onMappingOptionChange}
1074
- value={selectedMappingOption}
1075
- placeholder={mappingSelectorPlaceholder}
1076
- isClearable={true}
1077
- escapeClearsValue={true}
1078
- darkMode={true}
1079
- />
1080
- </div>
1081
- <div className="query-setup__wizard__group">
1082
- <div className="query-setup__wizard__group__title">
1083
- Runtime
1084
- </div>
1085
- <CustomSelectorInput
1086
- className="query-setup__wizard__selector"
1087
- options={runtimeOptions}
1088
- disabled={
1089
- !mappingOptions.length ||
1090
- !querySetupState.currentMapping
1091
- }
1092
- onChange={onRuntimeOptionChange}
1093
- value={selectedRuntimeOption}
1094
- placeholder={runtimeSelectorPlaceholder}
1095
- isClearable={true}
1096
- escapeClearsValue={true}
1097
- darkMode={true}
1098
- />
1099
- </div>
1100
- </>
1101
- )}
1102
- </div>
1103
- </div>
1104
- </div>
1105
- );
1106
- },
1107
- );
1108
-
1109
- const QuerySetupLandingPage = observer(() => {
1110
- const setupStore = useQuerySetupStore();
1111
- const extraQuerySetupOptions = setupStore.pluginManager
1112
- .getApplicationPlugins()
1113
- .flatMap(
1114
- (plugin) =>
1115
- plugin.getExtraQuerySetupOptionRendererConfigurations?.() ?? [],
1116
- )
1117
- .filter(isNonNullable)
1118
- .map((config) => (
1119
- <Fragment key={config.key}>{config.renderer(setupStore)}</Fragment>
1120
- ));
1121
- const editQuery = (): void =>
1122
- setupStore.setSetupState(new EditExistingQuerySetupState(setupStore));
1123
- const updateServiceQuery = (): void =>
1124
- setupStore.setSetupState(
1125
- new UpdateExistingServiceQuerySetupState(setupStore),
1126
- );
1127
- const cloneServiceQuery = (): void =>
1128
- setupStore.setSetupState(new CloneServiceQuerySetupState(setupStore));
1129
- const createMappingQuery = (): void =>
1130
- setupStore.setSetupState(new CreateMappingQuerySetupState(setupStore));
1131
- const loadServiceQueryFromProject = (): void =>
1132
- setupStore.setSetupState(new LoadProjectServiceQuerySetupState(setupStore));
1133
- const productionizeQuery = (): void =>
1134
- setupStore.setSetupState(new QueryProductionizationSetupState(setupStore));
1135
-
1136
- useEffect(() => {
1137
- setupStore.initialize();
1138
- }, [setupStore]);
1139
-
1140
- return (
1141
- <>
1142
- <div className="query-setup__landing-page">
362
+ <>
1143
363
  <PanelLoadingIndicator isLoading={setupStore.initState.isInProgress} />
1144
364
  {setupStore.initState.hasCompleted && (
1145
- <>
1146
- <div className="query-setup__landing-page__title">
1147
- What do you want to do today
1148
- <QuestionCircleIcon
1149
- className="query-setup__landing-page__title__question-mark"
1150
- title="Choose one of the option below to start"
1151
- />
1152
- </div>
1153
- <div className="query-setup__landing-page__options">
1154
- <button
1155
- className="query-setup__landing-page__option query-setup__landing-page__option--existing-query"
1156
- onClick={editQuery}
1157
- >
1158
- <div className="query-setup__landing-page__option__icon">
1159
- <ManageSearchIcon className="query-setup__landing-page__icon--search" />
1160
- </div>
1161
- <div className="query-setup__landing-page__option__label">
1162
- Load an existing query
1163
- </div>
1164
- </button>
1165
- {extraQuerySetupOptions}
1166
- <button
1167
- className="query-setup__landing-page__option query-setup__landing-page__option--advanced query-setup__landing-page__option--create-query"
1168
- onClick={createMappingQuery}
1169
- >
1170
- <div className="query-setup__landing-page__option__icon">
1171
- <PlusIcon />
1172
- </div>
1173
- <div className="query-setup__landing-page__option__label">
1174
- Create new query on a mapping
1175
- </div>
1176
- </button>
1177
- <button
1178
- className="query-setup__landing-page__option query-setup__landing-page__option--advanced query-setup__landing-page__option--service-query"
1179
- onClick={cloneServiceQuery}
1180
- >
1181
- <div className="query-setup__landing-page__option__icon">
1182
- <RobotIcon />
1183
- </div>
1184
- <div className="query-setup__landing-page__option__label">
1185
- Clone an existing service query
1186
- </div>
1187
- </button>
1188
- <button
1189
- className="query-setup__landing-page__option query-setup__landing-page__option--advanced query-setup__landing-page__option--service-query"
1190
- onClick={updateServiceQuery}
1191
- >
1192
- <div className="query-setup__landing-page__option__icon">
1193
- <DroidIcon />
1194
- </div>
1195
- <div className="query-setup__landing-page__option__label">
1196
- Update an existing service query
1197
- </div>
1198
- </button>
1199
- <button
1200
- className="query-setup__landing-page__option query-setup__landing-page__option--advanced query-setup__landing-page__option--service-query"
1201
- onClick={loadServiceQueryFromProject}
1202
- >
1203
- <div className="query-setup__landing-page__option__icon">
1204
- <DroidIcon />
1205
- </div>
1206
- <div className="query-setup__landing-page__option__label">
1207
- Load service query from a project
1208
- </div>
1209
- </button>
1210
- <button
1211
- className="query-setup__landing-page__option query-setup__landing-page__option--advanced query-setup__landing-page__option--service-query"
1212
- onClick={productionizeQuery}
1213
- >
1214
- <div className="query-setup__landing-page__option__icon">
1215
- <ArrowCirceUpIcon />
1216
- </div>
1217
- <div className="query-setup__landing-page__option__label">
1218
- Productionize an existing query
1219
- </div>
1220
- </button>
1221
- </div>
1222
- </>
365
+ <div className="query-setup">{children}</div>
1223
366
  )}
1224
- </div>
1225
- </>
1226
- );
1227
- });
1228
-
1229
- export const QuerySetup = withQuerySetupStore(
1230
- observer(() => {
1231
- const setupStore = useQuerySetupStore();
1232
- const querySetupState = setupStore.querySetupState;
1233
- const renderQuerySetupScreen = (
1234
- setupState: QuerySetupState,
1235
- ): React.ReactNode => {
1236
- if (setupState instanceof EditExistingQuerySetupState) {
1237
- return <EditExistingQuerySetup querySetupState={setupState} />;
1238
- } else if (setupState instanceof CreateMappingQuerySetupState) {
1239
- return <CreateMappingQuerySetup querySetupState={setupState} />;
1240
- } else if (setupState instanceof CloneServiceQuerySetupState) {
1241
- return <CloneServiceQuerySetup querySetupState={setupState} />;
1242
- } else if (setupState instanceof UpdateExistingServiceQuerySetupState) {
1243
- return <UpdateExistingServiceQuerySetup querySetupState={setupState} />;
1244
- } else if (setupState instanceof LoadProjectServiceQuerySetupState) {
1245
- return <LoadProjectServiceQuerySetup querySetupState={setupState} />;
1246
- } else if (setupState instanceof QueryProductionizationSetupState) {
1247
- return <QueryProductionizationSetup querySetupState={setupState} />;
1248
- }
1249
- const extraQuerySetupRenderers = setupStore.pluginManager
1250
- .getApplicationPlugins()
1251
- .flatMap((plugin) => plugin.getExtraQuerySetupRenderers?.() ?? []);
1252
- for (const querySetupRenderer of extraQuerySetupRenderers) {
1253
- const elementEditor = querySetupRenderer(setupState);
1254
- if (elementEditor) {
1255
- return elementEditor;
1256
- }
1257
- }
1258
- return null;
1259
- };
1260
-
1261
- return (
1262
- <div className="query-setup">
1263
- {!querySetupState && <QuerySetupLandingPage />}
1264
- {querySetupState && renderQuerySetupScreen(querySetupState)}
1265
- </div>
367
+ </>
1266
368
  );
1267
- }),
369
+ },
1268
370
  );