Skip to content

Commit

Permalink
chore(ui): improve empty state of the namespace files editor (#7495)
Browse files Browse the repository at this point in the history
  • Loading branch information
Piyush-r-bhaskar authored Feb 28, 2025
1 parent d8295ef commit 60a84a4
Show file tree
Hide file tree
Showing 4 changed files with 299 additions and 20 deletions.
20 changes: 17 additions & 3 deletions ui/src/components/inputs/EditorSidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,7 @@
...mapState({
flow: (state) => state.flow.flow,
explorerVisible: (state) => state.editor.explorerVisible,
treeRefresh: (state) => state.editor.treeRefresh,
}),
folders() {
function extractPaths(basePath = "", array) {
Expand Down Expand Up @@ -504,9 +505,9 @@
this.renderNodes(items);
this.items = this.sorted(this.items);
}
if (node.level >= 1) {
this.$store.commit("editor/setTreeData", this.items);
resolve(this.items);
} else if (node.level >= 1) {
const payload = {
namespace: this.currentNS ?? this.$route.params.namespace,
path: this.getPath(node),
Expand Down Expand Up @@ -1059,6 +1060,19 @@
immediate: true,
deep: true,
},
treeRefresh: {
async handler() {
if (this.$refs.tree) {
this.items = undefined;
const items = await this.readDirectory({
namespace: this.currentNS ?? this.$route.params.namespace
});
this.renderNodes(items);
this.items = this.sorted(this.items);
}
},
immediate: true,
},
},
};
</script>
Expand Down
288 changes: 272 additions & 16 deletions ui/src/components/inputs/EditorView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -154,21 +154,83 @@
<KeyShortcuts />
</template>
</editor>
<section v-else class="no-tabs-opened">
<div class="img" />

<h2>{{ $t("namespace_editor.empty.title") }}</h2>
<p><span>{{ $t("namespace_editor.empty.message") }}</span></p>

<iframe
width="60%"
height="400px"
src="https://www.youtube.com/embed/o-d-GaXUiKQ?si=TTjV8jgRg6-lj_cC"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen
/>
</section>
<div v-else class="no-tabs-opened">
<div class="img mb-1" />

<div>
<h5 class="mb-0 fw-bold">
{{ $t("namespace_editor.empty.title") }}
</h5>
<p>
{{ $t("namespace_editor.empty.create_message") }}
</p>
</div>

<div class="empty-state-actions mt-1">
<el-dropdown>
<el-button :icon="Plus" type="primary">
{{ $t("create") }}
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="createFile">
<FilePlus class="me-2" />
{{ $t("namespace files.create.file") }}
</el-dropdown-item>
<el-dropdown-item @click="createFolder">
<FolderPlus class="me-2" />
{{ $t("namespace files.create.folder") }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<input
ref="filePicker"
type="file"
multiple
class="hidden"
@change="handleFileImport"
>
<input
ref="folderPicker"
type="file"
webkitdirectory
mozdirectory
msdirectory
odirectory
directory
class="hidden"
@change="handleFileImport"
>
<el-dropdown>
<el-button :icon="Download" type="primary">
{{ $t("import") }}
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="$refs.filePicker.click()">
<File class="me-2" />
{{ $t("namespace files.import.files") }}
</el-dropdown-item>
<el-dropdown-item @click="$refs.folderPicker.click()">
<Folder class="me-2" />
{{ $t("namespace files.import.folder") }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<el-divider>{{ $t("namespace_editor.empty.video_message") }}</el-divider>

<div class="video-container">
<iframe
src="https://www.youtube.com/embed/o-d-GaXUiKQ?si=TTjV8jgRg6-lj_cC"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen
/>
</div>
</div>
</template>
<NoCode
v-else
Expand Down Expand Up @@ -320,6 +382,52 @@
</el-button>
</template>
</el-dialog>
<el-dialog
v-model="dialog.visible"
:title="dialog.type === 'file' ? $t('namespace files.create.file') : $t('namespace files.create.folder')"
width="500"
@keydown.enter.prevent="dialog.name ? dialogHandler() : undefined"
>
<div class="pb-1">
<span>{{ $t(`namespace files.dialog.name.${dialog.type}`) }}</span>
</div>
<el-input
ref="creation_name"
v-model="dialog.name"
size="large"
class="mb-3"
/>
<div class="py-1">
<span>{{ $t("namespace files.dialog.parent_folder") }}</span>
</div>
<el-select
v-model="dialog.folder"
clearable
size="large"
class="mb-3 w-100"
>
<el-option
v-for="folder in folders"
:key="folder"
:value="folder"
:label="folder"
/>
</el-select>
<template #footer>
<div>
<el-button @click="dialog.visible = false">
{{ $t("cancel") }}
</el-button>
<el-button
type="primary"
:disabled="!dialog.name"
@click="dialogHandler"
>
{{ $t("namespace files.create.label") }}
</el-button>
</div>
</template>
</el-dialog>
</template>

<script setup>
Expand All @@ -333,6 +441,12 @@
import MenuClose from "vue-material-design-icons/MenuClose.vue";
import Close from "vue-material-design-icons/Close.vue";
import CircleMedium from "vue-material-design-icons/CircleMedium.vue";
import FilePlus from "vue-material-design-icons/FilePlus.vue";
import FolderPlus from "vue-material-design-icons/FolderPlus.vue";
import Download from "vue-material-design-icons/Download.vue";
import Plus from "vue-material-design-icons/Plus.vue";
import File from "vue-material-design-icons/File.vue";
import Folder from "vue-material-design-icons/Folder.vue";
import TypeIcon from "../utils/icons/Type.vue"
Expand Down Expand Up @@ -1380,6 +1494,102 @@
const blob = new Blob([flowYaml.value], {type: "text/yaml"});
localUtils.downloadUrl(window.URL.createObjectURL(blob), "flow.yaml");
};
const dialog = ref({
visible: false,
type: "file",
name: undefined,
folder: undefined,
});
const createFile = () => {
dialog.value = {
visible: true,
type: "file",
name: undefined,
folder: undefined
};
store.commit("editor/toggleExplorerVisibility", true);
};
const createFolder = () => {
dialog.value = {
visible: true,
type: "folder",
name: undefined,
folder: undefined
};
store.commit("editor/toggleExplorerVisibility", true);
};
const folders = computed(() => {
function extractPaths(basePath = "", array) {
const paths = [];
array?.forEach((item) => {
if (item.type === "Directory") {
const folderPath = `${basePath}${item.fileName}`;
paths.push(folderPath);
paths.push(
...extractPaths(
`${folderPath}/`,
item.children ?? [],
),
);
}
});
return paths;
}
return extractPaths(undefined, store.state.editor.treeData);
});
const dialogHandler = async () => {
try {
const path = dialog.value.folder
? `${dialog.value.folder}/${dialog.value.name}`
: dialog.value.name;
if (dialog.value.type === "file") {
await store.dispatch("namespace/createFile", {
namespace: props.namespace ?? route.params.namespace,
path,
content: "",
});
} else {
await store.dispatch("namespace/createDirectory", {
namespace: props.namespace ?? route.params.namespace,
path,
});
}
dialog.value.visible = false;
store.commit("editor/refreshTree");
if (dialog.value.type === "file") {
store.commit("editor/changeOpenedTabs", {
action: "open",
name: dialog.value.name,
path,
extension: dialog.value.name.split(".").pop()
});
}
} catch (error) {
console.error(error);
toast().error(t("namespace files.create.error"));
}
};
const handleFileImport = async (event) => {
const files = event.target.files;
for (const file of files) {
const content = await new Promise((resolve) => {
const reader = new FileReader();
reader.onload = (e) => resolve(e.target.result);
reader.readAsArrayBuffer(file);
});
const path = file.webkitRelativePath || file.name;
await store.dispatch("namespace/importFileDirectory", {
namespace: props.namespace ?? route.params.namespace,
content,
path
});
}
store.commit("editor/refreshTree");
event.target.value = "";
};
</script>
<style lang="scss" scoped>
Expand Down Expand Up @@ -1506,12 +1716,23 @@
}
.no-tabs-opened {
margin-top: 5em 10em;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
max-width: 800px;
width: 100%;
padding: 2rem;
padding-bottom: 0;
margin: 0 auto;
height: 100%;
.img {
background: url("../../assets/empty-ns-files.png") no-repeat center;
background-size: contain;
width: 180px;
height: 180px;
}
h2 {
Expand All @@ -1523,6 +1744,41 @@
p {
line-height: 22px;
font-size: 14px;
margin-bottom: 1rem;
color: var(--ks-content-secondary);
}
.empty-state-actions {
margin-bottom: 2.5rem;
display: flex;
justify-content: center;
gap: 1rem;
width: 100%;
}
:deep(.el-divider__text) {
font-size: 12px;
padding: 0 15px;
color: var(--ks-content-secondary);
background-color: #f9f9fa;
html.dark & {
background-color: #1C1E27;
}
}
.video-container {
width: 100%;
margin-top: 1rem;
border: 1px solid var(--ks-border-primary);
border-radius: 0.5rem;
iframe {
width: 100%;
min-height: 380px;
height: auto;
}
}
.hidden {
display: none;
}
}
Expand Down
8 changes: 8 additions & 0 deletions ui/src/stores/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export default {
current: undefined,
tabs: [],
view: undefined,
treeData: [],
},
mutations: {
updateOnboarding(state) {
Expand Down Expand Up @@ -100,5 +101,12 @@ export default {
changeView(state, view) {
state.view = view;
},
refreshTree(state) {
state.explorerVisible = true;
state.treeRefresh = Date.now();
},
setTreeData(state, data) {
state.treeData = data;
},
},
};
Loading

0 comments on commit 60a84a4

Please sign in to comment.