Repository: kestra-io/kestra
Issue: Blueprint adjustments (2)
Labels: bug
backend
frontend

### Feature description

- [x] When clicking on the "back" arrow, users are moved to the blueprints homepage -> would be nice that when the user search, the "go back" button goes back to the search results. Same if he filtered on a tag.

- [x] In a similar way, in the editor view, users should be able to get back their blueprint page whenever they click on another view button (doc, topo, etc.). Atm it's coming back to the blueprint default page (home)

- [x] Too many space between source code and corresponding title

![image](https://github.com/kestra-io/kestra/assets/46634684/ab5071bb-3422-4f59-afc0-8fd4e65e184b)

- [x] add the possibility to move the topology horizontaly/verticically. If possible always set horizontal in Blueprints main section.
- [x] Keep the breadcrumb (like in other page headers)

Issue comments:
it would be great to also add this:

- [x] open a blueprint in a new tab - currently, you can only open it on the same page, making it difficult to inspect/browse  blueprints
@Ben8t regarding the "too many space" between source code and editor, that's because there's a copy button in the editor header which creates this offsets. Should I overlap the copy button in top-right corner of the actual editor (eventually hiding part of the id / namespace if it is a really long one) ?

For the topology orientation, I replicated the behaviour of the topology view: if on blueprints standalone page you are able to switch your orientation but when side-by-side with the editor you can't change the orientation and it is taken from your local storage (ie your preferences)
> it would be great to also add this:
> 
> * [x]  open a blueprint in a new tab - currently, you can only open it on the same page, making it difficult to inspect/browse  blueprints

Do you mean CTRL click to open in a new browser tab ?
@brian-mulier-p 

>  regarding the "too many space" between source code and editor, that's because there's a copy button in the editor header which creates this offsets. Should I overlap the copy button in top-right corner of the actual editor (eventually hiding part of the id / namespace if it is a really long one) ?

Yes go for it, so long id/namespace would be something very rare coming from internal blueprint (so it's 0.0001% chance of occurring atm)

Pull request review comments:
We should have native link only, we should not decided for the user to open or not a blank and let them decide with a normal link 

Patch that resolved the issue:
diff --git a/ui/src/components/flows/blueprints/BlueprintDetail.vue b/ui/src/components/flows/blueprints/BlueprintDetail.vue
index f2aad90431..9687cd7e6e 100644
--- a/ui/src/components/flows/blueprints/BlueprintDetail.vue
+++ b/ui/src/components/flows/blueprints/BlueprintDetail.vue
@@ -42,15 +42,15 @@
                             :namespace="parsedFlow.namespace"
                             :flow-graph="flowGraph"
                             :source="blueprint.flow"
+                            :view-type="embed ? 'source-blueprints' : 'blueprints'"
                             is-read-only
-                            graph-only
                         />
                     </div>
                 </el-card>
                 <h5>{{ $t("source") }}</h5>
-                <editor :read-only="true" :full-height="false" :minimap="false" :model-value="blueprint.flow" lang="yaml">
+                <editor class="position-relative" :read-only="true" :full-height="false" :minimap="false" :model-value="blueprint.flow" lang="yaml">
                     <template #nav>
-                        <div style="text-align: right">
+                        <div class="position-absolute copy-wrapper">
                             <el-tooltip trigger="click" content="Copied" placement="left" :auto-close="2000">
                                 <el-button text round :icon="icon.ContentCopy" @click="copy(blueprint.flow)" />
                             </el-tooltip>
@@ -112,7 +112,7 @@
                 if (this.embed) {
                     this.$emit("back");
                 } else {
-                    this.$router.push({name: "blueprints", params: this.$route.params})
+                    this.$router.push({name: "blueprints"})
                 }
             },
             copy(text) {
@@ -187,6 +187,12 @@
         }
     }
 
