@finos/legend-query-builder 4.14.36 → 4.14.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/lib/__lib__/QueryBuilderDocumentation.d.ts +2 -1
  2. package/lib/__lib__/QueryBuilderDocumentation.d.ts.map +1 -1
  3. package/lib/__lib__/QueryBuilderDocumentation.js +1 -0
  4. package/lib/__lib__/QueryBuilderDocumentation.js.map +1 -1
  5. package/lib/components/QueryBuilder.d.ts.map +1 -1
  6. package/lib/components/QueryBuilder.js +26 -9
  7. package/lib/components/QueryBuilder.js.map +1 -1
  8. package/lib/components/QueryBuilderSideBar.d.ts.map +1 -1
  9. package/lib/components/QueryBuilderSideBar.js +3 -3
  10. package/lib/components/QueryBuilderSideBar.js.map +1 -1
  11. package/lib/components/QueryBuilderTextEditor.d.ts.map +1 -1
  12. package/lib/components/QueryBuilderTextEditor.js +17 -3
  13. package/lib/components/QueryBuilderTextEditor.js.map +1 -1
  14. package/lib/components/explorer/QueryBuilderMilestoningEditor.d.ts +5 -0
  15. package/lib/components/explorer/QueryBuilderMilestoningEditor.d.ts.map +1 -1
  16. package/lib/components/explorer/QueryBuilderMilestoningEditor.js +10 -5
  17. package/lib/components/explorer/QueryBuilderMilestoningEditor.js.map +1 -1
  18. package/lib/components/fetch-structure/QueryBuilderResultModifierPanel.d.ts.map +1 -1
  19. package/lib/components/fetch-structure/QueryBuilderResultModifierPanel.js +35 -3
  20. package/lib/components/fetch-structure/QueryBuilderResultModifierPanel.js.map +1 -1
  21. package/lib/components/fetch-structure/QueryBuilderTDSPanel.d.ts.map +1 -1
  22. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js +11 -1
  23. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js.map +1 -1
  24. package/lib/components/result/QueryBuilderResultPanel.d.ts.map +1 -1
  25. package/lib/components/result/QueryBuilderResultPanel.js +21 -8
  26. package/lib/components/result/QueryBuilderResultPanel.js.map +1 -1
  27. package/lib/components/workflows/ClassQueryBuilder.d.ts.map +1 -1
  28. package/lib/components/workflows/ClassQueryBuilder.js +3 -3
  29. package/lib/components/workflows/ClassQueryBuilder.js.map +1 -1
  30. package/lib/components/workflows/MappingQueryBuilder.d.ts.map +1 -1
  31. package/lib/components/workflows/MappingQueryBuilder.js +3 -3
  32. package/lib/components/workflows/MappingQueryBuilder.js.map +1 -1
  33. package/lib/components/workflows/ServiceQueryBuilder.d.ts.map +1 -1
  34. package/lib/components/workflows/ServiceQueryBuilder.js +3 -3
  35. package/lib/components/workflows/ServiceQueryBuilder.js.map +1 -1
  36. package/lib/index.css +2 -2
  37. package/lib/index.css.map +1 -1
  38. package/lib/package.json +1 -1
  39. package/lib/stores/QueryBuilderTextEditorState.d.ts +4 -1
  40. package/lib/stores/QueryBuilderTextEditorState.d.ts.map +1 -1
  41. package/lib/stores/QueryBuilderTextEditorState.js +19 -2
  42. package/lib/stores/QueryBuilderTextEditorState.js.map +1 -1
  43. package/lib/stores/QueryBuilder_LegendApplicationPlugin_Extension.d.ts +16 -0
  44. package/lib/stores/QueryBuilder_LegendApplicationPlugin_Extension.d.ts.map +1 -1
  45. package/lib/stores/QueryLoaderState.d.ts +2 -2
  46. package/lib/stores/QueryLoaderState.d.ts.map +1 -1
  47. package/lib/stores/QueryLoaderState.js +3 -3
  48. package/lib/stores/QueryLoaderState.js.map +1 -1
  49. package/lib/stores/explorer/QueryFunctionsExplorerState.d.ts +1 -1
  50. package/lib/stores/explorer/QueryFunctionsExplorerState.d.ts.map +1 -1
  51. package/lib/stores/explorer/QueryFunctionsExplorerState.js +2 -1
  52. package/lib/stores/explorer/QueryFunctionsExplorerState.js.map +1 -1
  53. package/lib/stores/fetch-structure/tds/QueryBuilderTDSState.d.ts +1 -0
  54. package/lib/stores/fetch-structure/tds/QueryBuilderTDSState.d.ts.map +1 -1
  55. package/lib/stores/fetch-structure/tds/QueryBuilderTDSState.js +8 -0
  56. package/lib/stores/fetch-structure/tds/QueryBuilderTDSState.js.map +1 -1
  57. package/lib/stores/milestoning/QueryBuilderMilestoningState.d.ts.map +1 -1
  58. package/lib/stores/milestoning/QueryBuilderMilestoningState.js +9 -3
  59. package/lib/stores/milestoning/QueryBuilderMilestoningState.js.map +1 -1
  60. package/package.json +5 -5
  61. package/src/__lib__/QueryBuilderDocumentation.ts +1 -0
  62. package/src/components/QueryBuilder.tsx +100 -17
  63. package/src/components/QueryBuilderSideBar.tsx +3 -2
  64. package/src/components/QueryBuilderTextEditor.tsx +46 -11
  65. package/src/components/explorer/QueryBuilderMilestoningEditor.tsx +60 -46
  66. package/src/components/fetch-structure/QueryBuilderResultModifierPanel.tsx +53 -3
  67. package/src/components/fetch-structure/QueryBuilderTDSPanel.tsx +116 -19
  68. package/src/components/result/QueryBuilderResultPanel.tsx +84 -29
  69. package/src/components/workflows/ClassQueryBuilder.tsx +3 -2
  70. package/src/components/workflows/MappingQueryBuilder.tsx +3 -2
  71. package/src/components/workflows/ServiceQueryBuilder.tsx +7 -3
  72. package/src/stores/QueryBuilderTextEditorState.ts +20 -2
  73. package/src/stores/QueryBuilder_LegendApplicationPlugin_Extension.ts +21 -0
  74. package/src/stores/QueryLoaderState.ts +5 -7
  75. package/src/stores/explorer/QueryFunctionsExplorerState.ts +6 -2
  76. package/src/stores/fetch-structure/tds/QueryBuilderTDSState.ts +11 -0
  77. package/src/stores/milestoning/QueryBuilderMilestoningState.ts +11 -7
