Merge pull request #38 from Lissy93/feature_workspace-view

Feature workspace view
This commit is contained in:
Alicia Sykes 2021-06-18 13:46:27 +01:00 committed by GitHub
commit b5f8c85522
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 588 additions and 42 deletions

View File

@ -299,6 +299,13 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWAREOR THE
OR OTHER DEALINGS IN THE SOFTWARE. OR OTHER DEALINGS IN THE SOFTWARE.
``` ```
#### Author 🦄
<p align="right"><img width="200" src="https://i.ibb.co/FnxqTfx/aht-bot-round.png" alt="Alicia Sykes"></p>
Developed by [Alicia Sykes](https://aliciasykes.com) ([@Lissy93](https://github.com/lissy93)) in 2021.
- **PGP Key**: [`0688 F8D3 4587 D954 E9E5 1FB8 FEDB 68F5 5C02 83A7`](https://keybase.io/aliciasykes/pgp_keys.asc?fingerprint=0688f8d34587d954e9e51fb8fedb68f55c0283a7)
- **BTC Address**: `3853bSxupMjvxEYfwGDGAaLZhTKxB2vEVC`
**[⬆️ Back to Top](#dashy)** **[⬆️ Back to Top](#dashy)**
--- ---

View File

@ -49,6 +49,20 @@ Maximum of 24mb of storage per user. Please do not repeatedly hit the endpoint,
- Add your `zone_id` (found in the Overview tab of your desired domain on Cloudflare) - Add your `zone_id` (found in the Overview tab of your desired domain on Cloudflare)
- Add your `route`, which should be a domain or host, supporting a wildcard - Add your `route`, which should be a domain or host, supporting a wildcard
```toml
name = "dashy-worker"
type = "javascript"
workers_dev = true
route = "example.com/*"
zone_id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
account_id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
kv_namespaces = [
{ binding = "DASHY_CLOUD_BACKUP", id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }
]
```
#### Complete `index.js` #### Complete `index.js`
- Write code to handle your requests, and interact with any other data sources in this file - Write code to handle your requests, and interact with any other data sources in this file
- Generally, this is done within an event listener for 'fetch', and returns a promise - Generally, this is done within an event listener for 'fetch', and returns a promise
@ -66,7 +80,7 @@ async function handleRequest(request) {
} }
``` ```
- For the code used for Dashy's cloud service, see [here](https://notes.aliciasykes.com/p/j2F1deljv1) - For the code used for Dashy's cloud service, see [here](https://gist.github.com/Lissy93/d19b43d50f30e02fa25f349cf5cb5ed8#file-index-js)
#### Commands #### Commands

View File

@ -50,6 +50,8 @@ All fields are optional, unless otherwise stated.
**`backgroundImg`** | `string` | _Optional_ | Path to an optional full-screen app background image. This can be either remote (http) or local (/). Note that this will slow down initial load **`backgroundImg`** | `string` | _Optional_ | Path to an optional full-screen app background image. This can be either remote (http) or local (/). Note that this will slow down initial load
**`enableFontAwesome`** | `boolean` | _Optional_ | Where `true` is enabled, if left blank font-awesome will be enabled only if required by 1 or more icons **`enableFontAwesome`** | `boolean` | _Optional_ | Where `true` is enabled, if left blank font-awesome will be enabled only if required by 1 or more icons
**`fontAwesomeKey`** | `string` | _Optional_ | If you have a font-awesome key, then you can use it here and make use of premium icons. It is a 10-digit alpha-numeric string from you're FA kit URL (e.g. `13014ae648`) **`fontAwesomeKey`** | `string` | _Optional_ | If you have a font-awesome key, then you can use it here and make use of premium icons. It is a 10-digit alpha-numeric string from you're FA kit URL (e.g. `13014ae648`)
**`layout`** | `string` | _Optional_ | App layout, either `horizontal`, `vertical`, `auto` or `sidebar`. Defaults to `auto`. This specifies the layout and direction of how sections are positioned on the home screen. This can also be modified from the UI.
**`iconSize`** | `string` | _Optional_ | The size of link items / icons. Can be either `small`, `medium,` or `large`. Defaults to `medium`. This can also be set directly from the UI.
**`theme`** | `string` | _Optional_ | The default theme for first load (you can change this later from the UI) **`theme`** | `string` | _Optional_ | The default theme for first load (you can change this later from the UI)
**`cssThemes`** | `string[]` | _Optional_ | An array of custom theme names which can be used in the theme switcher dropdown **`cssThemes`** | `string[]` | _Optional_ | An array of custom theme names which can be used in the theme switcher dropdown
**`externalStyleSheet`** | `string` or `string[]` | _Optional_ | Either a URL to an external stylesheet or an array or URLs, which can be applied as themes within the UI **`externalStyleSheet`** | `string` or `string[]` | _Optional_ | Either a URL to an external stylesheet or an array or URLs, which can be applied as themes within the UI
@ -104,9 +106,9 @@ All fields are optional, unless otherwise stated.
**`itemSize`** | `string` | _Optional_ | Specify the size for items within this group, either `small`, `medium` or `large`. Note that this will overide any settings specified through the UI **`itemSize`** | `string` | _Optional_ | Specify the size for items within this group, either `small`, `medium` or `large`. Note that this will overide any settings specified through the UI
**`rows`** | `number` | _Optional_ | Height of the section, specified as the number of rows it should span vertically, e.g. `2`. Defaults to `1`. Max is `5`. **`rows`** | `number` | _Optional_ | Height of the section, specified as the number of rows it should span vertically, e.g. `2`. Defaults to `1`. Max is `5`.
**`cols`** | `number` | _Optional_ | Width of the section, specified as the number of columns the section should span horizontally, e.g. `2`. Defaults to `1`. Max is `5`. **`cols`** | `number` | _Optional_ | Width of the section, specified as the number of columns the section should span horizontally, e.g. `2`. Defaults to `1`. Max is `5`.
**`layout`** | `string` | _Optional_ | Specify which CSS layout will be used to responsivley place items. Can be either `auto` (which uses flex layout), or `grid`. If `grid` is selected, then `itemCountX` and `itemCountY` may also be set. Defaults to `auto` **`sectionLayout`** | `string` | _Optional_ | Specify which CSS layout will be used to responsivley place items. Can be either `auto` (which uses flex layout), or `grid`. If `grid` is selected, then `itemCountX` and `itemCountY` may also be set. Defaults to `auto`
**`itemCountX`** | `number` | _Optional_ | The number of items to display per row / horizontally. If not set, it will be calculated automatically based on available space. Can only be set if `layout` is set to `grid`. Must be a whole number between `1` and `12` **`itemCountX`** | `number` | _Optional_ | The number of items to display per row / horizontally. If not set, it will be calculated automatically based on available space. Can only be set if `sectionLayout` is set to `grid`. Must be a whole number between `1` and `12`
**`itemCountY`** | `number` | _Optional_ | The number of items to display per column / vertically. If not set, it will be calculated automatically based on available space. If `itemCountX` is set, then `itemCountY` can be calculated automatically. Can only be set if `layout` is set to `grid`. Must be a whole number between `1` and `12` **`itemCountY`** | `number` | _Optional_ | The number of items to display per column / vertically. If not set, it will be calculated automatically based on available space. If `itemCountX` is set, then `itemCountY` can be calculated automatically. Can only be set if `sectionLayout` is set to `grid`. Must be a whole number between `1` and `12`
**[⬆️ Back to Top](#configuring)** **[⬆️ Back to Top](#configuring)**

View File

@ -128,7 +128,8 @@ Click one of the links below, to open an issue:
### Contributors ### Contributors
![Auto-generated contributors](https://raw.githubusercontent.com/Lissy93/dashy/03fbaf35ff4653d16a622cfce00a1347c13d0192/docs/assets/CONTRIBUTORS.svg) ![Auto-generated contributors](https://raw.githubusercontent.com/Lissy93/dashy/master/docs/assets/CONTRIBUTORS.svg)
### Star-Gazers Over Time ### Star-Gazers Over Time

View File

@ -9,6 +9,7 @@
- [Basic Commands](#basic-commands) - [Basic Commands](#basic-commands)
- [Healthchecks](#healthchecks) - [Healthchecks](#healthchecks)
- [Monitoring](#logs-and-performance) - [Monitoring](#logs-and-performance)
- [Auto Starting](#auto-starting-at-system-boot)
- [Updating](#updating) - [Updating](#updating)
- [Updating Docker Container](#updating-docker-container) - [Updating Docker Container](#updating-docker-container)
- [Automating Docker Updates](#automatic-docker-updates) - [Automating Docker Updates](#automatic-docker-updates)
@ -49,6 +50,16 @@ You can also build and deploy the Docker container from source.
- Edit the `./public/conf.yml` file and take a look at the `docker-compose.yml` - Edit the `./public/conf.yml` file and take a look at the `docker-compose.yml`
- Start the container: `docker compose up` - Start the container: `docker compose up`
### Other Container Engines
Docker isn't the only host application capable of running standard Linux containers - [Podman](http://podman.io) is another popular option. Unlike Docker, Podman does not rely on a daemon to be running on your host system. This means there is no single point of failure and it can also support rootless containers, which is perfect for Dashy as it does not require any sudo privileges. Podman was developed by RedHat, and it's source code is written in Go, and published on [GitHub](https://github.com/containers/podman).
Installation of the podman is really easy, as it's repository is available for most package managers (for example; Arch: `sudo pacman -S podman`, Debian/ Ubuntu: `sudo apt-get install podman`, Gentoo: `sudo emerge app-emulation/podman`, and MacOS: `brew install podman`). For more info, check out the [podman installation docs](https://podman.io/getting-started/installation). If you are using Windows, then take a look at Brent Baude's article on [Running Podman on WSL](https://www.redhat.com/sysadmin/podman-windows-wsl2). Since it's CLI is pretty much identical to that of Dockers, Podman's learning curve is very shallow.
To run Dashy with Podman, just replace `docker` with `podman` in the above instructions. E.g. `podman run -p 8080:80 lissy93/dashy`
It's worth noting that Podman isn't the only container running alternative, there's also [`rkt`](https://www.openshift.com/learn/topics/rkt), [`runc`](https://github.com/opencontainers/runc), [`containerd`](https://containerd.io/) and [`cri-o`](https://cri-o.io/). Dashy has not been tested with any of these engines, but it should work just fine.
### Deploy from Source ### Deploy from Source
If you do not want to use Docker, you can run Dashy directly on your host system. For this, you will need both [git](https://git-scm.com/downloads) and the latest or LTS version of [Node.js](https://nodejs.org/) installed. If you do not want to use Docker, you can run Dashy directly on your host system. For this, you will need both [git](https://git-scm.com/downloads) and the latest or LTS version of [Node.js](https://nodejs.org/) installed.
@ -187,11 +198,25 @@ To restart unhealthy containers automatically, check out [Autoheal](https://hub.
### Logs and Performance ### Logs and Performance
##### Container Logs
You can view logs for a given Docker container with `docker logs [container-id]`, add the `--follow` flag to stream the logs. For more info, see the [Logging Documentation](https://docs.docker.com/config/containers/logging/). There's also [Dozzle](https://dozzle.dev/), a useful tool, that provides a web interface where you can stream and query logs from all your running containers from a single web app. You can view logs for a given Docker container with `docker logs [container-id]`, add the `--follow` flag to stream the logs. For more info, see the [Logging Documentation](https://docs.docker.com/config/containers/logging/). There's also [Dozzle](https://dozzle.dev/), a useful tool, that provides a web interface where you can stream and query logs from all your running containers from a single web app.
##### Container Performance
You can check the resource usage for your running Docker containers with `docker stats` or `docker stats [container-id]`. For more info, see the [Stats Documentation](https://docs.docker.com/engine/reference/commandline/stats/). There's also [cAdvisor](https://github.com/google/cadvisor), a useful web app for viewing and analyzing resource usage and performance of all your running containers. You can check the resource usage for your running Docker containers with `docker stats` or `docker stats [container-id]`. For more info, see the [Stats Documentation](https://docs.docker.com/engine/reference/commandline/stats/). There's also [cAdvisor](https://github.com/google/cadvisor), a useful web app for viewing and analyzing resource usage and performance of all your running containers.
You can also view logs, resource usage and other info as well as manage your Docker workflow in third-party Docker management apps. For example [Portainer](https://github.com/portainer/portainer) an all-in-one management web UI for Docker and Kubernetes, or [LazyDocker](https://github.com/jesseduffield/lazydocker) a terminal UI for Docker container management and monitoring. ##### Management Apps
You can also view logs, resource usage and other info as well as manage your entire Docker workflow in third-party Docker management apps. For example [Portainer](https://github.com/portainer/portainer) an all-in-one open source management web UI for Docker and Kubernetes, or [LazyDocker](https://github.com/jesseduffield/lazydocker) a terminal UI for Docker container management and monitoring.
##### Advanced Logging and Monitoring
Docker supports using [Prometheus](https://prometheus.io/) to collect logs, which can then be visualized using a platform like [Grafana](https://grafana.com/). For more info, see [this guide](https://docs.docker.com/config/daemon/prometheus/). If you need to route your logs to a remote syslog, then consider using [logspout](https://github.com/gliderlabs/logspout). For enterprise-grade instances, there are managed services, that make monitoring container logs and metrics very easy, such as [Sematext](https://sematext.com/blog/docker-container-monitoring-with-sematext/) with [Logagent](https://github.com/sematext/logagent-js).
### Auto-Starting at System Boot
You can use Docker's [restart policies](https://docs.docker.com/engine/reference/run/#restart-policies---restart) to instruct the container to start after a system reboot, or restart after a crash. Just add the `--restart=always` flag to your Docker compose script or Docker run command. For more information, see the docs on [Starting Containers Automatically](https://docs.docker.com/config/containers/start-containers-automatically/).
For Podman, you can use `systemd` to create a service that launches your container, [the docs](https://podman.io/blogs/2018/09/13/systemd.html) explains things further. A similar approach can be used with Docker, if you need to start containers after a reboot, but before any user interaction.
To restart the container after something within it has crashed, consider using [`docker-autoheal`](https://github.com/willfarrell/docker-autoheal) by @willfarrell, a service that monitors and restarts unhealthy containers. For more info, see the [Healthchecks](#healthchecks) section above.
**[⬆️ Back to Top](#deployment)** **[⬆️ Back to Top](#deployment)**

View File

@ -7,5 +7,6 @@
- [User Guide](/docs/user-guide.md) - [User Guide](/docs/user-guide.md)
- [Troubleshooting](/docs/troubleshooting.md) - [Troubleshooting](/docs/troubleshooting.md)
- [Backup & Restore](/docs/backup-restore.md) - [Backup & Restore](/docs/backup-restore.md)
- [Status Indicators](/docs/status-indicators.md)
- [Theming](/docs/theming.md) - [Theming](/docs/theming.md)
- [Authentication](/docs/authentication.md) - [Authentication](/docs/authentication.md)

View File

@ -108,10 +108,14 @@ You can target specific elements on the UI with these variables. All are optiona
- `--config-settings-background` - The text color for text within the settings container. Defaults to `--background-darker` - `--config-settings-background` - The text color for text within the settings container. Defaults to `--background-darker`
- `--scroll-bar-color` - Color of the scroll bar thumb. Defaults to `--primary` - `--scroll-bar-color` - Color of the scroll bar thumb. Defaults to `--primary`
- `--scroll-bar-background` Color of the scroll bar blank space. Defaults to `--background-darker` - `--scroll-bar-background` Color of the scroll bar blank space. Defaults to `--background-darker`
- `--highlight-background` Fill color for text highlighting. Defaults to `--primary`
- `--highlight-color` Text color for selected/ highlighted text. Defaults to `--background`
- `--toast-background` - Background color for the toast info popup. Defaults to `--primary` - `--toast-background` - Background color for the toast info popup. Defaults to `--primary`
- `--toast-color` - Text, icon and border color in the toast info popup. Defaults to `--background` - `--toast-color` - Text, icon and border color in the toast info popup. Defaults to `--background`
- `--welcome-popup-background` - Background for the info pop-up shown on first load. Defaults to `--background-darker` - `--welcome-popup-background` - Background for the info pop-up shown on first load. Defaults to `--background-darker`
- `--welcome-popup-text-color` - Text color for the welcome pop-up. Defaults to `--primary` - `--welcome-popup-text-color` - Text color for the welcome pop-up. Defaults to `--primary`
- `--side-bar-background` - Background color of the sidebar used in the workspace view. Defaults to `--background-darker`
- `--side-bar-color` - Color of icons and text within the sidebar. Defaults to `--primary`
#### Non-Color Variables #### Non-Color Variables
- `--outline-color` - Used to outline focused or selected elements - `--outline-color` - Used to outline focused or selected elements

View File

@ -68,6 +68,7 @@ export default {
<style lang="scss"> <style lang="scss">
@import '@/styles/global-styles.scss'; @import '@/styles/global-styles.scss';
@import '@/styles/color-palette.scss'; @import '@/styles/color-palette.scss';
@import '@/styles/dimensions.scss';
@import '@/styles/color-themes.scss'; @import '@/styles/color-themes.scss';
@import '@/styles/typography.scss'; @import '@/styles/typography.scss';

View File

@ -70,7 +70,7 @@ export default {
return this.displayData.itemSize || this.itemSize; return this.displayData.itemSize || this.itemSize;
}, },
isGridLayout() { isGridLayout() {
return this.displayData.layout === 'grid' return this.displayData.sectionLayout === 'grid'
|| !!(this.displayData.itemCountX || this.displayData.itemCountY); || !!(this.displayData.itemCountX || this.displayData.itemCountY);
}, },
gridStyle() { gridStyle() {

View File

@ -1,5 +1,5 @@
<template> <template>
<div> <div class="item-icon">
<i v-if="iconType === 'font-awesome'" :class="`${icon} ${size}`" ></i> <i v-if="iconType === 'font-awesome'" :class="`${icon} ${size}`" ></i>
<img v-else-if="icon" :src="iconPath" @error="imageNotFound" <img v-else-if="icon" :src="iconPath" @error="imageNotFound"
:class="`tile-icon ${size} ${broken ? 'broken' : ''}`" :class="`tile-icon ${size} ${broken ? 'broken' : ''}`"
@ -98,9 +98,16 @@ export default {
<style lang="scss"> <style lang="scss">
.tile-icon { .tile-icon {
width: 60px; width: 2rem;
// filter: var(--item-icon-transform); // filter: var(--item-icon-transform);
border-radius: var(--curve-factor);
&.broken { display: none; } &.broken { display: none; }
&.small {
width: 1.5rem;
}
&.large {
width: 3rem;
}
} }
i.fas, i.fab, i.far, i.fal, i.fad { i.fas, i.fab, i.far, i.fal, i.fad {
font-size: 2rem; font-size: 2rem;

View File

@ -29,6 +29,9 @@ export default {
<style scoped lang="scss"> <style scoped lang="scss">
footer { footer {
position: fixed;
width: 100%;
bottom: 0;
padding: 0.25rem; padding: 0.25rem;
text-align: center; text-align: center;
color: var(--medium-grey); color: var(--medium-grey);

View File

@ -54,6 +54,7 @@ export default {
} }
}, },
methods: { methods: {
/* Sets the theme, by updating data-theme attribute on the html tag */
setLocalTheme(newTheme) { setLocalTheme(newTheme) {
const htmlTag = document.getElementsByTagName('html')[0]; const htmlTag = document.getElementsByTagName('html')[0];
if (htmlTag.hasAttribute('data-theme')) htmlTag.removeAttribute('data-theme'); if (htmlTag.hasAttribute('data-theme')) htmlTag.removeAttribute('data-theme');

View File

@ -0,0 +1,90 @@
<template>
<nav class="side-bar">
<div v-for="(section, index) in sections" :key="index">
<div @click="openSection(index)" class="side-bar-item-container">
<SideBarItem
class="item"
:icon="section.icon"
:title="section.name"
/>
</div>
<transition name="slide">
<SideBarSection
v-if="isOpen[index]"
:items="section.items"
@launch-app="launchApp"
/>
</transition>
</div>
</nav>
</template>
<script>
import SideBarItem from '@/components/Workspace/SideBarItem.vue';
import SideBarSection from '@/components/Workspace/SideBarSection.vue';
export default {
name: 'SideBar',
inject: ['config'],
props: {
sections: Array,
},
data() {
return {
isOpen: new Array(this.sections.length).fill(false),
};
},
components: {
SideBarItem,
SideBarSection,
},
methods: {
/* Toggles the section clicked, and closes all other sections */
openSection(index) {
this.isOpen = this.isOpen.map((val, ind) => (ind !== index ? false : !val));
},
launchApp(url) {
this.$emit('launch-app', url);
},
},
};
</script>
<style lang="scss" scoped>
@import '@/styles/media-queries.scss';
@import '@/styles/style-helpers.scss';
nav.side-bar {
position: fixed;
display: flex;
flex-direction: column;
background: var(--side-bar-background);
color: var(--side-bar-color);
height: 100%;
width: var(--side-bar-width);
text-align: center;
overflow: auto;
@extend .scroll-bar;
.side-bar-item-container {
z-index: 5;
}
.item:not(:last-child) {
border-bottom: 1px dashed var(--side-bar-color);
z-index: 5;
}
}
.slide-leave-active,
.slide-enter-active {
transition: all 0.1s ease-in-out;
}
.slide-enter {
transform: translate(0, -80%);
}
.slide-leave-to {
transform: translate(0, -80%);
}
</style>

View File

@ -0,0 +1,64 @@
<template>
<div @click="itemClicked()"
:class="`side-bar-item ${icon ? 'w-icon' : 'text-only'}`" v-tooltip="tooltip">
<Icon v-if="icon" :icon="icon" size="small" :url="url" />
<p class="small-title" v-else>{{ title }}</p>
</div>
</template>
<script>
import Icon from '@/components/LinkItems/ItemIcon.vue';
export default {
name: 'SideBarItem',
inject: ['config'],
props: {
icon: String,
title: String,
url: String,
click: Function,
},
components: {
Icon,
},
methods: {
itemClicked() {
if (this.url) this.$emit('launch-app', this.url);
},
},
data() {
return {
tooltip: {
disabled: !this.title,
content: this.title,
trigger: 'hover focus',
hideOnTargetClick: true,
html: false,
placement: 'right-start',
delay: { show: 800, hide: 1000 },
},
};
},
};
</script>
<style lang="scss" scoped>
@import '@/styles/media-queries.scss';
@import '@/styles/style-helpers.scss';
div.side-bar-item {
color: var(--side-bar-color);
background: var(--side-bar-background);
text-align: center;
&.text-only {
background: none;
border: none;
box-shadow: none;
p.small-title {
margin: 0.1rem auto;
font-size: 0.6rem;
}
}
}
</style>

View File

@ -0,0 +1,54 @@
<template>
<div class="sub-side-bar">
<div v-for="(item, index) in items" :key="index">
<SideBarItem
class="item"
:icon="item.icon"
:title="item.title"
:url="item.url"
@launch-app="launchApp"
/>
</div>
</div>
</template>
<script>
import SideBarItem from '@/components/Workspace/SideBarItem.vue';
export default {
name: 'SideBarSection',
inject: ['config'],
props: {
items: Array,
},
components: {
SideBarItem,
},
methods: {
launchApp(url) {
this.$emit('launch-app', url);
},
},
};
</script>
<style lang="scss" scoped>
@import '@/styles/media-queries.scss';
@import '@/styles/style-helpers.scss';
div.sub-side-bar {
display: flex;
flex-direction: column;
background: var(--side-bar-background-lighter);
border-radius: var(--curve-factor);
margin: 0.2rem;
color: var(--side-bar-color);
text-align: center;
z-index: 3;
.item:not(:last-child) {
border-bottom: 1px dashed var(--side-bar-color);
}
}
</style>

View File

@ -0,0 +1,30 @@
<template>
<div class="web-content">
<iframe :src="url" />
</div>
</template>
<script>
export default {
name: 'WebContent',
props: {
url: String,
},
};
</script>
<style lang="scss" scoped>
@import '@/styles/media-queries.scss';
@import '@/styles/style-helpers.scss';
iframe {
position: absolute;
left: var(--side-bar-width);
height: calc(100% - var(--header-height));
width: calc(100% - var(--side-bar-width));
border: none;
background: white;
}
</style>

View File

@ -3,8 +3,10 @@ import Router from 'vue-router';
import Home from '@/views/Home.vue'; import Home from '@/views/Home.vue';
import Login from '@/views/Login.vue'; import Login from '@/views/Login.vue';
import Workspace from '@/views/Workspace.vue';
import { isLoggedIn } from '@/utils/Auth'; import { isLoggedIn } from '@/utils/Auth';
import { appConfig, pageInfo, sections } from '@/utils/ConfigAccumalator'; import { appConfig, pageInfo, sections } from '@/utils/ConfigAccumalator';
import { metaTagData } from '@/utils/defaults';
Vue.use(Router); Vue.use(Router);
@ -26,12 +28,17 @@ const router = new Router({
}, },
meta: { meta: {
title: pageInfo.title || 'Home Page', title: pageInfo.title || 'Home Page',
metaTags: [ metaTags: metaTagData,
{ },
name: 'description', },
content: 'A simple static homepage for you\'re server', {
}, path: '/workspace',
], name: 'workspace',
component: Workspace,
props: { appConfig, pageInfo, sections },
meta: {
title: pageInfo.title || 'Dashy Workspace',
metaTags: metaTagData,
}, },
}, },
{ {

View File

@ -31,22 +31,6 @@
--transparent-white-50: #ffffff80; --transparent-white-50: #ffffff80;
--transparent-white-30: #ffffff4d; --transparent-white-30: #ffffff4d;
/* Other Variables */
--outline-color: none;
--curve-factor: 5px; // Border radius for most components
--curve-factor-navbar: 16px; // Border radius for header
--dimming-factor: 0.7; // Opacity for semi-transparent components
/* Settings for specific components */
--scroll-bar-width: 8px;
--item-group-padding: 5px; // Determines width of item-group outline
--item-shadow: 1px 1px 2px #130f23;
--item-hover-shadow: 1px 2px 4px #373737;
--item-icon-transform: drop-shadow(2px 4px 6px var(--transparent-50)) saturate(0.65);
--item-icon-transform-hover: drop-shadow(4px 8px 3px var(--transparent-50)) saturate(2);
--item-group-shadow: var(--item-shadow);
--settings-container-shadow: none;
/* Color variables for specific components /* Color variables for specific components
* all variables are optional, since they inherit initial values from above* * all variables are optional, since they inherit initial values from above*
* Using specific variables makes overriding for custom themes really easy */ * Using specific variables makes overriding for custom themes really easy */
@ -77,10 +61,17 @@
--toast-color: var(--background); --toast-color: var(--background);
--scroll-bar-color: var(--primary); --scroll-bar-color: var(--primary);
--scroll-bar-background: var(--background-darker); --scroll-bar-background: var(--background-darker);
--highlight-color: var(--background);
--highlight-background: var(--primary);
--loading-screen-color: var(--primary); --loading-screen-color: var(--primary);
--loading-screen-background: var(--background); --loading-screen-background: var(--background);
--login-form-color: var(--primary); --login-form-color: var(--primary);
--login-form-background: var(--background); --login-form-background: var(--background);
--login-form-background-secondary: var(--background-darker); --login-form-background-secondary: var(--background-darker);
--about-page-color: var(--white);
--about-page-background: #0b1021;
--about-page-accent: var(--primary);
--side-bar-background: var(--background-darker);
--side-bar-background-lighter: var(--background);
--side-bar-color: var(--primary);
} }

View File

@ -0,0 +1,26 @@
:root {
/* General Variables */
--outline-color: none;
--curve-factor: 5px; // Border radius for most components
--curve-factor-navbar: 16px; // Border radius for header
--dimming-factor: 0.7; // Opacity for semi-transparent components
/* Basic Page Components */
--scroll-bar-width: 8px;
--header-height: 6.3rem;
/* Section & Item dimensions */
--item-group-padding: 5px; // Determines width of item-group outline
--item-shadow: 1px 1px 2px #130f23;
--item-hover-shadow: 1px 2px 4px #373737;
--item-icon-transform: drop-shadow(2px 4px 6px var(--transparent-50)) saturate(0.65);
--item-icon-transform-hover: drop-shadow(4px 8px 3px var(--transparent-50)) saturate(2);
--item-group-shadow: var(--item-shadow);
/* Settings and config menu */
--settings-container-shadow: none;
/* Workspace View */
--side-bar-width: 3.5rem; // The width of the sidebar
}

View File

@ -7,6 +7,7 @@ html {
transition: all 1s; transition: all 1s;
margin-top: -3px; margin-top: -3px;
@extend .scroll-bar; @extend .scroll-bar;
@extend .highlight;
box-sizing: border-box; box-sizing: border-box;
input[type=button], button, a { input[type=button], button, a {
cursor: pointer; cursor: pointer;

View File

@ -15,6 +15,19 @@
} }
} }
/* Custom highlight color */
.highlight {
::selection {
background-color: var(--highlight-background);
color: var(--highlight-color);
}
::-moz-selection, ::-o-selection, ::-ms-selection, ::-webkit-selection {
background-color: var(--highlight-background);
color: var(--highlight-color);
}
}
/* Single-style helpers */ /* Single-style helpers */
.bold { font-weight: bold; } .bold { font-weight: bold; }
.light { font-weight: lighter; } .light { font-weight: lighter; }

View File

@ -8,13 +8,17 @@ import Defaults, { localStorageKeys } from '@/utils/defaults';
import conf from '../../public/conf.yml'; import conf from '../../public/conf.yml';
export const appConfig = (() => { export const appConfig = (() => {
let usersAppConfig = Defaults.appConfig;
if (localStorage[localStorageKeys.APP_CONFIG]) { if (localStorage[localStorageKeys.APP_CONFIG]) {
return JSON.parse(localStorage[localStorageKeys.APP_CONFIG]); usersAppConfig = JSON.parse(localStorage[localStorageKeys.APP_CONFIG]);
} else if (conf.appConfig) { } else if (conf.appConfig) {
return conf.appConfig; usersAppConfig = conf.appConfig;
} else {
return Defaults.appConfig;
} }
usersAppConfig.layout = localStorage[localStorageKeys.LAYOUT_ORIENTATION]
|| conf.appConfig.layout || Defaults.layout;
usersAppConfig.iconSize = localStorage[localStorageKeys.ICON_SIZE]
|| conf.appConfig.iconSize || Defaults.iconSize;
return usersAppConfig;
})(); })();
export const pageInfo = (() => { export const pageInfo = (() => {

View File

@ -69,6 +69,25 @@
"pattern": "^[a-z0-9]{10}$", "pattern": "^[a-z0-9]{10}$",
"description": "API key for font-awesome" "description": "API key for font-awesome"
}, },
"layout": {
"enum": [
"horizontal",
"vertical",
"auto",
"sidebar"
],
"default": "auto",
"description": "Specifies sections layout orientation on the home screen"
},
"iconSize": {
"enum": [
"small",
"medium",
"large"
],
"default": "medium",
"description": "The size of each link item / icon"
},
"cssThemes": { "cssThemes": {
"type": "array", "type": "array",
"description": "Theme names to be added to the dropdown", "description": "Theme names to be added to the dropdown",
@ -195,7 +214,7 @@
"default": 1, "default": 1,
"description": "The amount of space that the section spans horizontally" "description": "The amount of space that the section spans horizontally"
}, },
"layout": { "sectionLayout": {
"enum": [ "enum": [
"grid", "grid",
"auto" "auto"

View File

@ -77,4 +77,7 @@ module.exports = {
}, },
backupEndpoint: 'https://dashy-sync-service.as93.net', backupEndpoint: 'https://dashy-sync-service.as93.net',
splashScreenTime: 1900, splashScreenTime: 1900,
metaTagData: [
{ name: 'description', content: 'A simple static homepage for you\'re server' },
],
}; };

View File

@ -1,5 +1,129 @@
<template> <template>
<div class="about"> <article class="about">
<h1>This is an about page</h1> <h1>About Dashy</h1>
</div> <section>
<h2>Project Overview</h2>
<p>
Dashy is a self-hosted startpage for keeping your running applications and
services organised. The aim of Dashy is to provide an easy-to-deploy,
highly customizable and functianl dashboard, in order to improvde productivity
and enable easy organisation of running services or web links.
</p>
</section>
<section>
<h2>Source</h2>
<p>
Dashy's source is available through <a href="https://github.com/lissy93/dashy">GitHub</a>,
and the Docker image is on <a href="https://hub.docker.com/r/lissy93/dashy">DockerHub</a>.
</p>
</section>
<section>
<h2>Reporting a Bug</h2>
<p>
If you've come across an bug in Dashy, please <a href="https://git.io/JnqPR">open an issue</a>,
so that it can be fixed.
</p>
</section>
<section>
<h2>Getting Support</h2>
<p>
If you need any help deploying, using, or developing Dashy, feel free to
<a href="https://git.io/JnqiA">open a support ticket</a> or start a
<a href="https://github.com/Lissy93/dashy/discussions">discussion</a> on GitHub.
</p>
</section>
<section>
<h2>About the Author</h2>
<a href="https://aliciasykes.com">
<img class="aht-pic" src="https://i.ibb.co/FnxqTfx/aht-bot-round.png" alt="Alicia Sykes">
</a>
Dashy is developed an maintained by <a href="https://aliciasykes.com">Alicia Sykes</a>
(<a href="https://github.com/lissy93">@Lissy93</a>), with support from the community.
<ul>
<li><b>PGP Key</b> - <a href="https://keybase.io/aliciasykes/pgp_keys.asc?fingerprint=0688f8d34587d954e9e51fb8fedb68f55c0283a7"><code>0688 F8D3 4587 D954 E9E5 1FB8 FEDB 68F5 5C02 83A7</code></a></li>
<li><b>BTC Address</b> - <code>3853bSxupMjvxEYfwGDGAaLZhTKxB2vEVC</code></li>
<li><b>Mail</b> - <code>alicia at omg dot lol</code></li>
<li><b>Blog</b> - <a href="https://notes.aliciasykes.com">notes.aliciasykes.com</a></li>
</ul>
</section>
<section class="license">
<h2>License</h2>
<code>
Copyright © 2021 Alicia Sykes (https://aliciasykes.com)
</code>
<br><br>
<code>
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the Software), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the following conditions:
</code>
<br><br>
<code>
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
</code>
<br><br>
<code>
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWAREOR THE USE
OR OTHER DEALINGS IN THE SOFTWARE.
</code>
</section>
</article>
</template> </template>
<script>
document.getElementsByTagName('html')[0].setAttribute('data-theme', 'callisto');
</script>
<style scoped lang="scss">
article.about {
background: var(--about-page-background);
color: var(--about-page-color);
width: 80%;
max-width: 1000px;
margin: 1rem auto 2.5rem auto;
padding: 1rem;
border: 1px solid var(--about-page-accent);
border-radius: var(--curve-factor);
h1, h2, h3, p, span, div, section {
color: var(--about-page-color);
}
h1 {
font-size: 2rem;
text-align: center;
}
a, code {
color: var(--about-page-accent);
}
section {
padding-bottom: 1rem;
&:not(:last-child) {
border-bottom: 1px dashed var(--about-page-accent);
}
}
p {
line-height: 1.5rem;
font-size: 1rem;
}
img.aht-pic {
width: 100px;
float: right;
}
section.license code {
color: var(--about-page-color);
}
}
</style>

View File

@ -64,14 +64,14 @@ export default {
}), }),
computed: { computed: {
layoutOrientation: { layoutOrientation: {
get: () => localStorage[localStorageKeys.LAYOUT_ORIENTATION] || Defaults.layout, get() { return this.appConfig.layout || Defaults.layout; },
set: function setLayout(layout) { set: function setLayout(layout) {
localStorage.setItem(localStorageKeys.LAYOUT_ORIENTATION, layout); localStorage.setItem(localStorageKeys.LAYOUT_ORIENTATION, layout);
this.layout = layout; this.layout = layout;
}, },
}, },
iconSize: { iconSize: {
get: () => localStorage[localStorageKeys.ICON_SIZE] || Defaults.iconSize, get() { return this.appConfig.iconSize || Defaults.iconSize; },
set: function setIconSize(iconSize) { set: function setIconSize(iconSize) {
localStorage.setItem(localStorageKeys.ICON_SIZE, iconSize); localStorage.setItem(localStorageKeys.ICON_SIZE, iconSize);
this.itemSizeBound = iconSize; this.itemSizeBound = iconSize;

54
src/views/Workspace.vue Normal file
View File

@ -0,0 +1,54 @@
<template>
<div class="work-space">
<SideBar :sections="sections" @launch-app="launchApp" />
<WebContent :url="url" />
</div>
</template>
<script>
import SideBar from '@/components/Workspace/SideBar';
import WebContent from '@/components/Workspace/WebContent';
import Defaults, { localStorageKeys } from '@/utils/defaults';
export default {
name: 'Workspace',
props: {
sections: Array,
appConfig: Object,
},
data: () => ({
url: '',
}),
components: {
SideBar,
WebContent,
},
methods: {
launchApp(url) {
this.url = url;
},
setTheme() {
const theme = localStorage[localStorageKeys.THEME] || this.confTheme || Defaults.theme;
const htmlTag = document.getElementsByTagName('html')[0];
if (htmlTag.hasAttribute('data-theme')) htmlTag.removeAttribute('data-theme');
htmlTag.setAttribute('data-theme', theme);
},
initiateFontAwesome() {
const fontAwesomeScript = document.createElement('script');
const faKey = this.appConfig.fontAwesomeKey || Defaults.fontAwesomeKey;
fontAwesomeScript.setAttribute('src', `https://kit.fontawesome.com/${faKey}.js`);
document.head.appendChild(fontAwesomeScript);
},
},
mounted() {
this.setTheme();
this.initiateFontAwesome();
},
};
</script>
<style scoped lang="scss">
</style>