+    .copy-wrapper {
+        right: $spacer;
+        top: $spacer;
+        z-index: 1
+    }
+
     .blueprint-container {
         height: 100%;
 
diff --git a/ui/src/components/inputs/EditorView.vue b/ui/src/components/inputs/EditorView.vue
index 52b7c1b0d7..154d512425 100644
--- a/ui/src/components/inputs/EditorView.vue
+++ b/ui/src/components/inputs/EditorView.vue
@@ -621,7 +621,7 @@
             @restartGuidedTour="() => persistViewType('source')"
         />
         <div class="slider" @mousedown="dragEditor" v-if="combinedEditor" />
-        <Blueprints v-if="viewType === 'source-blueprints'" embed class="combined-right-view enhance-readability" :top-navbar="false" prevent-route-info />
+        <Blueprints :class="{'d-none': viewType !== 'source-blueprints'}" embed class="combined-right-view enhance-readability" :top-navbar="false" prevent-route-info />
         <div
             :class="viewType === 'source-topology' ? 'combined-right-view' : viewType === 'topology' ? 'vueflow': 'hide-view'"
         >
diff --git a/ui/src/components/inputs/LowCodeEditor.vue b/ui/src/components/inputs/LowCodeEditor.vue
index b1790eaffe..8abeddb14e 100644
--- a/ui/src/components/inputs/LowCodeEditor.vue
+++ b/ui/src/components/inputs/LowCodeEditor.vue
@@ -592,7 +592,7 @@
             </template>
 
             <Controls :show-interactive="false">
-                <ControlButton @click="toggleOrientation" v-if="viewType === 'topology'">
+                <ControlButton @click="toggleOrientation" v-if="['topology', 'blueprints'].includes(viewType)">
                     <SplitCellsVertical :size="48" v-if="!isHorizontal" />
                     <SplitCellsHorizontal v-if="isHorizontal" />
                 </ControlButton>
diff --git a/ui/src/override/components/flows/blueprints/Blueprints.vue b/ui/src/override/components/flows/blueprints/Blueprints.vue
index f5cc86d4bb..d7bbcc9f2c 100644
--- a/ui/src/override/components/flows/blueprints/Blueprints.vue
+++ b/ui/src/override/components/flows/blueprints/Blueprints.vue
@@ -41,24 +41,26 @@
             </template>
             <template #table>
                 <el-card class="blueprint-card hoverable" v-for="blueprint in blueprints" @click="goToDetail(blueprint.id)">
-                    <div class="side">
-                        <div class="title">
-                            {{ blueprint.title }}
+                    <component class="blueprint-link" :is="embed ? 'div' : 'router-link'" :to="embed ? undefined : {name: 'blueprints/view', params: {blueprintId: blueprint.id}}">
+                        <div class="side">
+                            <div class="title">
+                                {{ blueprint.title }}
+                            </div>
+                            <div class="tags text-uppercase">
+                                {{ tagsToString(blueprint.tags) }}
+                            </div>
+                            <div class="tasks-container">
+                                <task-icon :cls="task" only-icon v-for="task in [...new Set(blueprint.includedTasks)]" />
+                            </div>
                         </div>
-                        <div class="tags text-uppercase">
-                            {{ tagsToString(blueprint.tags) }}
+                        <div class="side buttons ms-auto">
+                            <el-tooltip trigger="click" content="Copied" placement="left" :auto-close="2000">
+                                <el-button class="hoverable" @click.prevent.stop="copy(blueprint.id)" :icon="icon.ContentCopy" size="large" text bg>
+                                    {{ $t('copy') }}
+                                </el-button>
+                            </el-tooltip>
                         </div>
-                        <div class="tasks-container">
-                            <task-icon :cls="task" only-icon v-for="task in [...new Set(blueprint.includedTasks)]" />
-                        </div>
-                    </div>
-                    <div class="side buttons ms-auto">
-                        <el-tooltip trigger="click" content="Copied" placement="left" :auto-close="2000">
-                            <el-button class="hoverable" @click.stop="copy(blueprint.id)" :icon="icon.ContentCopy" size="large" text bg>
-                                {{ $t('copy') }}
-                            </el-button>
-                        </el-tooltip>
-                    </div>
+                    </component>
                 </el-card>
             </template>
         </data-table>
@@ -67,6 +69,7 @@
 <script>
     import RouteContext from "../../../../mixins/routeContext";
     import DataTableActions from "../../../../mixins/dataTableActions";
+    import RestoreUrl from "../../../../mixins/restoreUrl";
     import SearchField from "../../../../components/layout/SearchField.vue";
     import DataTable from "../../../../components/layout/DataTable.vue";
     import BlueprintDetail from "../../../../components/flows/blueprints/BlueprintDetail.vue";
@@ -75,7 +78,7 @@
     import TaskIcon from "../../../../components/plugins/TaskIcon.vue";
 
     export default {
-        mixins: [DataTableActions, RouteContext],
+        mixins: [DataTableActions, RouteContext, RestoreUrl],
         inheritAttrs: false,
         components: {
             TaskIcon,
@@ -104,8 +107,6 @@
             goToDetail(blueprintId) {
                 if (this.embed) {
                     this.selectedBlueprintId = blueprintId;
-                } else {
-                    this.$router.push({name: "blueprints/view", params: {blueprintId}})
                 }
             },
             loadTags(beforeLoadBlueprintBaseUri) {
diff --git a/ui/src/styles/components/blueprints/blueprints.scss b/ui/src/styles/components/blueprints/blueprints.scss
index c9283afa9f..40ac5adeb9 100644
--- a/ui/src/styles/components/blueprints/blueprints.scss
+++ b/ui/src/styles/components/blueprints/blueprints.scss
@@ -107,64 +107,67 @@
                 border-bottom-right-radius: $border-radius;
             }
 
-            > :deep(.el-card__body) {
+            .blueprint-link {
                 display: flex;
-            }
-
-            .side {
-                margin: auto 0;
+                color: inherit;
+                text-decoration: inherit;
+                width: 100%;
 
-                &:first-child {
-                    overflow: hidden;
+                .side {
+                    margin: auto 0;
 
-                    & > * {
+                    &:first-child {
                         overflow: hidden;
-                        text-overflow: ellipsis;
+
+                        & > * {
+                            overflow: hidden;
+                            text-overflow: ellipsis;
+                        }
                     }
-                }
 
-                &.buttons {
-                    white-space: nowrap;
-                }
+                    &.buttons {
+                        white-space: nowrap;
+                    }
 
-                .title {
-                    font-weight: bold;
-                    font-size: $small-font-size;
-                }
+                    .title {
+                        font-weight: bold;
+                        font-size: $small-font-size;
+                    }
 
-                .tags {
-                    font-family: $font-family-monospace;
-                    font-weight: bold;
-                    font-size: $sub-sup-font-size;
-                    margin-bottom: calc(var(--spacer) / 2);
-                    color: $primary;
+                    .tags {
+                        font-family: $font-family-monospace;
+                        font-weight: bold;
+                        font-size: $sub-sup-font-size;
+                        margin-bottom: calc(var(--spacer) / 2);
+                        color: $primary;
 
-                    html.dark & {
-                        color: $pink;
+                        html.dark & {
+                            color: $pink;
+                        }
                     }
-                }
 
-                .tasks-container {
-                    $plugin-icon-size: calc(var(--font-size-base) + 0.4rem);
+                    .tasks-container {
+                        $plugin-icon-size: calc(var(--font-size-base) + 0.4rem);
 
-                    display: flex;
-                    flex-wrap: wrap;
-                    flex-direction: column;
-                    gap: calc(var(--spacer) / 4);
-                    width: fit-content;
-                    height: $plugin-icon-size;
+                        display: flex;
+                        flex-wrap: wrap;
+                        flex-direction: column;
+                        gap: calc(var(--spacer) / 4);
+                        width: fit-content;
+                        height: $plugin-icon-size;
 
-                    :deep(> *) {
-                        width: $plugin-icon-size;
-                        padding: 0.2rem;
-                        border-radius: $border-radius;
+                        :deep(> *) {
+                            width: $plugin-icon-size;
+                            padding: 0.2rem;
+                            border-radius: $border-radius;
 
-                        html.dark & {
-                            background-color: var(--bs-gray-900);
-                        }
+                            html.dark & {
+                                background-color: var(--bs-gray-900);
+                            }
 
-                        & * {
-                            margin-top: 0;
+                            & * {
+                                margin-top: 0;
+                            }
                         }
                     }
                 }


Test patch: