@finos/legend-application-studio 28.4.4 → 28.5.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 (113) hide show
  1. package/lib/__lib__/LegendStudioDocumentation.d.ts +1 -0
  2. package/lib/__lib__/LegendStudioDocumentation.d.ts.map +1 -1
  3. package/lib/__lib__/LegendStudioDocumentation.js +1 -0
  4. package/lib/__lib__/LegendStudioDocumentation.js.map +1 -1
  5. package/lib/__lib__/LegendStudioNavigation.d.ts +12 -2
  6. package/lib/__lib__/LegendStudioNavigation.d.ts.map +1 -1
  7. package/lib/__lib__/LegendStudioNavigation.js +92 -37
  8. package/lib/__lib__/LegendStudioNavigation.js.map +1 -1
  9. package/lib/components/LegendStudioWebApplication.d.ts.map +1 -1
  10. package/lib/components/LegendStudioWebApplication.js +6 -0
  11. package/lib/components/LegendStudioWebApplication.js.map +1 -1
  12. package/lib/components/editor/ActivityBar.js +1 -1
  13. package/lib/components/editor/ActivityBar.js.map +1 -1
  14. package/lib/components/editor/Editor.d.ts.map +1 -1
  15. package/lib/components/editor/Editor.js +10 -2
  16. package/lib/components/editor/Editor.js.map +1 -1
  17. package/lib/components/editor/StatusBar.d.ts.map +1 -1
  18. package/lib/components/editor/StatusBar.js +4 -1
  19. package/lib/components/editor/StatusBar.js.map +1 -1
  20. package/lib/components/editor/__test-utils__/EditorComponentTestUtils.d.ts.map +1 -1
  21. package/lib/components/editor/__test-utils__/EditorComponentTestUtils.js +1 -1
  22. package/lib/components/editor/__test-utils__/EditorComponentTestUtils.js.map +1 -1
  23. package/lib/components/editor/editor-group/GrammarTextEditor.d.ts.map +1 -1
  24. package/lib/components/editor/editor-group/GrammarTextEditor.js +6 -3
  25. package/lib/components/editor/editor-group/GrammarTextEditor.js.map +1 -1
  26. package/lib/components/editor/editor-group/project-configuration-editor/ProjectDependencyEditor.d.ts +4 -0
  27. package/lib/components/editor/editor-group/project-configuration-editor/ProjectDependencyEditor.d.ts.map +1 -1
  28. package/lib/components/editor/side-bar/ProjectOverview.d.ts.map +1 -1
  29. package/lib/components/editor/side-bar/ProjectOverview.js +109 -7
  30. package/lib/components/editor/side-bar/ProjectOverview.js.map +1 -1
  31. package/lib/components/project-view/ProjectViewer.js +1 -1
  32. package/lib/components/project-view/ProjectViewer.js.map +1 -1
  33. package/lib/components/workspace-review/WorkspaceReview.js +1 -1
  34. package/lib/components/workspace-review/WorkspaceReview.js.map +1 -1
  35. package/lib/components/workspace-setup/CreateWorkspaceModal.d.ts +5 -1
  36. package/lib/components/workspace-setup/CreateWorkspaceModal.d.ts.map +1 -1
  37. package/lib/components/workspace-setup/CreateWorkspaceModal.js +34 -6
  38. package/lib/components/workspace-setup/CreateWorkspaceModal.js.map +1 -1
  39. package/lib/components/workspace-setup/WorkspaceSelectorUtils.d.ts.map +1 -1
  40. package/lib/components/workspace-setup/WorkspaceSelectorUtils.js +1 -1
  41. package/lib/components/workspace-setup/WorkspaceSelectorUtils.js.map +1 -1
  42. package/lib/components/workspace-setup/WorkspaceSetup.d.ts +1 -0
  43. package/lib/components/workspace-setup/WorkspaceSetup.d.ts.map +1 -1
  44. package/lib/components/workspace-setup/WorkspaceSetup.js +2 -1
  45. package/lib/components/workspace-setup/WorkspaceSetup.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/LegendStudioApplicationPlugin.d.ts +1 -1
  50. package/lib/stores/LegendStudioApplicationPlugin.d.ts.map +1 -1
  51. package/lib/stores/editor/EditorSDLCState.d.ts +8 -2
  52. package/lib/stores/editor/EditorSDLCState.d.ts.map +1 -1
  53. package/lib/stores/editor/EditorSDLCState.js +29 -3
  54. package/lib/stores/editor/EditorSDLCState.js.map +1 -1
  55. package/lib/stores/editor/EditorStore.d.ts +1 -1
  56. package/lib/stores/editor/EditorStore.d.ts.map +1 -1
  57. package/lib/stores/editor/EditorStore.js +8 -5
  58. package/lib/stores/editor/EditorStore.js.map +1 -1
  59. package/lib/stores/editor/StandardEditorMode.d.ts.map +1 -1
  60. package/lib/stores/editor/StandardEditorMode.js +1 -1
  61. package/lib/stores/editor/StandardEditorMode.js.map +1 -1
  62. package/lib/stores/editor/editor-state/project-configuration-editor-state/ProjectConfigurationEditorState.d.ts.map +1 -1
  63. package/lib/stores/editor/editor-state/project-configuration-editor-state/ProjectConfigurationEditorState.js +1 -1
  64. package/lib/stores/editor/editor-state/project-configuration-editor-state/ProjectConfigurationEditorState.js.map +1 -1
  65. package/lib/stores/editor/sidebar-state/ProjectOverviewState.d.ts +9 -3
  66. package/lib/stores/editor/sidebar-state/ProjectOverviewState.d.ts.map +1 -1
  67. package/lib/stores/editor/sidebar-state/ProjectOverviewState.js +71 -7
  68. package/lib/stores/editor/sidebar-state/ProjectOverviewState.js.map +1 -1
  69. package/lib/stores/editor/sidebar-state/WorkspaceReviewState.d.ts.map +1 -1
  70. package/lib/stores/editor/sidebar-state/WorkspaceReviewState.js +6 -6
  71. package/lib/stores/editor/sidebar-state/WorkspaceReviewState.js.map +1 -1
  72. package/lib/stores/editor/sidebar-state/WorkspaceUpdaterState.d.ts.map +1 -1
  73. package/lib/stores/editor/sidebar-state/WorkspaceUpdaterState.js +2 -2
  74. package/lib/stores/editor/sidebar-state/WorkspaceUpdaterState.js.map +1 -1
  75. package/lib/stores/workspace-review/WorkspaceReviewStore.d.ts +3 -1
  76. package/lib/stores/workspace-review/WorkspaceReviewStore.d.ts.map +1 -1
  77. package/lib/stores/workspace-review/WorkspaceReviewStore.js +13 -7
  78. package/lib/stores/workspace-review/WorkspaceReviewStore.js.map +1 -1
  79. package/lib/stores/workspace-setup/ProjectConfigurationStatus.d.ts +1 -1
  80. package/lib/stores/workspace-setup/ProjectConfigurationStatus.d.ts.map +1 -1
  81. package/lib/stores/workspace-setup/ProjectConfigurationStatus.js +2 -2
  82. package/lib/stores/workspace-setup/ProjectConfigurationStatus.js.map +1 -1
  83. package/lib/stores/workspace-setup/WorkspaceSetupStore.d.ts +6 -2
  84. package/lib/stores/workspace-setup/WorkspaceSetupStore.d.ts.map +1 -1
  85. package/lib/stores/workspace-setup/WorkspaceSetupStore.js +45 -12
  86. package/lib/stores/workspace-setup/WorkspaceSetupStore.js.map +1 -1
  87. package/package.json +3 -3
  88. package/src/__lib__/LegendStudioDocumentation.ts +1 -0
  89. package/src/__lib__/LegendStudioNavigation.ts +106 -25
  90. package/src/components/LegendStudioWebApplication.tsx +6 -0
  91. package/src/components/editor/ActivityBar.tsx +1 -1
  92. package/src/components/editor/Editor.tsx +15 -2
  93. package/src/components/editor/StatusBar.tsx +5 -1
  94. package/src/components/editor/__test-utils__/EditorComponentTestUtils.tsx +1 -0
  95. package/src/components/editor/editor-group/GrammarTextEditor.tsx +6 -3
  96. package/src/components/editor/editor-group/project-configuration-editor/ProjectDependencyEditor.tsx +1 -1
  97. package/src/components/editor/side-bar/ProjectOverview.tsx +340 -8
  98. package/src/components/project-view/ProjectViewer.tsx +1 -1
  99. package/src/components/workspace-review/WorkspaceReview.tsx +1 -1
  100. package/src/components/workspace-setup/CreateWorkspaceModal.tsx +63 -4
  101. package/src/components/workspace-setup/WorkspaceSelectorUtils.tsx +13 -6
  102. package/src/components/workspace-setup/WorkspaceSetup.tsx +3 -0
  103. package/src/stores/LegendStudioApplicationPlugin.ts +1 -1
  104. package/src/stores/editor/EditorSDLCState.ts +45 -0
  105. package/src/stores/editor/EditorStore.ts +15 -2
  106. package/src/stores/editor/StandardEditorMode.ts +1 -0
  107. package/src/stores/editor/editor-state/project-configuration-editor-state/ProjectConfigurationEditorState.ts +1 -0
  108. package/src/stores/editor/sidebar-state/ProjectOverviewState.ts +135 -1
  109. package/src/stores/editor/sidebar-state/WorkspaceReviewState.ts +6 -1
  110. package/src/stores/editor/sidebar-state/WorkspaceUpdaterState.ts +2 -0
  111. package/src/stores/workspace-review/WorkspaceReviewStore.ts +15 -1
  112. package/src/stores/workspace-setup/ProjectConfigurationStatus.ts +2 -0
  113. package/src/stores/workspace-setup/WorkspaceSetupStore.ts +71 -7