@@ -278,12 +278,10 @@ const AllVersionsInRangelMilestoningParametersEditor = observer(
278
278
  },
279
279
  );
280
280
 
281
- export const MilestoningParametersEditor = observer(
281
+ export const MilestoningParametersEditorContent = observer(
282
282
  (props: { queryBuilderState: QueryBuilderState }) => {
283
283
  const { queryBuilderState } = props;
284
- const applicationStore = queryBuilderState.applicationStore;
285
284
  const milestoningState = queryBuilderState.milestoningState;
286
- const close = (): void => milestoningState.setShowMilestoningEditor(false);
287
285
  const isCompatibleMilestoningParameter = (
288
286
  variable: VariableExpression,
289
287
  ): boolean =>
@@ -292,6 +290,62 @@ export const MilestoningParametersEditor = observer(
292
290
  variable.genericType?.value.rawType.name === PRIMITIVE_TYPE.DATE ||
293
291
  variable.genericType?.value.rawType.name === PRIMITIVE_TYPE.DATETIME;
294
292
 
293
+ return (
294
+ <>
295
+ {milestoningState.isCurrentClassMilestoned && (
296
+ <PanelFormBooleanField
297
+ isReadOnly={false}
298
+ value={milestoningState.isAllVersionsEnabled}
299
+ name="all Versions"
300
+ prompt="Query All Milestoned Versions of the Root Class"
301
+ update={(value: boolean | undefined): void =>
302
+ milestoningState.setAllVersions(value)
303
+ }
304
+ />
305
+ )}
306
+ {milestoningState.isAllVersionsEnabled &&
307
+ milestoningState.isCurrentClassSupportsVersionsInRange && (
308
+ <>
309
+ <PanelFormBooleanField
310
+ isReadOnly={false}
311
+ value={milestoningState.isAllVersionsInRangeEnabled}
312
+ name=" All Versions In Range"
313
+ prompt="Optionally apply a date range to get All Versions for"
314
+ update={(value: boolean | undefined): void =>
315
+ milestoningState.setAllVersionsInRange(value)
316
+ }
317
+ />
318
+
319
+ {milestoningState.isAllVersionsInRangeEnabled && (
320
+ <AllVersionsInRangelMilestoningParametersEditor
321
+ queryBuilderState={queryBuilderState}
322
+ />
323
+ )}
324
+ </>
325
+ )}
326
+ <TemporalMilestoningEditor queryBuilderState={queryBuilderState} />
327
+ <PanelFormSection>
328
+ <div className="panel__content__form__section__header__label">
329
+ List of compatible milestoning parameters
330
+ </div>
331
+ </PanelFormSection>
332
+ <div className="panel__content__form__section__list__items">
333
+ <VariableSelector
334
+ queryBuilderState={queryBuilderState}
335
+ filterBy={isCompatibleMilestoningParameter}
336
+ />
337
+ </div>
338
+ </>
339
+ );
340
+ },
341
+ );
342
+
343
+ export const MilestoningParametersEditor = observer(
344
+ (props: { queryBuilderState: QueryBuilderState }) => {
345
+ const { queryBuilderState } = props;
346
+ const applicationStore = queryBuilderState.applicationStore;
347
+ const milestoningState = queryBuilderState.milestoningState;
348
+ const close = (): void => milestoningState.setShowMilestoningEditor(false);
295
349
  return (
296
350
  <Dialog
297
351
  open={milestoningState.showMilestoningEditor}
@@ -310,49 +364,9 @@ export const MilestoningParametersEditor = observer(
310
364
  >
311
365
  <ModalHeader title="Milestoning Parameters" />
312
366
  <ModalBody className="query-builder__variables__modal__body">
313
- {milestoningState.isCurrentClassMilestoned && (
314
- <PanelFormBooleanField
315
- isReadOnly={false}
316
- value={milestoningState.isAllVersionsEnabled}
317
- name="all Versions"
318
- prompt="Query All Milestoned Versions of the Root Class"
319
- update={(value: boolean | undefined): void =>
320
- milestoningState.setAllVersions(value)
321
- }
322
- />
323
- )}
324
- {milestoningState.isAllVersionsEnabled &&
325
- milestoningState.isCurrentClassSupportsVersionsInRange && (
326
- <>
327
- <PanelFormBooleanField
328
- isReadOnly={false}
329
- value={milestoningState.isAllVersionsInRangeEnabled}
330
- name=" All Versions In Range"
331
- prompt="Optionally apply a date range to get All Versions for"
332
- update={(value: boolean | undefined): void =>
333
- milestoningState.setAllVersionsInRange(value)
334
- }
335
- />
336
-
337
- {milestoningState.isAllVersionsInRangeEnabled && (
338
- <AllVersionsInRangelMilestoningParametersEditor
339
- queryBuilderState={queryBuilderState}
340
- />
341
- )}
342
- </>
343
- )}
344
- <TemporalMilestoningEditor queryBuilderState={queryBuilderState} />
345
- <PanelFormSection>
346
- <div className="panel__content__form__section__header__label">
347
- List of compatible milestoning parameters
348
- </div>
349
- </PanelFormSection>
350
- <div className="panel__content__form__section__list__items">
351
- <VariableSelector
352
- queryBuilderState={queryBuilderState}
353
- filterBy={isCompatibleMilestoningParameter}
354
- />
355
- </div>
367
+ <MilestoningParametersEditorContent
368
+ queryBuilderState={queryBuilderState}
369
+ />
356
370
  </ModalBody>
357
371
  <ModalFooter>
358
372
  <ModalFooterButton text="Close" onClick={close} type="secondary" />
@@ -48,7 +48,7 @@ import { useApplicationStore } from '@finos/legend-application';
48
48
  import type { QueryBuilderTDSState } from '../../stores/fetch-structure/tds/QueryBuilderTDSState.js';
49
49
  import type { QueryBuilderTDSColumnState } from '../../stores/fetch-structure/tds/QueryBuilderTDSColumnState.js';
50
50
  import { COLUMN_SORT_TYPE } from '../../graph/QueryBuilderMetaModelConst.js';
51
- import { useCallback, useEffect, useState } from 'react';
51
+ import { useCallback, useEffect, useRef, useState } from 'react';
52
52
  import type { QueryBuilderProjectionColumnState } from '../../stores/fetch-structure/tds/projection/QueryBuilderProjectionColumnState.js';
53
53
  import { QUERY_BUILDER_TEST_ID } from '../../__lib__/QueryBuilderTesting.js';
54
54
  import { VariableSelector } from '../shared/QueryBuilderVariableSelector.js';
@@ -65,6 +65,7 @@ import {
65
65
  type QueryBuilderVariableDragSource,
66
66
  } from '../shared/BasicValueSpecificationEditor.js';
67
67
  import { useDrop } from 'react-dnd';
68
+ import { MilestoningParametersEditorContent } from '../explorer/QueryBuilderMilestoningEditor.js';
68
69
 
69
70
  const ColumnSortEditor = observer(
70
71
  (props: {
@@ -270,6 +271,31 @@ export const QueryResultModifierModal = observer(
270
271
  deepClone(watermarkState.value),
271
272
  );
272
273
 
274
+ //milestoning config
275
+ const milestoningState = tdsState.queryBuilderState.milestoningState;
276
+ const businessDate = useRef(milestoningState.businessDate);
277
+ const processingDate = useRef(milestoningState.processingDate);
278
+ const isAllVersionsEnabled = useRef(milestoningState.isAllVersionsEnabled);
279
+ const isAllVersionsInRangeEnabled = useRef(
280
+ milestoningState.isAllVersionsInRangeEnabled,
281
+ );
282
+ const startDate = useRef(milestoningState.startDate);
283
+ const endDate = useRef(milestoningState.endDate);
284
+
285
+ const resetMilestoningToInitial = (): void => {
286
+ if (isAllVersionsInRangeEnabled.current) {
287
+ milestoningState.setAllVersionsInRange(
288
+ isAllVersionsInRangeEnabled.current,
289
+ );
290
+ milestoningState.setStartDate(startDate.current);
291
+ milestoningState.setEndDate(endDate.current);
292
+ } else {
293
+ milestoningState.setAllVersions(isAllVersionsEnabled.current);
294
+ }
295
+ milestoningState.setBusinessDate(businessDate.current);
296
+ milestoningState.setProcessingDate(processingDate.current);
297
+ };
298
+
273
299
  // Sync temp state with tdsState when modal is opened/closed
274
300
  useEffect(() => {
275
301
  setSortColumns(cloneSortColumnStateArray(stateSortColumns));
@@ -287,7 +313,10 @@ export const QueryResultModifierModal = observer(
287
313
  ]);
288
314
 
289
315
  // Handle user actions
290
- const closeModal = (): void => resultSetModifierState.setShowModal(false);
316
+ const closeModal = (): void => {
317
+ resetMilestoningToInitial();
318
+ resultSetModifierState.setShowModal(false);
319
+ };
291
320
  const applyChanges = (): void => {
292
321
  resultSetModifierState.setSortColumns(sortColumns);
293
322
  resultSetModifierState.setDistinct(distinct);
@@ -299,6 +328,13 @@ export const QueryResultModifierModal = observer(
299
328
  }
300
329
  resultSetModifierState.setShowModal(false);
301
330
  watermarkState.setValue(watermarkValue);
331
+ businessDate.current = milestoningState.businessDate;
332
+ processingDate.current = milestoningState.processingDate;
333
+ isAllVersionsEnabled.current = milestoningState.isAllVersionsEnabled;
334
+ isAllVersionsInRangeEnabled.current =
335
+ milestoningState.isAllVersionsInRangeEnabled;
336
+ startDate.current = milestoningState.startDate;
337
+ endDate.current = milestoningState.endDate;
302
338
  };
303
339
 
304
340
  const handleLimitResultsChange: React.ChangeEventHandler<
@@ -408,9 +444,23 @@ export const QueryResultModifierModal = observer(
408
444
  }
409
445
  className="editor-modal query-builder__projection__modal"
410
446
  >
411
- <ModalHeader title="Result Set Modifier" />
447
+ <ModalHeader title="Query Options" />
412
448
  <ModalBody className="query-builder__projection__modal__body">
413
449
  <div className="query-builder__projection__options">
450
+ {tdsState.queryBuilderState.milestoningState
451
+ .isMilestonedQuery && (
452
+ <>
453
+ <div className="query-builder__projection__options__section-name">
454
+ Milestoning
455
+ </div>
456
+ <MilestoningParametersEditorContent
457
+ queryBuilderState={tdsState.queryBuilderState}
458
+ />
459
+ <div className="query-builder__projection__options__section-name">
460
+ Other
461
+ </div>
462
+ </>
463
+ )}
414
464
  <ColumnsSortEditor
415
465
  projectionColumns={tdsState.projectionColumns}
416
466
  sortColumns={sortColumns}
@@ -1148,27 +1148,118 @@ export const QueryBuilderTDSPanel = observer(
1148
1148
  >
1149
1149
  <div className="query-builder__projection__result-modifier-prompt__header">
1150
1150
  <button
1151
- className="query-builder__projection__result-modifier-prompt__header__label"
1151
+ className="query-builder__projection__result-modifier-prompt__header__label editable-value"
1152
1152
  onClick={openResultSetModifierEditor}
1153
- title="Configure result set modifiers..."
1153
+ title="Configure Query Options..."
1154
1154
  >
1155
1155
  <CogIcon className="query-builder__projection__result-modifier-prompt__header__label__icon" />
1156
1156
  <div className="query-builder__projection__result-modifier-prompt__header__label__title">
1157
- Query Options
1157
+ {tdsState.isQueryOptionsSet
1158
+ ? 'Query Options'
1159
+ : 'Set Query Options'}
1158
1160
  </div>
1159
1161
  </button>
1162
+ <div className="query-builder__projection__result-modifier-prompt__divider">
1163
+ {tdsState.isQueryOptionsSet && ' - '}
1164
+ </div>
1165
+ {tdsState.queryBuilderState.milestoningState.businessDate && (
1166
+ <div className="query-builder__projection__result-modifier-prompt__group">
1167
+ <div className="query-builder__projection__result-modifier-prompt__group__label">
1168
+ Business Date
1169
+ </div>
1170
+ <button
1171
+ className="query-builder__projection__result-modifier-prompt__header__label editable-value"
1172
+ onClick={openResultSetModifierEditor}
1173
+ >
1174
+ <div className="query-builder__projection__result-modifier-prompt__header__label__title">
1175
+ {getNameOfValueSpecification(
1176
+ tdsState.queryBuilderState.milestoningState
1177
+ .businessDate,
1178
+ tdsState.queryBuilderState,
1179
+ )}
1180
+ </div>
1181
+ </button>
1182
+ </div>
1183
+ )}
1184
+ {tdsState.queryBuilderState.milestoningState.processingDate && (
1185
+ <div className="query-builder__projection__result-modifier-prompt__group">
1186
+ <div className="query-builder__projection__result-modifier-prompt__group__label">
1187
+ Processing Date
1188
+ </div>
1189
+ <button
1190
+ className="query-builder__projection__result-modifier-prompt__header__label editable-value"
1191
+ onClick={openResultSetModifierEditor}
1192
+ >
1193
+ <div className="query-builder__projection__result-modifier-prompt__header__label__title">
1194
+ {getNameOfValueSpecification(
1195
+ tdsState.queryBuilderState.milestoningState
1196
+ .processingDate,
1197
+ tdsState.queryBuilderState,
1198
+ )}
1199
+ </div>
1200
+ </button>
1201
+ </div>
1202
+ )}
1203
+ {tdsState.queryBuilderState.milestoningState
1204
+ .isAllVersionsEnabled &&
1205
+ !tdsState.queryBuilderState.milestoningState
1206
+ .isAllVersionsInRangeEnabled && (
1207
+ <div className="query-builder__projection__result-modifier-prompt__group">
1208
+ <div className="query-builder__projection__result-modifier-prompt__group__label">
1209
+ All Versions
1210
+ </div>
1211
+ <button
1212
+ className="query-builder__projection__result-modifier-prompt__header__label editable-value"
1213
+ onClick={openResultSetModifierEditor}
1214
+ >
1215
+ <div className="query-builder__projection__result-modifier-prompt__header__label__title">
1216
+ Yes
1217
+ </div>
1218
+ </button>
1219
+ </div>
1220
+ )}
1221
+ {tdsState.queryBuilderState.milestoningState
1222
+ .isAllVersionsInRangeEnabled &&
1223
+ tdsState.queryBuilderState.milestoningState.startDate &&
1224
+ tdsState.queryBuilderState.milestoningState.endDate && (
1225
+ <div className="query-builder__projection__result-modifier-prompt__group">
1226
+ <div className="query-builder__projection__result-modifier-prompt__group__label">
1227
+ All Versions
1228
+ </div>
1229
+ <button
1230
+ className="query-builder__projection__result-modifier-prompt__header__label editable-value"
1231
+ onClick={openResultSetModifierEditor}
1232
+ >
1233
+ <div className="query-builder__projection__result-modifier-prompt__header__label__title">
1234
+ (
1235
+ {getNameOfValueSpecification(
1236
+ tdsState.queryBuilderState.milestoningState.startDate,
1237
+ tdsState.queryBuilderState,
1238
+ )}{' '}
1239
+ -{' '}
1240
+ {getNameOfValueSpecification(
1241
+ tdsState.queryBuilderState.milestoningState.endDate,
1242
+ tdsState.queryBuilderState,
1243
+ )}
1244
+ )
1245
+ </div>
1246
+ </button>
1247
+ </div>
1248
+ )}
1160
1249
  </div>
1161
1250
  {tdsState.resultSetModifierState.limit && (
1162
1251
  <div className="query-builder__projection__result-modifier-prompt__group">
1163
1252
  <div className="query-builder__projection__result-modifier-prompt__group__label">
1164
1253
  Max Rows
1165
1254
  </div>
1166
- <div
1167
- className="query-builder__projection__result-modifier-prompt__group__content"
1255
+ <button
1256
+ className="query-builder__projection__result-modifier-prompt__header__label editable-value"
1168
1257
  onClick={openResultSetModifierEditor}
1169
1258
  >
1170
- {tdsState.resultSetModifierState.limit}
1171
- </div>
1259
+ <div className="query-builder__projection__result-modifier-prompt__header__label__title">
1260
+ {tdsState.resultSetModifierState.limit}
1261
+ </div>
1262
+ </button>
1172
1263
  </div>
1173
1264
  )}
1174
1265
  {tdsState.resultSetModifierState.distinct && (
@@ -1176,12 +1267,14 @@ export const QueryBuilderTDSPanel = observer(
1176
1267
  <div className="query-builder__projection__result-modifier-prompt__group__label">
1177
1268
  Eliminate Duplicate Rows
1178
1269
  </div>
1179
- <div
1180
- className="query-builder__projection__result-modifier-prompt__group__content"
1270
+ <button
1271
+ className="query-builder__projection__result-modifier-prompt__header__label editable-value"
1181
1272
  onClick={openResultSetModifierEditor}
1182
1273
  >
1183
- Yes
1184
- </div>
1274
+ <div className="query-builder__projection__result-modifier-prompt__header__label__title">
1275
+ Yes
1276
+ </div>
1277
+ </button>
1185
1278
  </div>
1186
1279
  )}
1187
1280
  {tdsState.resultSetModifierState.sortColumns.length > 0 && (
@@ -1191,13 +1284,15 @@ export const QueryBuilderTDSPanel = observer(
1191
1284
  </div>
1192
1285
  {tdsState.resultSetModifierState.sortColumns.map(
1193
1286
  (columnState) => (
1194
- <div
1195
- className="query-builder__projection__result-modifier-prompt__group__content"
1287
+ <button
1196
1288
  key={columnState.columnState.uuid}
1289
+ className="query-builder__projection__result-modifier-prompt__header__label editable-value"
1197
1290
  onClick={openResultSetModifierEditor}
1198
1291
  >
1199
- {`${columnState.columnState.columnName} ${columnState.sortType}`}
1200
- </div>
1292
+ <div className="query-builder__projection__result-modifier-prompt__header__label__title">
1293
+ {`${columnState.columnState.columnName} ${columnState.sortType}`}
1294
+ </div>
1295
+ </button>
1201
1296
  ),
1202
1297
  )}
1203
1298
  </div>
@@ -1207,12 +1302,14 @@ export const QueryBuilderTDSPanel = observer(
1207
1302
  <div className="query-builder__projection__result-modifier-prompt__group__label">
1208
1303
  Slice
1209
1304
  </div>
1210
- <div
1211
- className="query-builder__projection__result-modifier-prompt__group__content"
1305
+ <button
1306
+ className="query-builder__projection__result-modifier-prompt__header__label editable-value"
1212
1307
  onClick={openResultSetModifierEditor}
1213
1308
  >
1214
- {`${tdsState.resultSetModifierState.slice[0]},${tdsState.resultSetModifierState.slice[1]}`}
1215
- </div>
1309
+ <div className="query-builder__projection__result-modifier-prompt__header__label__title">
1310
+ {`${tdsState.resultSetModifierState.slice[0]},${tdsState.resultSetModifierState.slice[1]}`}
1311
+ </div>
1312
+ </button>
1216
1313
  </div>
1217
1314
  )}
1218
1315
  {tdsState.queryBuilderState.watermarkState.value && (
@@ -16,7 +16,6 @@
16
16
 
17
17
  import {
18
18
  BlankPanelContent,
19
- PanelLoadingIndicator,
20
19
  PlayIcon,
21
20
  DropdownMenu,
22
21
  MenuContent,
@@ -37,6 +36,14 @@ import {
37
36
  PanelDivider,
38
37
  SquareIcon,
39
38
  CheckSquareIcon,
39
+ MenuContentItemIcon,
40
+ MenuContentItemLabel,
41
+ ChartIcon,
42
+ CsvIcon,
43
+ DebugIcon,
44
+ ReportIcon,
45
+ CubesLoadingIndicatorIcon,
46
+ CubesLoadingIndicator,
40
47
  } from '@finos/legend-art';
41
48
  import { observer } from 'mobx-react-lite';
42
49
  import { flowResult } from 'mobx';
@@ -306,6 +313,31 @@ export const QueryBuilderResultPanel = observer(
306
313
  !queryBuilderState.isLocalModeEnabled,
307
314
  );
308
315
  };
316
+
317
+ const extraExportMenuContentItems = applicationStore.pluginManager
318
+ .getApplicationPlugins()
319
+ .flatMap(
320
+ (plugin) =>
321
+ (
322
+ plugin as QueryBuilder_LegendApplicationPlugin_Extension
323
+ ).getExtraQueryBuilderExportMenuActionConfigurations?.() ?? [],
324
+ )
325
+ .map((item) => (
326
+ <MenuContentItem
327
+ key={item.key}
328
+ title={item.title ?? ''}
329
+ onClick={() => {
330
+ item.onClick(queryBuilderState);
331
+ }}
332
+ >
333
+ {item.icon && <MenuContentItemIcon>{item.icon}</MenuContentItemIcon>}
334
+ <MenuContentItemLabel>{item.label}</MenuContentItemLabel>
335
+ </MenuContentItem>
336
+ ));
337
+
338
+ const isLoading =
339
+ resultState.isRunningQuery || resultState.isGeneratingPlan;
340
+
309
341
  return (
310
342
  <div
311
343
  data-testid={QUERY_BUILDER_TEST_ID.QUERY_BUILDER_RESULT_PANEL}
@@ -355,7 +387,7 @@ export const QueryBuilderResultPanel = observer(
355
387
 
356
388
  <div className="panel__header">
357
389
  <div className="panel__header__title">
358
- <div className="panel__header__title__label">result</div>
390
+ <div className="panel__header__title__label">results</div>
359
391
  {executedSql && (
360
392
  <Button
361
393
  onClick={() => setShowSqlModal(true)}
@@ -429,7 +461,7 @@ export const QueryBuilderResultPanel = observer(
429
461
  {allowSettingPreviewLimit && (
430
462
  <div className="query-builder__result__limit">
431
463
  <div className="query-builder__result__limit__label">
432
- preview limit
464
+ preview row limit
433
465
  </div>
434
466
  <input
435
467
  ref={inputRef}
@@ -448,22 +480,20 @@ export const QueryBuilderResultPanel = observer(
448
480
  <div className="query-builder__result__execute-btn btn__dropdown-combo btn__dropdown-combo--primary">
449
481
  {resultState.isRunningQuery ? (
450
482
  <button
451
- className="btn__dropdown-combo__canceler"
483
+ className="btn__dropdown-combo__canceler query-builder__result__execute-btn__btn"
452
484
  onClick={cancelQuery}
453
485
  tabIndex={-1}
454
486
  disabled={!isQueryValid}
455
487
  >
456
- <div className="btn--dark btn--caution btn__dropdown-combo__canceler__label">
457
- <PauseCircleIcon className="btn__dropdown-combo__canceler__label__icon" />
458
- <div className="btn__dropdown-combo__canceler__label__title">
459
- Stop
460
- </div>
488
+ <div className="btn--dark btn--caution btn__dropdown-combo__canceler__label query-builder__result__execute-btn__btn">
489
+ <PauseCircleIcon />
490
+ Stop
461
491
  </div>
462
492
  </button>
463
493
  ) : (
464
494
  <>
465
495
  <button
466
- className="btn__dropdown-combo__label"
496
+ className="btn__dropdown-combo__label query-builder__result__execute-btn__btn query-builder__result__execute-btn__btn--green"
467
497
  onClick={runQuery}
468
498
  tabIndex={-1}
469
499
  title={
@@ -475,13 +505,11 @@ export const QueryBuilderResultPanel = observer(
475
505
  }
476
506
  disabled={isRunQueryDisabled}
477
507
  >
478
- <PlayIcon className="btn__dropdown-combo__label__icon" />
479
- <div className="btn__dropdown-combo__label__title">
480
- Run Query
481
- </div>
508
+ <PlayIcon />
509
+ Run Query
482
510
  </button>
483
511
  <DropdownMenu
484
- className="btn__dropdown-combo__dropdown-btn"
512
+ className="btn__dropdown-combo__dropdown-btn query-builder__result__execute-btn__btn query-builder__result__execute-btn__btn--green"
485
513
  disabled={isRunQueryDisabled}
486
514
  content={
487
515
  <MenuContent>
@@ -490,14 +518,22 @@ export const QueryBuilderResultPanel = observer(
490
518
  onClick={generatePlan}
491
519
  disabled={isRunQueryDisabled}
492
520
  >
493
- Generate Plan
521
+ <MenuContentItemIcon>
522
+ <ReportIcon />
523
+ </MenuContentItemIcon>
524
+ <MenuContentItemLabel>
525
+ Generate Plan
526
+ </MenuContentItemLabel>
494
527
  </MenuContentItem>
495
528
  <MenuContentItem
496
529
  className="btn__dropdown-combo__option"
497
530
  onClick={debugPlanGeneration}
498
531
  disabled={isRunQueryDisabled}
499
532
  >
500
- Debug
533
+ <MenuContentItemIcon>
534
+ <DebugIcon />
535
+ </MenuContentItemIcon>
536
+ <MenuContentItemLabel>Debug</MenuContentItemLabel>
501
537
  </MenuContentItem>
502
538
  </MenuContent>
503
539
  }
@@ -522,14 +558,15 @@ export const QueryBuilderResultPanel = observer(
522
558
  ).map((format) => (
523
559
  <MenuContentItem
524
560
  key={format}
525
- className="query-builder__result__export__dropdown__menu__item"
526
561
  onClick={(): void => confirmExport(format)}
527
562
  >
528
- {format}
563
+ <MenuContentItemIcon>
564
+ <CsvIcon />
565
+ </MenuContentItemIcon>
566
+ <MenuContentItemLabel>{format}</MenuContentItemLabel>
529
567
  </MenuContentItem>
530
568
  ))}
531
569
  <MenuContentItem
532
- className="query-builder__result__export__dropdown__menu__item"
533
570
  onClick={(): void =>
534
571
  resultState.setIsQueryUsageViewerOpened(true)
535
572
  }
@@ -538,8 +575,12 @@ export const QueryBuilderResultPanel = observer(
538
575
  !isExtraQueryUsageOptionsConfigured
539
576
  }
540
577
  >
541
- Query Code Snippets...
578
+ <MenuContentItemIcon>
579
+ <ChartIcon />
580
+ </MenuContentItemIcon>
581
+ <MenuContentItemLabel>Others...</MenuContentItemLabel>
542
582
  </MenuContentItem>
583
+ {extraExportMenuContentItems}
543
584
  </MenuContent>
544
585
  }
545
586
  menuProps={{
@@ -558,20 +599,34 @@ export const QueryBuilderResultPanel = observer(
558
599
  {resultState.isQueryUsageViewerOpened && (
559
600
  <QueryUsageViewer resultState={resultState} />
560
601
  )}
602
+ {applicationStore.pluginManager
603
+ .getApplicationPlugins()
604
+ .flatMap(
605
+ (plugin) =>
606
+ (
607
+ plugin as QueryBuilder_LegendApplicationPlugin_Extension
608
+ ).getExtraQueryBuilderExportMenuActionConfigurations?.() ??
609
+ [],
610
+ )
611
+ .map((item) => (
612
+ <div key={item.key}>
613
+ {item.renderExtraComponent
614
+ ? item.renderExtraComponent(queryBuilderState)
615
+ : undefined}
616
+ </div>
617
+ ))}
561
618
  </div>
562
619
  </div>
563
- <PanelContent>
564
- <PanelLoadingIndicator
565
- isLoading={
566
- resultState.isRunningQuery || resultState.isGeneratingPlan
567
- }
568
- />
569
- {!executionResult && (
620
+ <PanelContent className="query-builder__result__content">
621
+ <CubesLoadingIndicator isLoading={isLoading}>
622
+ <CubesLoadingIndicatorIcon />
623
+ </CubesLoadingIndicator>
624
+ {!executionResult && !isLoading && (
570
625
  <BlankPanelContent>
571
626
  Build or load a valid query first
572
627
  </BlankPanelContent>
573
628
  )}
574
- {executionResult && (
629
+ {executionResult && !isLoading && (
575
630
  <div className="query-builder__result__values">
576
631
  <QueryBuilderResultValues
577
632
  executionResult={executionResult}
@@ -17,6 +17,7 @@
17
17
  import {
18
18
  CustomSelectorInput,
19
19
  PanelHeader,
20
+ compareLabelFn,
20
21
  createFilter,
21
22
  } from '@finos/legend-art';
22
23
  import { observer } from 'mobx-react-lite';
@@ -71,7 +72,7 @@ const ClassQueryBuilderSetupPanelContent = observer(
71
72
  : []
72
73
  )
73
74
  .map(buildElementOption)
74
- .sort((a, b) => a.label.localeCompare(b.label));
75
+ .sort(compareLabelFn);
75
76
  const selectedMappingOption = queryBuilderState.executionContextState
76
77
  .mapping
77
78
  ? buildElementOption(queryBuilderState.executionContextState.mapping)
@@ -107,7 +108,7 @@ const ClassQueryBuilderSetupPanelContent = observer(
107
108
  new RuntimePointer(PackageableElementExplicitReference.create(rt)),
108
109
  )
109
110
  .map(buildRuntimeValueOption)
110
- .sort((a, b) => a.label.localeCompare(b.label));
111
+ .sort(compareLabelFn);
111
112
  const selectedRuntimeOption = queryBuilderState.executionContextState
112
113
  .runtimeValue
113
114
  ? buildRuntimeValueOption(