@finos/legend-extension-dsl-data-space 10.4.255 → 10.4.257

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 (85) hide show
  1. package/lib/__lib__/DSL_DataSpace_Event.d.ts +1 -0
  2. package/lib/__lib__/DSL_DataSpace_Event.d.ts.map +1 -1
  3. package/lib/__lib__/DSL_DataSpace_Event.js +1 -0
  4. package/lib/__lib__/DSL_DataSpace_Event.js.map +1 -1
  5. package/lib/components/DataSpaceDataAccess.d.ts.map +1 -1
  6. package/lib/components/DataSpaceDataAccess.js +3 -16
  7. package/lib/components/DataSpaceDataAccess.js.map +1 -1
  8. package/lib/components/DataSpaceDescription.d.ts.map +1 -1
  9. package/lib/components/DataSpaceDescription.js +2 -6
  10. package/lib/components/DataSpaceDescription.js.map +1 -1
  11. package/lib/components/DataSpaceQualityEmote.d.ts +22 -0
  12. package/lib/components/DataSpaceQualityEmote.d.ts.map +1 -0
  13. package/lib/components/DataSpaceQualityEmote.js +133 -0
  14. package/lib/components/DataSpaceQualityEmote.js.map +1 -0
  15. package/lib/components/DataSpaceQuickStart.d.ts.map +1 -1
  16. package/lib/components/DataSpaceQuickStart.js +7 -19
  17. package/lib/components/DataSpaceQuickStart.js.map +1 -1
  18. package/lib/components/DataSpaceViewer.d.ts.map +1 -1
  19. package/lib/components/DataSpaceViewer.js +40 -21
  20. package/lib/components/DataSpaceViewer.js.map +1 -1
  21. package/lib/components/__test-utils__/DataSpaceViewerTestUtils.d.ts +2 -0
  22. package/lib/components/__test-utils__/DataSpaceViewerTestUtils.d.ts.map +1 -1
  23. package/lib/components/__test-utils__/DataSpaceViewerTestUtils.js +1 -0
  24. package/lib/components/__test-utils__/DataSpaceViewerTestUtils.js.map +1 -1
  25. package/lib/components/index.d.ts +1 -0
  26. package/lib/components/index.d.ts.map +1 -1
  27. package/lib/components/index.js +1 -0
  28. package/lib/components/index.js.map +1 -1
  29. package/lib/graph/metamodel/pure/model/packageableElements/dataSpace/DSL_DataSpace_DataSpace.d.ts +2 -1
  30. package/lib/graph/metamodel/pure/model/packageableElements/dataSpace/DSL_DataSpace_DataSpace.d.ts.map +1 -1
  31. package/lib/graph/metamodel/pure/model/packageableElements/dataSpace/DSL_DataSpace_DataSpace.js +4 -0
  32. package/lib/graph/metamodel/pure/model/packageableElements/dataSpace/DSL_DataSpace_DataSpace.js.map +1 -1
  33. package/lib/graph-manager/action/changeDetection/DSL_DataSpace_ObserverHelper.d.ts.map +1 -1
  34. package/lib/graph-manager/action/changeDetection/DSL_DataSpace_ObserverHelper.js +5 -1
  35. package/lib/graph-manager/action/changeDetection/DSL_DataSpace_ObserverHelper.js.map +1 -1
  36. package/lib/graph-manager/protocol/pure/DSL_DataSpace_PureProtocolProcessorPlugin.d.ts.map +1 -1
  37. package/lib/graph-manager/protocol/pure/DSL_DataSpace_PureProtocolProcessorPlugin.js +39 -1
  38. package/lib/graph-manager/protocol/pure/DSL_DataSpace_PureProtocolProcessorPlugin.js.map +1 -1
  39. package/lib/graph-manager/protocol/pure/v1/model/packageableElements/dataSpace/V1_DSL_DataSpace_DataSpace.d.ts +2 -1
  40. package/lib/graph-manager/protocol/pure/v1/model/packageableElements/dataSpace/V1_DSL_DataSpace_DataSpace.d.ts.map +1 -1
  41. package/lib/graph-manager/protocol/pure/v1/model/packageableElements/dataSpace/V1_DSL_DataSpace_DataSpace.js +4 -0
  42. package/lib/graph-manager/protocol/pure/v1/model/packageableElements/dataSpace/V1_DSL_DataSpace_DataSpace.js.map +1 -1
  43. package/lib/graph-manager/protocol/pure/v1/transformation/pureProtocol/V1_DSL_DataSpace_ProtocolHelper.d.ts.map +1 -1
  44. package/lib/graph-manager/protocol/pure/v1/transformation/pureProtocol/V1_DSL_DataSpace_ProtocolHelper.js +6 -4
  45. package/lib/graph-manager/protocol/pure/v1/transformation/pureProtocol/V1_DSL_DataSpace_ProtocolHelper.js.map +1 -1
  46. package/lib/index.css +2 -2
  47. package/lib/index.css.map +1 -1
  48. package/lib/package.json +1 -1
  49. package/lib/stores/DataSpaceLayoutState.d.ts +7 -34
  50. package/lib/stores/DataSpaceLayoutState.d.ts.map +1 -1
  51. package/lib/stores/DataSpaceLayoutState.js +22 -159
  52. package/lib/stores/DataSpaceLayoutState.js.map +1 -1
  53. package/lib/stores/DataSpaceQualityState.d.ts +49 -0
  54. package/lib/stores/DataSpaceQualityState.d.ts.map +1 -0
  55. package/lib/stores/DataSpaceQualityState.js +84 -0
  56. package/lib/stores/DataSpaceQualityState.js.map +1 -0
  57. package/lib/stores/DataSpaceViewerDiagramViewerState.js +1 -2
  58. package/lib/stores/DataSpaceViewerDiagramViewerState.js.map +1 -1
  59. package/lib/stores/DataSpaceViewerState.d.ts +11 -8
  60. package/lib/stores/DataSpaceViewerState.d.ts.map +1 -1
  61. package/lib/stores/DataSpaceViewerState.js +20 -13
  62. package/lib/stores/DataSpaceViewerState.js.map +1 -1
  63. package/lib/stores/query/DataSpaceAdvancedSearchState.d.ts.map +1 -1
  64. package/lib/stores/query/DataSpaceAdvancedSearchState.js +7 -1
  65. package/lib/stores/query/DataSpaceAdvancedSearchState.js.map +1 -1
  66. package/package.json +14 -14
  67. package/src/__lib__/DSL_DataSpace_Event.ts +1 -0
  68. package/src/components/DataSpaceDataAccess.tsx +9 -68
  69. package/src/components/DataSpaceDescription.tsx +8 -41
  70. package/src/components/DataSpaceQualityEmote.tsx +223 -0
  71. package/src/components/DataSpaceQuickStart.tsx +37 -103
  72. package/src/components/DataSpaceViewer.tsx +156 -112
  73. package/src/components/__test-utils__/DataSpaceViewerTestUtils.ts +3 -0
  74. package/src/components/index.ts +5 -0
  75. package/src/graph/metamodel/pure/model/packageableElements/dataSpace/DSL_DataSpace_DataSpace.ts +5 -0
  76. package/src/graph-manager/action/changeDetection/DSL_DataSpace_ObserverHelper.ts +6 -0
  77. package/src/graph-manager/protocol/pure/DSL_DataSpace_PureProtocolProcessorPlugin.ts +56 -0
  78. package/src/graph-manager/protocol/pure/v1/model/packageableElements/dataSpace/V1_DSL_DataSpace_DataSpace.ts +5 -0
  79. package/src/graph-manager/protocol/pure/v1/transformation/pureProtocol/V1_DSL_DataSpace_ProtocolHelper.ts +6 -3
  80. package/src/stores/DataSpaceLayoutState.ts +25 -209
  81. package/src/stores/DataSpaceQualityState.ts +117 -0
  82. package/src/stores/DataSpaceViewerDiagramViewerState.ts +1 -1
  83. package/src/stores/DataSpaceViewerState.ts +45 -19
  84. package/src/stores/query/DataSpaceAdvancedSearchState.ts +9 -2
  85. package/tsconfig.json +2 -0