@@ -37,6 +37,7 @@ import {
37
37
  ModalFooter,
38
38
  MenuContentItem,
39
39
  MenuContent,
40
+ PanelFormBooleanField,
40
41
  } from '@finos/legend-art';
41
42
  import { PROJECT_OVERVIEW_ACTIVITY_MODE } from '../../../stores/editor/sidebar-state/ProjectOverviewState.js';
42
43
  import {
@@ -52,10 +53,24 @@ import {
52
53
  NewVersionType,
53
54
  WorkspaceType,
54
55
  areWorkspacesEquivalent,
56
+ Review,
57
+ ReviewState,
58
+ Patch,
55
59
  } from '@finos/legend-server-sdlc';
56
60
  import { useEditorStore } from '../EditorStoreProvider.js';
57
61
  import { useApplicationStore } from '@finos/legend-application';
58
62
  import { useLegendStudioApplicationStore } from '../../LegendStudioFrameworkProvider.js';
63
+ import {
64
+ ActionState,
65
+ assertErrorThrown,
66
+ guaranteeNonNullable,
67
+ LogEvent,
68
+ } from '@finos/legend-shared';
69
+ import type { VersionOption } from '../editor-group/project-configuration-editor/ProjectDependencyEditor.js';
70
+ import { DocumentationLink } from '@finos/legend-lego/application';
71
+ import { LEGEND_STUDIO_DOCUMENTATION_KEY } from '../../../__lib__/LegendStudioDocumentation.js';
72
+ import type { PatchOption } from '../../workspace-setup/CreateWorkspaceModal.js';
73
+ import { LEGEND_STUDIO_APP_EVENT } from '../../../__lib__/LegendStudioEvent.js';
59
74
 
60
75
  const ShareProjectModal = observer(
61
76
  (props: { open: boolean; closeModal: () => void }) => {
@@ -180,10 +195,10 @@ const WorkspaceViewer = observer((props: { workspace: Workspace }) => {
180
195
  const { workspace } = props;
181
196
  const editorStore = useEditorStore();
182
197
  const applicationStore = useApplicationStore();
183
- const isActive = areWorkspacesEquivalent(
184
- editorStore.sdlcState.activeWorkspace,
185
- workspace,
186
- );
198
+ const isActive =
199
+ areWorkspacesEquivalent(editorStore.sdlcState.activeWorkspace, workspace) &&
200
+ workspace.source ===
201
+ editorStore.sdlcState.activePatch?.patchReleaseVersionId.id;
187
202
  const [isSelectedFromContextMenu, setIsSelectedFromContextMenu] =
188
203
  useState(false);
189
204
  const onContextMenuOpen = (): void => setIsSelectedFromContextMenu(true);
@@ -210,6 +225,7 @@ const WorkspaceViewer = observer((props: { workspace: Workspace }) => {
210
225
  applicationStore.navigationService.navigator.generateAddress(
211
226
  generateEditorRoute(
212
227
  workspace.projectId,
228
+ editorStore.sdlcState.activePatch?.patchReleaseVersionId.id,
213
229
  workspace.workspaceId,
214
230
  workspace.workspaceType,
215
231
  ),
@@ -226,9 +242,14 @@ const WorkspaceViewer = observer((props: { workspace: Workspace }) => {
226
242
  <UserIcon />
227
243
  )}
228
244
  </div>
229
- <div className="project-overview__item__link__content__name">
245
+ <div className="project-overview__item__link__content__name project-overview__workspace__viewer__label">
230
246
  {workspace.workspaceId}
231
247
  </div>
248
+ {workspace.source && (
249
+ <div className="project-overview__workspace__viewer__source">
250
+ {`patch/${workspace.source}`}
251
+ </div>
252
+ )}
232
253
  </div>
233
254
  </button>
234
255
  </ContextMenu>
@@ -276,7 +297,7 @@ const WorkspacesViewer = observer(() => {
276
297
  >
277
298
  {workspaces.map((workspace) => (
278
299
  <WorkspaceViewer
279
- key={`${workspace.workspaceType}.${workspace.workspaceId}`}
300
+ key={`${workspace.workspaceType}.${workspace.workspaceId}.${workspace.source}`}
280
301
  workspace={workspace}
281
302
  />
282
303
  ))}
@@ -324,7 +345,11 @@ const ReleaseEditor = observer(() => {
324
345
  flowResult(projectOverviewState.fetchLatestProjectVersion()).catch(
325
346
  applicationStore.alertUnhandledError,
326
347
  );
327
- }, [applicationStore, projectOverviewState]);
348
+ }, [
349
+ applicationStore,
350
+ editorStore.sdlcState.activePatch,
351
+ projectOverviewState,
352
+ ]);
328
353
 
329
354
  return (
330
355
  <div className="panel side-bar__panel project-overview__panel project-overview__release">
@@ -337,7 +362,11 @@ const ReleaseEditor = observer(() => {
337
362
  </div>
338
363
  <div className="panel__content project-overview__release__panel__content project-overview__release__content">
339
364
  <PanelLoadingIndicator isLoading={isDispatchingAction} />
340
- <div className="project-overview__release__editor">
365
+ <div
366
+ className={clsx(
367
+ 'project-overview__release__editor project-overview__release__editor__project',
368
+ )}
369
+ >
341
370
  <textarea
342
371
  className="project-overview__release__editor__input input--dark"
343
372
  spellCheck={false}
@@ -483,6 +512,306 @@ const ReleaseEditor = observer(() => {
483
512
  );
484
513
  });
485
514
 
515
+ const PatchEditor = observer(() => {
516
+ const editorStore = useEditorStore();
517
+ const applicationStore = useLegendStudioApplicationStore();
518
+ const projectOverviewState = editorStore.projectOverviewState;
519
+ const [versionOptions] = useState<VersionOption[]>(
520
+ editorStore.sdlcState.projectVersions.map((v) => ({
521
+ label: v.id.id,
522
+ value: v.id.id,
523
+ })),
524
+ );
525
+ const [selectedVersionOption, setSelectedVersionOption] =
526
+ useState<VersionOption | null>(null);
527
+ const patches = projectOverviewState.patches;
528
+ const [selectedPatchOption, setSelectedPatchOption] =
529
+ useState<PatchOption | null>(null);
530
+ const [workspaceName, setWorkspaceName] = useState<string>('');
531
+ const [isGroupWorkspace, setIsGroupWorkspace] = useState<boolean>(true);
532
+ const [committedReviews, setCommittedReviews] = useState<Review[]>([]);
533
+ const [fetchSelectedPatchCommittedReviews] = useState(ActionState.create());
534
+ const onPatchOptionChange = async (
535
+ val: PatchOption | null,
536
+ ): Promise<void> => {
537
+ if (
538
+ (val !== null || selectedPatchOption !== null) &&
539
+ (!val || !selectedPatchOption || val.value !== selectedPatchOption.value)
540
+ ) {
541
+ setSelectedPatchOption(val);
542
+ fetchSelectedPatchCommittedReviews.inProgress();
543
+ try {
544
+ if (val && val.value instanceof Patch) {
545
+ const reviews = await editorStore.sdlcServerClient.getReviews(
546
+ projectOverviewState.sdlcState.activeProject.projectId,
547
+ val.value.patchReleaseVersionId.id,
548
+ {
549
+ state: ReviewState.COMMITTED,
550
+ },
551
+ );
552
+ setCommittedReviews(
553
+ reviews.map((v) => Review.serialization.fromJson(v)),
554
+ );
555
+ } else {
556
+ setCommittedReviews([]);
557
+ }
558
+ } catch (error) {
559
+ assertErrorThrown(error);
560
+ editorStore.applicationStore.logService.error(
561
+ LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE),
562
+ error,
563
+ );
564
+ } finally {
565
+ fetchSelectedPatchCommittedReviews.reset();
566
+ }
567
+ } else {
568
+ setCommittedReviews([]);
569
+ }
570
+ };
571
+ const onVersionOptionChange = (val: VersionOption | null): void => {
572
+ if (
573
+ (val !== null || selectedVersionOption !== null) &&
574
+ (!val ||
575
+ !selectedVersionOption ||
576
+ val.value !== selectedVersionOption.value)
577
+ ) {
578
+ setSelectedVersionOption(val);
579
+ }
580
+ };
581
+ const createPatch = (): void => {
582
+ if (selectedVersionOption) {
583
+ flowResult(
584
+ projectOverviewState.createPatch(
585
+ selectedVersionOption.value,
586
+ workspaceName,
587
+ isGroupWorkspace ? WorkspaceType.GROUP : WorkspaceType.USER,
588
+ ),
589
+ ).catch(applicationStore.alertUnhandledError);
590
+ }
591
+ };
592
+ const toggleGroupWorkspace = (): void => {
593
+ setIsGroupWorkspace(!isGroupWorkspace);
594
+ };
595
+ const changeWorkspaceName: React.ChangeEventHandler<HTMLInputElement> = (
596
+ event,
597
+ ) => setWorkspaceName(event.target.value);
598
+ const isDispatchingAction =
599
+ projectOverviewState.isFetchingLatestVersion ||
600
+ projectOverviewState.isFetchingCurrentProjectRevision ||
601
+ projectOverviewState.isCreatingVersion;
602
+ const { latestProjectVersion, currentProjectRevision } = projectOverviewState;
603
+ const createPatchRelease = applicationStore.guardUnhandledError(() =>
604
+ flowResult(
605
+ projectOverviewState.createPatchVersion(
606
+ guaranteeNonNullable(selectedPatchOption).label,
607
+ ),
608
+ ),
609
+ );
610
+ const isCurrentProjectVersionLatest =
611
+ Boolean(latestProjectVersion) &&
612
+ latestProjectVersion?.revisionId === currentProjectRevision?.id;
613
+ const canCreateVersion =
614
+ !isCurrentProjectVersionLatest &&
615
+ !isDispatchingAction &&
616
+ editorStore.sdlcServerClient.features.canCreateVersion;
617
+ const patchBlurb = `Releasing above mentioned version would delete the upstream branch created for doing this patch release. By doing so you won't be access any of the branches created for development for doing this patch release`;
618
+
619
+ useEffect(() => {
620
+ flowResult(projectOverviewState.fetchPatches()).catch(
621
+ applicationStore.alertUnhandledError,
622
+ );
623
+ }, [
624
+ applicationStore.alertUnhandledError,
625
+ applicationStore.notificationService,
626
+ editorStore.sdlcServerClient,
627
+ editorStore.sdlcState.activeProject.projectId,
628
+
629
+ projectOverviewState,
630
+ ]);
631
+
632
+ return (
633
+ <div className="panel side-bar__panel project-overview__panel project-overview__patch">
634
+ <div className="panel__header">
635
+ <div className="panel__header__title">
636
+ <div className="panel__header__title__content">
637
+ Create Patch
638
+ <DocumentationLink
639
+ className="project-overview__patch__documentation"
640
+ documentationKey={LEGEND_STUDIO_DOCUMENTATION_KEY.CREATE_PATCH}
641
+ />
642
+ </div>
643
+ </div>
644
+ </div>
645
+ <div className="panel__content project-overview__panel__content">
646
+ <div
647
+ className={clsx('project-overview__patch__create', {
648
+ 'project-overview__patch__create--progress':
649
+ projectOverviewState.createPatchState.isInProgress,
650
+ })}
651
+ >
652
+ <PanelLoadingIndicator
653
+ isLoading={projectOverviewState.createPatchState.isInProgress}
654
+ />
655
+ <div className="project-overview__patch__content__progress-msg">
656
+ {projectOverviewState.createPatchState.message}
657
+ </div>
658
+ <div className="panel__content__form">
659
+ <div className="panel__content__form__section">
660
+ <div className="panel__content__form__section__header__label">
661
+ Source Version
662
+ </div>
663
+ <CustomSelectorInput
664
+ className="project-overview__patch__source__version__selector"
665
+ options={versionOptions}
666
+ onChange={onVersionOptionChange}
667
+ value={selectedVersionOption}
668
+ placeholder={'Select source version'}
669
+ isClearable={true}
670
+ escapeClearsValue={true}
671
+ darkMode={true}
672
+ />
673
+ </div>
674
+ </div>
675
+ <div className="panel__content__form">
676
+ <div className="panel__content__form__section">
677
+ <div className="panel__content__form__section__header__label">
678
+ Workspace Name
679
+ </div>
680
+ <input
681
+ className="panel__content__form__section__input"
682
+ title="Workspace Name"
683
+ spellCheck={false}
684
+ value={workspaceName}
685
+ placeholder="Workspace Name"
686
+ onChange={changeWorkspaceName}
687
+ />
688
+ </div>
689
+ </div>
690
+ <div className="panel__content__form__section project-overview__patch__workspace__type__button">
691
+ <PanelFormBooleanField
692
+ name="Group Workspace"
693
+ prompt="Group workspaces can be accessed by all users in the project"
694
+ value={isGroupWorkspace}
695
+ isReadOnly={false}
696
+ update={toggleGroupWorkspace}
697
+ />
698
+ </div>
699
+ <div className="panel__content__form__section__list__new-item__add">
700
+ <button
701
+ disabled={
702
+ projectOverviewState.createPatchState.isInProgress ||
703
+ !editorStore.sdlcServerClient.features.canCreateVersion ||
704
+ !selectedVersionOption
705
+ }
706
+ onClick={(): void => createPatch()}
707
+ className="panel__content__form__section__list__new-item__add-btn btn btn--dark project-overview__patch__create__button"
708
+ >
709
+ Create
710
+ </button>
711
+ </div>
712
+ </div>
713
+ <div className="project-overview__patch__release">
714
+ <div className="panel__header">
715
+ <div className="panel__header__title">
716
+ <div className="panel__header__title__content">Release Patch</div>
717
+ </div>
718
+ </div>
719
+ <div className="project-overview__patch__release">
720
+ <PanelLoadingIndicator isLoading={isDispatchingAction} />
721
+ <>
722
+ <div className="panel__content__form">
723
+ <div className="panel__content__form__section">
724
+ <div className="panel__content__form__section__header__label">
725
+ Patch
726
+ </div>
727
+ <CustomSelectorInput
728
+ className="project-overview__patch__source__version__selector"
729
+ options={patches.map((p) => ({
730
+ label: p.patchReleaseVersionId.id,
731
+ value: p,
732
+ }))}
733
+ onChange={onPatchOptionChange}
734
+ value={selectedPatchOption}
735
+ placeholder={'Select patch you want to release'}
736
+ isClearable={true}
737
+ escapeClearsValue={true}
738
+ darkMode={true}
739
+ />
740
+ </div>
741
+ </div>
742
+ <div className="project-overview__patch__release__content">
743
+ {patchBlurb}
744
+ </div>
745
+ <div className="panel__content__form__section__list__new-item__add">
746
+ <button
747
+ className="panel__content__form__section__list__new-item__add-btn btn btn--dark project-overview__patch__release__button"
748
+ onClick={createPatchRelease}
749
+ disabled={!canCreateVersion && !selectedPatchOption}
750
+ title={'Create a patch release'}
751
+ >
752
+ Release
753
+ </button>
754
+ </div>
755
+ </>
756
+ </div>
757
+ </div>
758
+
759
+ <div className="project-overview__release__info">
760
+ <div className="panel project-overview__release__info__reviews">
761
+ <div className="panel__header">
762
+ <div className="panel__header__title">
763
+ <div className="panel__header__title__content">
764
+ COMMITTED REVIEWS
765
+ </div>
766
+ <div
767
+ className="side-bar__panel__title__info"
768
+ title="All committed reviews in the patch since it got created"
769
+ >
770
+ <InfoCircleIcon />
771
+ </div>
772
+ </div>
773
+ <div
774
+ className="side-bar__panel__header__changes-count"
775
+ data-testid={
776
+ LEGEND_STUDIO_TEST_ID.SIDEBAR_PANEL_HEADER__CHANGES_COUNT
777
+ }
778
+ >
779
+ {committedReviews.length}
780
+ </div>
781
+ </div>
782
+ <PanelContent>
783
+ {committedReviews.map((review) => (
784
+ <button
785
+ key={review.id}
786
+ className="side-bar__panel__item workspace-updater__review__link"
787
+ tabIndex={-1}
788
+ onClick={(): void =>
789
+ applicationStore.navigationService.navigator.visitAddress(
790
+ applicationStore.navigationService.navigator.generateAddress(
791
+ generateReviewRoute(review.projectId, review.id),
792
+ ),
793
+ )
794
+ }
795
+ title="See review"
796
+ >
797
+ <div className="workspace-updater__review">
798
+ <span className="workspace-updater__review__name">
799
+ {review.title}
800
+ </span>
801
+ <span className="workspace-updater__review__info">
802
+ {review.author.name}
803
+ </span>
804
+ </div>
805
+ </button>
806
+ ))}
807
+ </PanelContent>
808
+ </div>
809
+ </div>
810
+ </div>
811
+ </div>
812
+ );
813
+ });
814
+
486
815
  const VersionsViewer = observer(() => {
487
816
  const editorStore = useEditorStore();
488
817
  const applicationStore = useLegendStudioApplicationStore();
@@ -815,6 +1144,7 @@ export const ProjectOverviewActivityBar = observer(() => {
815
1144
  },
816
1145
  { mode: PROJECT_OVERVIEW_ACTIVITY_MODE.VERSIONS, title: 'Versions' },
817
1146
  { mode: PROJECT_OVERVIEW_ACTIVITY_MODE.WORKSPACES, title: 'Workspaces' },
1147
+ { mode: PROJECT_OVERVIEW_ACTIVITY_MODE.PATCH, title: 'Patch' },
818
1148
  ]
819
1149
  .filter((activity): activity is ProjectOverviewActivityDisplay =>
820
1150
  Boolean(activity),
@@ -876,6 +1206,8 @@ export const ProjectOverview = observer(() => {
876
1206
  return <VersionsViewer />;
877
1207
  case PROJECT_OVERVIEW_ACTIVITY_MODE.WORKSPACES:
878
1208
  return <WorkspacesViewer />;
1209
+ case PROJECT_OVERVIEW_ACTIVITY_MODE.PATCH:
1210
+ return <PatchEditor />;
879
1211
  default:
880
1212
  return null;
881
1213
  }
@@ -115,7 +115,7 @@ const ProjectViewerStatusBar = observer(() => {
115
115
  onClick={(): void =>
116
116
  applicationStore.navigationService.navigator.visitAddress(
117
117
  applicationStore.navigationService.navigator.generateAddress(
118
- generateSetupRoute(projectId),
118
+ generateSetupRoute(projectId, undefined),
119
119
  ),
120
120
  )
121
121
  }
@@ -87,7 +87,7 @@ const WorkspaceReviewStatusBar = observer(() => {
87
87
  onClick={(): void =>
88
88
  applicationStore.navigationService.navigator.visitAddress(
89
89
  applicationStore.navigationService.navigator.generateAddress(
90
- generateSetupRoute(reviewStore.projectId),
90
+ generateSetupRoute(reviewStore.projectId, undefined),
91
91
  ),
92
92
  )
93
93
  }
@@ -25,14 +25,23 @@ import {
25
25
  PanelDivider,
26
26
  PanelForm,
27
27
  PanelFormBooleanField,
28
+ CustomSelectorInput,
28
29
  } from '@finos/legend-art';
29
30
  import { flowResult } from 'mobx';
30
- import { type Project, WorkspaceType } from '@finos/legend-server-sdlc';
31
+ import { type Project, WorkspaceType, Patch } from '@finos/legend-server-sdlc';
31
32
  import { useApplicationStore } from '@finos/legend-application';
32
33
  import { LEGEND_STUDIO_DOCUMENTATION_KEY } from '../../__lib__/LegendStudioDocumentation.js';
33
- import { useWorkspaceSetupStore } from './WorkspaceSetup.js';
34
+ import {
35
+ DEFAULT_WORKSPACE_SOURCE,
36
+ useWorkspaceSetupStore,
37
+ } from './WorkspaceSetup.js';
34
38
  import { DocumentationLink } from '@finos/legend-lego/application';
35
39
 
40
+ export interface PatchOption {
41
+ label: string;
42
+ value: Patch | string;
43
+ }
44
+
36
45
  export const CreateWorkspaceModal = observer(
37
46
  (props: { selectedProject: Project }) => {
38
47
  const { selectedProject } = props;
@@ -41,6 +50,35 @@ export const CreateWorkspaceModal = observer(
41
50
  const workspaceNameInputRef = useRef<HTMLInputElement>(null);
42
51
  const [workspaceName, setWorkspaceName] = useState('');
43
52
  const [isGroupWorkspace, setIsGroupWorkspace] = useState<boolean>(true);
53
+ const [patchOptions] = useState<PatchOption[]>(
54
+ [
55
+ {
56
+ label: DEFAULT_WORKSPACE_SOURCE,
57
+ value: DEFAULT_WORKSPACE_SOURCE,
58
+ } as PatchOption,
59
+ ].concat(
60
+ setupStore.patches.map((p) => ({
61
+ label: `patch/${p.patchReleaseVersionId.id}`,
62
+ value: p,
63
+ })),
64
+ ),
65
+ );
66
+ const [selectedPatchOption, setSelectedPatchOption] =
67
+ useState<PatchOption | null>({
68
+ label: DEFAULT_WORKSPACE_SOURCE,
69
+ value: DEFAULT_WORKSPACE_SOURCE,
70
+ });
71
+
72
+ const onPatchOptionChange = (val: PatchOption | null): void => {
73
+ if (
74
+ (val !== null || selectedPatchOption !== null) &&
75
+ (!val ||
76
+ !selectedPatchOption ||
77
+ val.value !== selectedPatchOption.value)
78
+ ) {
79
+ setSelectedPatchOption(val);
80
+ }
81
+ };
44
82
 
45
83
  const workspaceAlreadyExists = Boolean(
46
84
  setupStore.workspaces.find(
@@ -49,7 +87,12 @@ export const CreateWorkspaceModal = observer(
49
87
  ((workspace.workspaceType === WorkspaceType.GROUP &&
50
88
  isGroupWorkspace) ||
51
89
  (workspace.workspaceType === WorkspaceType.USER &&
52
- !isGroupWorkspace)),
90
+ !isGroupWorkspace)) &&
91
+ ((!workspace.source &&
92
+ !(selectedPatchOption?.value instanceof Patch)) ||
93
+ (selectedPatchOption?.value instanceof Patch &&
94
+ workspace.source ===
95
+ selectedPatchOption.value.patchReleaseVersionId.id)),
53
96
  ),
54
97
  );
55
98
  const createWorkspace = (): void => {
@@ -60,6 +103,9 @@ export const CreateWorkspaceModal = observer(
60
103
  flowResult(
61
104
  setupStore.createWorkspace(
62
105
  selectedProject.projectId,
106
+ selectedPatchOption?.value instanceof Patch
107
+ ? selectedPatchOption.value.patchReleaseVersionId.id
108
+ : undefined,
63
109
  workspaceName,
64
110
  isGroupWorkspace ? WorkspaceType.GROUP : WorkspaceType.USER,
65
111
  ),
@@ -130,7 +176,20 @@ export const CreateWorkspaceModal = observer(
130
176
  : ''
131
177
  }
132
178
  />
133
-
179
+ <div className="workspace-setup__create-workspace-modal__form__workspace--source">
180
+ <div className="workspace-setup__create-workspace-modal__form__workspace--source__label">
181
+ Workspace Source
182
+ </div>
183
+ <CustomSelectorInput
184
+ className="workspace-setup__create-workspace-modal__form__workspace--source__selector"
185
+ options={patchOptions}
186
+ onChange={onPatchOptionChange}
187
+ value={selectedPatchOption}
188
+ isClearable={true}
189
+ escapeClearsValue={true}
190
+ darkMode={true}
191
+ />
192
+ </div>
134
193
  <PanelFormBooleanField
135
194
  name="Group Workspace"
136
195
  prompt="Group workspaces can be accessed by all users in the project"
@@ -33,13 +33,20 @@ export const formatWorkspaceOptionLabel = (
33
33
  option: WorkspaceOption,
34
34
  ): React.ReactNode => (
35
35
  <div className="workspace-selector__option">
36
- <div className="workspace-selector__option__icon">
37
- {option.value.workspaceType === WorkspaceType.GROUP ? (
38
- <UsersIcon />
39
- ) : (
40
- <UserIcon />
36
+ <div className="workspace-selector__option__label">
37
+ <div className="workspace-selector__option__icon">
38
+ {option.value.workspaceType === WorkspaceType.GROUP ? (
39
+ <UsersIcon />
40
+ ) : (
41
+ <UserIcon />
42
+ )}
43
+ </div>
44
+ <div className="workspace-selector__option__name">{option.label}</div>
45
+ </div>
46
+ <div className="workspace-selector__option__source">
47
+ {option.value.source && (
48
+ <div className="workspace-selector__option__source__patch">{`patch/${option.value.source}`}</div>
41
49
  )}
42
50
  </div>
43
- <div className="workspace-selector__option__name">{option.label}</div>
44
51
  </div>
45
52
  );
@@ -62,6 +62,8 @@ const WorkspaceSetupStoreContext = createContext<
62
62
  WorkspaceSetupStore | undefined
63
63
  >(undefined);
64
64
 
65
+ export const DEFAULT_WORKSPACE_SOURCE = 'HEAD';
66
+
65
67
  const WorkspaceSetupStoreProvider: React.FC<{
66
68
  children: React.ReactNode;
67
69
  }> = ({ children }) => {
@@ -175,6 +177,7 @@ export const WorkspaceSetup = withWorkspaceSetupStore(
175
177
  applicationStore.navigationService.navigator.goToLocation(
176
178
  generateEditorRoute(
177
179
  setupStore.currentProject.projectId,
180
+ setupStore.currentWorkspace.source,
178
181
  setupStore.currentWorkspace.workspaceId,
179
182
  setupStore.currentWorkspace.workspaceType,
180
183
  ),
@@ -331,5 +331,5 @@ export interface DSL_LegendStudioApplicationPlugin_Extension
331
331
  * Get a string of the Pure grammar element name for auto-folding the element
332
332
  * (e.g. Diagram, or Text)
333
333
  */
334
- getAutoFoldingCandidateToken?(): string[];
334
+ getExtraGrammarTextEditorAutoFoldingElementCreatorKeywords?(): string[];
335
335
  }