Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI Config Editor Feature #3

Merged
merged 10 commits into from
May 17, 2021
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,16 @@ There are a few self-hosted web apps, that serve a similar purpose to Dashy. Inc

### Credits 🏆

The app makes use of the following components, kudos to their respective authors
And the app itself is built with [Vue.js](https://github.com/vuejs/vue) ![vue-logo](https://i.ibb.co/xqKW6h5/vue-logo.png)

And wouldn't have been quite possible, without the following components, kudos to their respective authors
- [`vue-select`](https://github.com/sagalbot/vue-select) - Dropdown component by @sagalbot
- [`vue-js-modal`](https://github.com/euvl/vue-js-modal) - Modal component by @euvl
- [`v-tooltip`](https://github.com/Akryum/v-tooltip) - Tooltip component by @Akryum

And the app itself is built with [Vue.js](https://github.com/vuejs/vue) ![vue-logo](https://i.ibb.co/xqKW6h5/vue-logo.png)
- [`vue-material-tabs`](https://github.com/jairoblatt/vue-material-tabs) - Tab view component by @jairoblatt
- [`VJsoneditor`](https://github.com/yansenlei/VJsoneditor) - Interactive JSON editor component by @yansenlei
- Forked from [JsonEditor](https://github.com/josdejong/jsoneditor) by @josdejong
- [`vue-toasted`](https://github.com/shakee93/vue-toasted) - Toast notification component by @shakee93

### License 📜

Expand Down
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@
"dependencies": {
"connect": "^3.7.0",
"register-service-worker": "^1.6.2",
"remedial": "^1.0.8",
"serve-static": "^1.14.1",
"v-jsoneditor": "^1.4.2",
"v-tooltip": "^2.1.3",
"vue": "^2.6.10",
"vue-cli-plugin-yaml": "^1.0.2",
"vue-js-modal": "^2.0.0-rc.6",
"vue-material-tabs": "^0.0.7",
"vue-router": "^3.0.3",
"vue-select": "^3.11.2"
"vue-select": "^3.11.2",
"vue-toasted": "^1.1.28"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.5.12",
Expand Down Expand Up @@ -61,4 +65,4 @@
"> 1%",
"last 2 versions"
]
}
}
13 changes: 10 additions & 3 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import Header from '@/components/PageStrcture/Header.vue';
import Footer from '@/components/PageStrcture/Footer.vue';
import Defaults from '@/utils/defaults';
import Defaults, { localStorageKeys } from '@/utils/defaults';
import conf from '../public/conf.yml';

export default {
Expand All @@ -27,10 +27,17 @@ export default {
/* Returns either page info from the config, or default values */
getPageInfo(pageInfo) {
const defaults = Defaults.pageInfo;

let localPageInfo;
try {
localPageInfo = JSON.parse(localStorage[localStorageKeys.PAGE_INFO]);
} catch (e) {
localPageInfo = {};
}
if (pageInfo) {
return {
title: pageInfo.title || defaults.title,
description: pageInfo.description || defaults.description,
title: localPageInfo.title || pageInfo.title || defaults.title,
description: localPageInfo.description || pageInfo.description || defaults.description,
navLinks: pageInfo.navLinks || defaults.navLinks,
};
}
Expand Down
1 change: 1 addition & 0 deletions src/assets/interface-icons/config-delete-local.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/interface-icons/config-download-file.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/interface-icons/config-edit-json.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/interface-icons/config-editor.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/interface-icons/config-meta-data.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
204 changes: 204 additions & 0 deletions src/components/Configuration/ConfigContainer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
<template>
<Tabs :navAuto="true" name="Add Item" ref="tabView">
<TabItem name="Config">
<div class="main-options-container">
<h2>Configuration Options</h2>
<a href="/conf.yml" download class="hyperlink-wrapper">
<button class="config-button center">
<DownloadIcon class="button-icon"/>
Download Config
</button>
</a>
<button class="config-button center" @click="goToEdit()">
<EditIcon class="button-icon"/>
Edit Sections
</button>
<button class="config-button center" @click="goToMetaEdit()">
<MetaDataIcon class="button-icon"/>
Edit Meta Data
</button>
<button class="config-button center" @click="resetLocalSettings()">
<DeleteIcon class="button-icon"/>
Reset Local Settings
</button>
</div>
</TabItem>
<TabItem name="Edit Sections">
<JsonEditor :sections="sections" />
</TabItem>
<TabItem name="View Raw YAML">
<pre>{{this.jsonParser(this.config)}}</pre>
<a class="download-button" href="/conf.yml" download>Download Config</a>
</TabItem>
<TabItem name="Edit Site Meta">
<EditSiteMeta :config="config" />
</TabItem>
</Tabs>
</template>

<script>

import JsonToYaml from '@/utils/JsonToYaml';
import EditSiteMeta from '@/components/Configuration/EditSiteMeta';
import JsonEditor from '@/components/Configuration/JsonEditor';
import DownloadIcon from '@/assets/interface-icons/config-download-file.svg';
import DeleteIcon from '@/assets/interface-icons/config-delete-local.svg';
import EditIcon from '@/assets/interface-icons/config-edit-json.svg';
import MetaDataIcon from '@/assets/interface-icons/config-meta-data.svg';

export default {
name: 'ConfigContainer',
data() {
return {
jsonParser: JsonToYaml,
};
},
props: {
config: Object,
},
computed: {
sections: function getSections() {
return this.config.sections;
},
},
components: {
EditSiteMeta,
JsonEditor,
DownloadIcon,
DeleteIcon,
EditIcon,
MetaDataIcon,
},
methods: {
/* Seletcs the edit tab of the tab view */
goToEdit() {
const itemToSelect = this.$refs.tabView.navItems[1];
this.$refs.tabView.activeTabItem({ tabItem: itemToSelect, byUser: true });
},
goToMetaEdit() {
const itemToSelect = this.$refs.tabView.navItems[3];
this.$refs.tabView.activeTabItem({ tabItem: itemToSelect, byUser: true });
},
/* Checks that the user is sure, then resets site-wide local storage, and reloads page */
resetLocalSettings() {
const msg = 'This will remove all user settings from local storage, '
+ 'but won\'t effect your \'conf.yml\' file. '
+ 'It is recommend to make a backup of your modified YAML settings first.\n\n'
+ 'Are you sure you want to proceed?';
const isTheUserSure = confirm(msg); // eslint-disable-line no-alert, no-restricted-globals
if (isTheUserSure) {
localStorage.clear();
this.$toasted.show('Data cleared succesfully');
setTimeout(() => {
location.reload(); // eslint-disable-line no-restricted-globals
}, 1900);
}
},
},
};
</script>

<style scoped lang="scss">

pre {
color: var(--config-code-color);
background: var(--config-code-background);
}

a.config-button, button.config-button {
display: flex;
align-items: center;
padding: 0.5rem 1rem;
margin: 0.25rem auto;
font-size: 1.2rem;
background: var(--config-settings-background);
color: var(--config-settings-color);
border: 1px solid var(--config-settings-color);
border-radius: var(--curve-factor);
text-decoration: none;
cursor: pointer;
margin: 0.5rem auto;
width: 18rem;
svg.button-icon {
path {
fill: var(--config-settings-color);
}
width: 1rem;
height: 1rem;
padding: 0.2rem;
margin-right: 0.5rem;
}
&:hover {
background: var(--config-settings-color);
color: var(--config-settings-background);
svg path {
fill: var(--config-settings-background);
}
}
}

a.download-button {
position: absolute;
top: 2px;
right: 2px;
padding: 0.25rem 0.5rem;
font-size: 1rem;
color: var(--config-settings-background);
border-radius: var(--curve-factor);
cursor: pointer;
&:hover {
background: var(--config-settings-color);
}
}

.tab-item {
overflow-y: auto;
}

a.hyperlink-wrapper {
margin: 0 auto;
text-decoration: none;
}

.main-options-container {
display: flex;
flex-direction: column;
padding-top: 2rem;
background: var(--background-darker);
height: calc(100% - 2rem);
h2 {
margin: 1rem auto;
color: var(--config-settings-color);
}
}
</style>

<style lang="scss">
.tab__pagination {
background: var(--config-settings-background);
color: var(--config-settings-color);
.tab__nav__items .tab__nav__item {
span {
color: var(--config-settings-color);
}
&:hover {
background: var(--config-settings-color) !important;
span {
color: var(--config-settings-background);
}
}
&.active {
span {
font-weight: bold;
color: var(--config-settings-color) !important;
}
}
}
.tab__nav__items .tab__nav__item.active {
border-bottom: 2px solid var(--config-settings-color);
}
hr.tab__slider {
background: var(--config-settings-color);
}
}
</style>
Loading