@@ -15,14 +15,8 @@
15
15
  */
16
16
 
17
17
  import { observer } from 'mobx-react-lite';
18
- import {
19
- AnchorLinkIcon,
20
- ExpandMoreIcon,
21
- QuestionCircleIcon,
22
- clsx,
23
- } from '@finos/legend-art';
18
+ import { CollapsibleWikiSection } from '@finos/legend-extension-dsl-data-product';
24
19
  import { type DataSpaceViewerState } from '../stores/DataSpaceViewerState.js';
25
- import { useApplicationStore } from '@finos/legend-application';
26
20
  import { useEffect, useRef } from 'react';
27
21
  import {
28
22
  DATA_SPACE_VIEWER_ACTIVITY_MODE,
@@ -34,21 +28,10 @@ import { DataSpaceWikiPlaceholder } from './DataSpacePlaceholder.js';
34
28
  export const DataSpaceDataAccess = observer(
35
29
  (props: { dataSpaceViewerState: DataSpaceViewerState }) => {
36
30
  const { dataSpaceViewerState } = props;
37
- const applicationStore = useApplicationStore();
38
- const analysisResult = dataSpaceViewerState.dataSpaceAnalysisResult;
39
- const documentationUrl = analysisResult.supportInfo?.documentationUrl;
40
31
  const sectionRef = useRef<HTMLDivElement>(null);
41
32
  const anchor = generateAnchorForActivity(
42
33
  DATA_SPACE_VIEWER_ACTIVITY_MODE.DATA_ACCESS,
43
34
  );
44
- const isCollapsed =
45
- dataSpaceViewerState.layoutState.sectionCollapseState.isSectionCollapsed(
46
- anchor,
47
- );
48
- const toggleCollapse = (): void =>
49
- dataSpaceViewerState.layoutState.sectionCollapseState.toggleSectionCollapse(
50
- anchor,
51
- );
52
35
 
53
36
  useEffect(() => {
54
37
  if (sectionRef.current) {
@@ -60,56 +43,14 @@ export const DataSpaceDataAccess = observer(
60
43
  return () => dataSpaceViewerState.layoutState.unsetWikiPageAnchor(anchor);
61
44
  }, [dataSpaceViewerState, anchor]);
62
45
 
63
- const seeDocumentation = (): void => {
64
- if (documentationUrl) {
65
- applicationStore.navigationService.navigator.visitAddress(
66
- documentationUrl,
67
- );
68
- }
69
- };
70
-
71
46
  return (
72
- <div ref={sectionRef} className="data-space__viewer__wiki__section">
73
- <div className="data-space__viewer__wiki__section__header">
74
- <div className="data-space__viewer__wiki__section__header__label">
75
- <button
76
- className="data-space__viewer__wiki__section__header__caret-btn"
77
- tabIndex={-1}
78
- onClick={toggleCollapse}
79
- title={isCollapsed ? 'Expand' : 'Collapse'}
80
- >
81
- <ExpandMoreIcon
82
- className={clsx(
83
- 'data-space__viewer__wiki__section__header__caret',
84
- {
85
- 'data-space__viewer__wiki__section__header__caret--collapsed':
86
- isCollapsed,
87
- },
88
- )}
89
- />
90
- </button>
91
- Data Access
92
- <button
93
- className="data-space__viewer__wiki__section__header__anchor"
94
- tabIndex={-1}
95
- onClick={() => dataSpaceViewerState.changeZone(anchor, true)}
96
- >
97
- <AnchorLinkIcon />
98
- </button>
99
- </div>
100
- {Boolean(documentationUrl) && (
101
- <button
102
- className="data-space__viewer__wiki__section__header__documentation"
103
- tabIndex={-1}
104
- onClick={seeDocumentation}
105
- title="See Documentation"
106
- >
107
- <QuestionCircleIcon />
108
- </button>
109
- )}
110
- </div>
111
- {!isCollapsed && (
112
- <div className="data-space__viewer__wiki__section__content">
47
+ <div ref={sectionRef} className="viewer__wiki__section">
48
+ <CollapsibleWikiSection
49
+ viewerState={dataSpaceViewerState}
50
+ section={DATA_SPACE_VIEWER_ACTIVITY_MODE.DATA_ACCESS}
51
+ showDocumentation={true}
52
+ >
53
+ <div className="viewer__wiki__section__content">
113
54
  <div className="data-space__viewer__data-access">
114
55
  {dataSpaceViewerState.currentDataAccessState ? (
115
56
  <DataAccessOverview
@@ -120,7 +61,7 @@ export const DataSpaceDataAccess = observer(
120
61
  )}
121
62
  </div>
122
63
  </div>
123
- )}
64
+ </CollapsibleWikiSection>
124
65
  </div>
125
66
  );
126
67
  },
@@ -14,7 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import { AnchorLinkIcon, ExpandMoreIcon, clsx } from '@finos/legend-art';
17
+ import { CollapsibleWikiSection } from '@finos/legend-extension-dsl-data-product';
18
18
  import { type DataSpaceViewerState } from '../stores/DataSpaceViewerState.js';
19
19
  import { observer } from 'mobx-react-lite';
20
20
  import { DataSpaceWikiPlaceholder } from './DataSpacePlaceholder.js';
@@ -33,14 +33,6 @@ export const DataSpaceDescription = observer(
33
33
  const anchor = generateAnchorForActivity(
34
34
  DATA_SPACE_VIEWER_ACTIVITY_MODE.DESCRIPTION,
35
35
  );
36
- const isCollapsed =
37
- dataSpaceViewerState.layoutState.sectionCollapseState.isSectionCollapsed(
38
- anchor,
39
- );
40
- const toggleCollapse = (): void =>
41
- dataSpaceViewerState.layoutState.sectionCollapseState.toggleSectionCollapse(
42
- anchor,
43
- );
44
36
 
45
37
  useEffect(() => {
46
38
  if (sectionRef.current) {
@@ -53,37 +45,12 @@ export const DataSpaceDescription = observer(
53
45
  }, [dataSpaceViewerState, anchor]);
54
46
 
55
47
  return (
56
- <div ref={sectionRef} className="data-space__viewer__wiki__section">
57
- <div className="data-space__viewer__wiki__section__header">
58
- <div className="data-space__viewer__wiki__section__header__label">
59
- <button
60
- className="data-space__viewer__wiki__section__header__caret-btn"
61
- tabIndex={-1}
62
- onClick={toggleCollapse}
63
- title={isCollapsed ? 'Expand' : 'Collapse'}
64
- >
65
- <ExpandMoreIcon
66
- className={clsx(
67
- 'data-space__viewer__wiki__section__header__caret',
68
- {
69
- 'data-space__viewer__wiki__section__header__caret--collapsed':
70
- isCollapsed,
71
- },
72
- )}
73
- />
74
- </button>
75
- Description
76
- <button
77
- className="data-space__viewer__wiki__section__header__anchor"
78
- tabIndex={-1}
79
- onClick={() => dataSpaceViewerState.changeZone(anchor, true)}
80
- >
81
- <AnchorLinkIcon />
82
- </button>
83
- </div>
84
- </div>
85
- {!isCollapsed && (
86
- <div className="data-space__viewer__wiki__section__content">
48
+ <div ref={sectionRef} className="viewer__wiki__section">
49
+ <CollapsibleWikiSection
50
+ viewerState={dataSpaceViewerState}
51
+ section={DATA_SPACE_VIEWER_ACTIVITY_MODE.DESCRIPTION}
52
+ >
53
+ <div className="viewer__wiki__section__content">
87
54
  {analysisResult.description !== undefined && (
88
55
  <div className="data-space__viewer__description">
89
56
  <div className="data-space__viewer__description__content">
@@ -97,7 +64,7 @@ export const DataSpaceDescription = observer(
97
64
  <DataSpaceWikiPlaceholder message="(not specified)" />
98
65
  )}
99
66
  </div>
100
- )}
67
+ </CollapsibleWikiSection>
101
68
  </div>
102
69
  );
103
70
  },
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Copyright (c) 2020-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { observer } from 'mobx-react-lite';
18
+ import { flowResult } from 'mobx';
19
+ import { useEffect } from 'react';
20
+ import { CircularProgress, Tooltip } from '@mui/material';
21
+ import { clsx } from '@finos/legend-art';
22
+ import {
23
+ DATA_SPACE_QUALITY_LEVEL,
24
+ type DataSpaceQualityBreakdown,
25
+ type DataSpaceQualityState,
26
+ } from '../stores/DataSpaceQualityState.js';
27
+
28
+ const QUALITY_EMOTE_CLASS_NAME = clsx(
29
+ 'data-space__viewer__header__actions',
30
+ 'data-space__viewer__header__quality-emote',
31
+ );
32
+
33
+ const TIER_STATUS_ICON = {
34
+ ACHIEVED: '✅',
35
+ NEXT: '➡️',
36
+ LOCKED: '🔒',
37
+ };
38
+
39
+ const QUALITY_EMOTE_CONFIG: Record<
40
+ DATA_SPACE_QUALITY_LEVEL,
41
+ { emoji: string; label: string }
42
+ > = {
43
+ [DATA_SPACE_QUALITY_LEVEL.DIAMOND]: {
44
+ emoji: '💎',
45
+ label: 'Diamond quality',
46
+ },
47
+ [DATA_SPACE_QUALITY_LEVEL.PLATINUM]: {
48
+ emoji: '🏆',
49
+ label: 'Platinum quality',
50
+ },
51
+ [DATA_SPACE_QUALITY_LEVEL.GOLD]: { emoji: '🥇', label: 'Gold quality' },
52
+ [DATA_SPACE_QUALITY_LEVEL.SILVER]: { emoji: '🥈', label: 'Silver quality' },
53
+ [DATA_SPACE_QUALITY_LEVEL.BRONZE]: { emoji: '🥉', label: 'Bronze quality' },
54
+ };
55
+
56
+ const AI_READY_QUALITY_LEVELS: DATA_SPACE_QUALITY_LEVEL[] = [
57
+ DATA_SPACE_QUALITY_LEVEL.DIAMOND,
58
+ DATA_SPACE_QUALITY_LEVEL.PLATINUM,
59
+ ];
60
+
61
+ // The full tier ladder, in order. Milestones are cumulative — reaching one implies every
62
+ // requirement of the milestones before it is already satisfied — so a user's current tier
63
+ // is exactly the last milestone they've achieved.
64
+ const TIER_LADDER = [
65
+ DATA_SPACE_QUALITY_LEVEL.BRONZE,
66
+ DATA_SPACE_QUALITY_LEVEL.SILVER,
67
+ DATA_SPACE_QUALITY_LEVEL.GOLD,
68
+ DATA_SPACE_QUALITY_LEVEL.PLATINUM,
69
+ DATA_SPACE_QUALITY_LEVEL.DIAMOND,
70
+ ];
71
+
72
+ const formatAttributeCoverageSuffix = (
73
+ breakdown: DataSpaceQualityBreakdown,
74
+ ): string =>
75
+ breakdown.attributeCoverage !== undefined
76
+ ? ` (currently ${Math.round(breakdown.attributeCoverage * 100)}%)`
77
+ : '';
78
+
79
+ const getMilestoneRequirement = (
80
+ level: DATA_SPACE_QUALITY_LEVEL,
81
+ breakdown: DataSpaceQualityBreakdown,
82
+ ): string => {
83
+ switch (level) {
84
+ case DATA_SPACE_QUALITY_LEVEL.SILVER: {
85
+ const missing: string[] = [];
86
+ if (!breakdown.isDescriptionDocumented) {
87
+ missing.push('write a description (2+ sentences)');
88
+ }
89
+ if (!breakdown.isExecutablesPresent) {
90
+ missing.push('add Executables');
91
+ }
92
+ if (!breakdown.isModelsDocumentationPresent) {
93
+ missing.push('add Models Documentation');
94
+ }
95
+ return missing.length > 0
96
+ ? `Still needed: ${missing.join(', ')}.`
97
+ : 'Write a description (2+ sentences), and make sure Executables and Models Documentation are present.';
98
+ }
99
+ case DATA_SPACE_QUALITY_LEVEL.GOLD:
100
+ return breakdown.isEveryServiceDocumented
101
+ ? 'Describe at least 50% of attributes.'
102
+ : 'Describe every executable, and describe at least 50% of attributes.';
103
+ case DATA_SPACE_QUALITY_LEVEL.PLATINUM:
104
+ return `Describe 90%-95% of attributes${formatAttributeCoverageSuffix(breakdown)}.`;
105
+ case DATA_SPACE_QUALITY_LEVEL.DIAMOND:
106
+ return `Describe 95%+ of attributes${formatAttributeCoverageSuffix(breakdown)}. Coming soon: will also require MCPs + Verified NLQs ground truth.`;
107
+ case DATA_SPACE_QUALITY_LEVEL.BRONZE:
108
+ default:
109
+ return '';
110
+ }
111
+ };
112
+
113
+ // Renders the full tier ladder up to Diamond, not just the next milestone, and marks which
114
+ // tiers unlock the AI-Ready stamp.
115
+ const QualityRoadmap = observer(
116
+ (props: {
117
+ qualityLevel: DATA_SPACE_QUALITY_LEVEL;
118
+ breakdown: DataSpaceQualityBreakdown;
119
+ }): React.ReactNode => {
120
+ const { qualityLevel, breakdown } = props;
121
+ const config = QUALITY_EMOTE_CONFIG[qualityLevel];
122
+ const currentIndex = TIER_LADDER.indexOf(qualityLevel);
123
+
124
+ return (
125
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '0.4rem' }}>
126
+ <div style={{ fontWeight: 700 }}>
127
+ {config.emoji} Current: {config.label}
128
+ </div>
129
+ {TIER_LADDER.slice(1).map((level, index) => {
130
+ const levelIndex = index + 1;
131
+ const isAchieved = currentIndex >= levelIndex;
132
+ const isNext = currentIndex === levelIndex - 1;
133
+ const levelConfig = QUALITY_EMOTE_CONFIG[level];
134
+ const isAiReadyMilestone = AI_READY_QUALITY_LEVELS.includes(level);
135
+ const statusIcon = isAchieved
136
+ ? TIER_STATUS_ICON.ACHIEVED
137
+ : isNext
138
+ ? TIER_STATUS_ICON.NEXT
139
+ : TIER_STATUS_ICON.LOCKED;
140
+ return (
141
+ <div
142
+ key={level}
143
+ style={{
144
+ fontWeight: isNext ? 700 : 400,
145
+ opacity: isNext ? 1 : 0.85,
146
+ }}
147
+ >
148
+ {statusIcon} {levelConfig.emoji} {levelConfig.label}
149
+ {isAiReadyMilestone ? ' (AI-Ready)' : ''}
150
+ {!isAchieved
151
+ ? ` — ${getMilestoneRequirement(level, breakdown)}`
152
+ : ''}
153
+ </div>
154
+ );
155
+ })}
156
+ </div>
157
+ );
158
+ },
159
+ );
160
+
161
+ export const DataSpaceQualityEmote = observer(
162
+ (props: { qualityState: DataSpaceQualityState }) => {
163
+ const { qualityState } = props;
164
+
165
+ useEffect(() => {
166
+ flowResult(qualityState.computeQuality()).catch(() => undefined);
167
+ }, [qualityState]);
168
+
169
+ if (!qualityState.isSupported) {
170
+ return null;
171
+ }
172
+
173
+ if (
174
+ qualityState.computingQualityState.isInInitialState ||
175
+ qualityState.computingQualityState.isInProgress
176
+ ) {
177
+ return (
178
+ <div className={QUALITY_EMOTE_CLASS_NAME}>
179
+ <CircularProgress size={12} />
180
+ </div>
181
+ );
182
+ }
183
+
184
+ if (!qualityState.qualityLevel || !qualityState.qualityBreakdown) {
185
+ return null;
186
+ }
187
+
188
+ const config = QUALITY_EMOTE_CONFIG[qualityState.qualityLevel];
189
+ const isAIReady = AI_READY_QUALITY_LEVELS.includes(
190
+ qualityState.qualityLevel,
191
+ );
192
+
193
+ return (
194
+ <Tooltip
195
+ title={
196
+ <QualityRoadmap
197
+ qualityLevel={qualityState.qualityLevel}
198
+ breakdown={qualityState.qualityBreakdown}
199
+ />
200
+ }
201
+ placement="bottom"
202
+ >
203
+ <div className={QUALITY_EMOTE_CLASS_NAME}>
204
+ AI Readiness Badge:{' '}
205
+ <span
206
+ className="data-space__viewer__header__quality-emote__icon"
207
+ aria-label={config.label}
208
+ >
209
+ {config.emoji}
210
+ </span>
211
+ {isAIReady && (
212
+ <span
213
+ className="data-space__viewer__header__quality-emote__ai-ready-stamp"
214
+ title="Documentation quality is high enough for reliable AI consumption"
215
+ >
216
+ AI-Ready
217
+ </span>
218
+ )}
219
+ </div>
220
+ </Tooltip>
221
+ );
222
+ },
223
+ );
@@ -21,13 +21,15 @@ import {
21
21
  CodeIcon,
22
22
  CopyIcon,
23
23
  DataAccessIcon,
24
- ExpandMoreIcon,
25
24
  LegendLogo,
26
25
  MoreVerticalIcon,
27
- QuestionCircleIcon,
28
26
  StatisticsIcon,
29
27
  clsx,
30
28
  } from '@finos/legend-art';
29
+ import {
30
+ CollapsibleChevron,
31
+ CollapsibleWikiSection,
32
+ } from '@finos/legend-extension-dsl-data-product';
31
33
  import { type DataSpaceViewerState } from '../stores/DataSpaceViewerState.js';
32
34
  import { useApplicationStore } from '@finos/legend-application';
33
35
  import {
@@ -471,14 +473,6 @@ const DataSpaceExecutableAnalysisResultView = observer(
471
473
  const executableAnalysisResult = executableState.value;
472
474
  const quickStartRef = useRef<HTMLDivElement>(null);
473
475
  const anchor = generateAnchorForQuickStart(executableAnalysisResult);
474
- const isCollapsed =
475
- dataSpaceViewerState.layoutState.sectionCollapseState.isSectionCollapsed(
476
- anchor,
477
- );
478
- const toggleCollapse = (): void =>
479
- dataSpaceViewerState.layoutState.sectionCollapseState.toggleSectionCollapse(
480
- anchor,
481
- );
482
476
 
483
477
  useEffect(() => {
484
478
  if (quickStartRef.current) {
@@ -510,37 +504,30 @@ const DataSpaceExecutableAnalysisResultView = observer(
510
504
  return (
511
505
  <div ref={quickStartRef} className="data-space__viewer__quickstart__item">
512
506
  <div className="data-space__viewer__quickstart__item__header">
513
- <button
514
- className="data-space__viewer__quickstart__item__header__caret-btn"
515
- tabIndex={-1}
516
- onClick={toggleCollapse}
517
- title={isCollapsed ? 'Expand' : 'Collapse'}
518
- >
519
- <ExpandMoreIcon
520
- className={clsx(
521
- 'data-space__viewer__quickstart__item__header__caret',
522
- {
523
- 'data-space__viewer__quickstart__item__header__caret--collapsed':
524
- isCollapsed,
525
- },
526
- )}
527
- />
528
- </button>
529
507
  <div className="data-space__viewer__quickstart__item__header__title">
530
508
  {executableAnalysisResult.title}
531
509
  </div>
532
510
  <div className="data-space__viewer__quickstart__item__header__type">
533
511
  {executableTypeLabel}
534
512
  </div>
513
+ <CollapsibleChevron
514
+ viewerState={dataSpaceViewerState}
515
+ anchor={anchor}
516
+ />
535
517
  <button
536
518
  className="data-space__viewer__quickstart__item__header__anchor"
537
519
  tabIndex={-1}
538
- onClick={() => dataSpaceViewerState.changeZone(anchor, true)}
520
+ onClick={() => {
521
+ dataSpaceViewerState.changeZone(anchor, true);
522
+ dataSpaceViewerState.copyLinkToClipboard(anchor);
523
+ }}
539
524
  >
540
525
  <AnchorLinkIcon />
541
526
  </button>
542
527
  </div>
543
- {!isCollapsed && (
528
+ {!dataSpaceViewerState.layoutState.sectionCollapseState.isSectionCollapsed(
529
+ anchor,
530
+ ) && (
544
531
  <>
545
532
  {executableAnalysisResult.description !== undefined && (
546
533
  <div className="data-space__viewer__quickstart__item__description">
@@ -568,24 +555,11 @@ const DataSpaceExecutableAnalysisResultView = observer(
568
555
  export const DataSpaceQuickStart = observer(
569
556
  (props: { dataSpaceViewerState: DataSpaceViewerState }) => {
570
557
  const { dataSpaceViewerState } = props;
571
- const applicationStore = useApplicationStore();
572
558
  const analysisResult = dataSpaceViewerState.dataSpaceAnalysisResult;
573
- const documentationUrl = analysisResult.supportInfo?.documentationUrl;
574
559
  const sectionRef = useRef<HTMLDivElement>(null);
575
560
  const anchor = generateAnchorForActivity(
576
561
  DATA_SPACE_VIEWER_ACTIVITY_MODE.QUICK_START,
577
562
  );
578
- const executableAnchors = dataSpaceViewerState.executableStates.map(
579
- (executableState) => generateAnchorForQuickStart(executableState.value),
580
- );
581
- const isCollapsed =
582
- dataSpaceViewerState.layoutState.sectionCollapseState.areAllSectionsCollapsed(
583
- executableAnchors,
584
- );
585
- const toggleCollapse = (): void =>
586
- dataSpaceViewerState.layoutState.sectionCollapseState.toggleAllSectionsCollapse(
587
- executableAnchors,
588
- );
589
563
 
590
564
  useEffect(() => {
591
565
  if (sectionRef.current) {
@@ -597,71 +571,31 @@ export const DataSpaceQuickStart = observer(
597
571
  return () => dataSpaceViewerState.layoutState.unsetWikiPageAnchor(anchor);
598
572
  }, [dataSpaceViewerState, anchor]);
599
573
 
600
- const seeDocumentation = (): void => {
601
- if (documentationUrl) {
602
- applicationStore.navigationService.navigator.visitAddress(
603
- documentationUrl,
604
- );
605
- }
606
- };
607
-
608
574
  return (
609
- <div ref={sectionRef} className="data-space__viewer__wiki__section">
610
- <div className="data-space__viewer__wiki__section__header">
611
- <div className="data-space__viewer__wiki__section__header__label">
612
- {executableAnchors.length > 0 && (
613
- <button
614
- className="data-space__viewer__wiki__section__header__caret-btn"
615
- tabIndex={-1}
616
- onClick={toggleCollapse}
617
- title={isCollapsed ? 'Expand All' : 'Collapse All'}
618
- >
619
- <ExpandMoreIcon
620
- className={clsx(
621
- 'data-space__viewer__wiki__section__header__caret',
622
- {
623
- 'data-space__viewer__wiki__section__header__caret--collapsed':
624
- isCollapsed,
625
- },
626
- )}
627
- />
628
- </button>
575
+ <div ref={sectionRef} className="viewer__wiki__section">
576
+ <CollapsibleWikiSection
577
+ viewerState={dataSpaceViewerState}
578
+ section={DATA_SPACE_VIEWER_ACTIVITY_MODE.QUICK_START}
579
+ showDocumentation={true}
580
+ >
581
+ <div className="viewer__wiki__section__content">
582
+ {dataSpaceViewerState.executableStates.length !== 0 && (
583
+ <div className="data-space__viewer__quickstart">
584
+ {dataSpaceViewerState.executableStates.map(
585
+ (executableState) => (
586
+ <DataSpaceExecutableAnalysisResultView
587
+ key={executableState.uuid}
588
+ executableState={executableState}
589
+ />
590
+ ),
591
+ )}
592
+ </div>
593
+ )}
594
+ {analysisResult.executables.length === 0 && (
595
+ <DataSpaceWikiPlaceholder message="(not specified)" />
629
596
  )}
630
- Quick Start
631
- <button
632
- className="data-space__viewer__wiki__section__header__anchor"
633
- tabIndex={-1}
634
- onClick={() => dataSpaceViewerState.changeZone(anchor, true)}
635
- >
636
- <AnchorLinkIcon />
637
- </button>
638
597
  </div>
639
- {Boolean(documentationUrl) && (
640
- <button
641
- className="data-space__viewer__wiki__section__header__documentation"
642
- tabIndex={-1}
643
- onClick={seeDocumentation}
644
- title="See Documentation"
645
- >
646
- <QuestionCircleIcon />
647
- </button>
648
- )}
649
- </div>
650
- <div className="data-space__viewer__wiki__section__content">
651
- {dataSpaceViewerState.executableStates.length !== 0 && (
652
- <div className="data-space__viewer__quickstart">
653
- {dataSpaceViewerState.executableStates.map((executableState) => (
654
- <DataSpaceExecutableAnalysisResultView
655
- key={executableState.uuid}
656
- executableState={executableState}
657
- />
658
- ))}
659
- </div>
660
- )}
661
- {analysisResult.executables.length === 0 && (
662
- <DataSpaceWikiPlaceholder message="(not specified)" />
663
- )}
664
- </div>
598
+ </CollapsibleWikiSection>
665
599
  </div>
666
600
  );
667
601
  },