Initial rewrite of the apps view
This commit is contained in:
129
dashboard/src/components/AppsView.vue
Normal file
129
dashboard/src/components/AppsView.vue
Normal file
@@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<h1 class="section-header">{{ $t('apps.title') }}</h1>
|
||||
|
||||
<div class="grid">
|
||||
<a v-for="app in apps" :key="app.id" class="item" :href="'https://' + app.fqdn" target="_blank" v-tooltip="app.fqdn">
|
||||
<img :src="API_ORIGIN + app.iconUrl"/>
|
||||
<div class="label">{{ app.label || app.subdomain || app.fqdn }}</div>
|
||||
<a class="config" :href="`#/app/${app.id}/info`"><Icon icon="fa-solid fa-cog" /></a>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { Button, Icon } from 'pankow';
|
||||
|
||||
import AppsModel from '../models/AppsModel.js';
|
||||
|
||||
const API_ORIGIN = import.meta.env.VITE_API_ORIGIN ? import.meta.env.VITE_API_ORIGIN : window.location.origin;
|
||||
const accessToken = localStorage.token;
|
||||
|
||||
const appsModel = AppsModel.create(API_ORIGIN, accessToken);
|
||||
|
||||
export default {
|
||||
name: 'AppsView',
|
||||
components: {
|
||||
Button,
|
||||
Icon,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
API_ORIGIN,
|
||||
ready: false,
|
||||
apps: [],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
async mounted() {
|
||||
this.apps = await appsModel.list();
|
||||
console.log(this.apps)
|
||||
this.ready = true;
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.grid {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
transition: 300ms;
|
||||
flex-wrap: wrap;
|
||||
justify-content: start;
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
.item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 190px;
|
||||
height: 180px;
|
||||
margin: 10px;
|
||||
overflow: hidden;
|
||||
border-radius: 10px;
|
||||
background-color: var(--card-background);
|
||||
}
|
||||
|
||||
.item:focus,
|
||||
.item:hover {
|
||||
box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.1);
|
||||
background-color: var(--pankow-color-background-hover);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.item img {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 18px;
|
||||
font-weight: 100;
|
||||
margin: 10px;
|
||||
color: var(--pankow-text-color);
|
||||
}
|
||||
|
||||
.item:focus .label,
|
||||
.item:hover .label {
|
||||
text-decoration: none;
|
||||
color: var(--accent-color);;
|
||||
}
|
||||
|
||||
.config {
|
||||
position: absolute;
|
||||
color: var(--pankow-text-color);
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-top-right-radius: 10px;
|
||||
right: 0;
|
||||
top: 0;
|
||||
opacity: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.config:focus,
|
||||
.config:hover {
|
||||
text-decoration: none;
|
||||
color: var(--accent-color);;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.item:focus .config,
|
||||
.item:hover .config {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<Notification />
|
||||
<AppsView v-if="view === VIEWS.APPS" />
|
||||
<SupportView v-if="view === VIEWS.SUPPORT" />
|
||||
<VolumesView v-if="view === VIEWS.VOLUMES" />
|
||||
</div>
|
||||
@@ -10,10 +11,12 @@
|
||||
|
||||
import { Notification } from 'pankow';
|
||||
|
||||
import AppsView from './AppsView.vue';
|
||||
import SupportView from './SupportView.vue';
|
||||
import VolumesView from './VolumesView.vue';
|
||||
|
||||
const VIEWS = {
|
||||
APPS: 'apps',
|
||||
SUPPORT: 'support',
|
||||
VOLUMES: 'volumes',
|
||||
};
|
||||
@@ -21,6 +24,7 @@ const VIEWS = {
|
||||
export default {
|
||||
name: 'Index',
|
||||
components: {
|
||||
AppsView,
|
||||
Notification,
|
||||
SupportView,
|
||||
VolumesView,
|
||||
@@ -44,7 +48,9 @@ export default {
|
||||
function onHashChange() {
|
||||
const view = location.hash.slice(2);
|
||||
|
||||
if (view === VIEWS.SUPPORT) {
|
||||
if (view === VIEWS.APPS) {
|
||||
that.view = VIEWS.APPS;
|
||||
} else if (view === VIEWS.SUPPORT) {
|
||||
that.view = VIEWS.SUPPORT;
|
||||
} else if (view === VIEWS.VOLUMES) {
|
||||
that.view = VIEWS.VOLUMES;
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
import { Button, InputDialog, TopBar, MainLayout } from 'pankow';
|
||||
|
||||
import LogsModel from '../models/LogsModel.js';
|
||||
import AppModel from '../models/AppModel.js';
|
||||
import AppsModel from '../models/AppsModel.js';
|
||||
|
||||
const API_ORIGIN = import.meta.env.VITE_API_ORIGIN ? import.meta.env.VITE_API_ORIGIN : window.location.origin;
|
||||
|
||||
@@ -46,7 +46,7 @@ export default {
|
||||
return {
|
||||
accessToken: localStorage.token,
|
||||
logsModel: null,
|
||||
appModel: null,
|
||||
appsModel: null,
|
||||
busyRestart: false,
|
||||
showRestart: false,
|
||||
showFilemanager: false,
|
||||
@@ -79,7 +79,7 @@ export default {
|
||||
|
||||
this.busyRestart = true;
|
||||
|
||||
await this.appModel.restart();
|
||||
await this.appsModel.restart();
|
||||
|
||||
this.busyRestart = false;
|
||||
}
|
||||
@@ -126,10 +126,10 @@ export default {
|
||||
this.logsModel = LogsModel.create(API_ORIGIN, this.accessToken, this.type, this.id);
|
||||
|
||||
if (this.type === 'app') {
|
||||
this.appModel = AppModel.create(API_ORIGIN, this.accessToken, this.id);
|
||||
this.appsModel = AppsModel.create(API_ORIGIN, this.accessToken, this.id);
|
||||
|
||||
try {
|
||||
const app = await this.appModel.get();
|
||||
const app = await this.appsModel.get();
|
||||
this.name = `${app.label || app.fqdn} (${app.manifest.title})`;
|
||||
this.showFilemanager = !!app.manifest.addons.localstorage;
|
||||
this.showTerminal = app.manifest.id !== 'io.cloudron.builtin.appproxy';
|
||||
|
||||
@@ -55,7 +55,7 @@ import { Terminal } from '@xterm/xterm';
|
||||
import { AttachAddon } from '@xterm/addon-attach';
|
||||
import { FitAddon } from '@xterm/addon-fit';
|
||||
|
||||
import { create } from '../models/AppModel.js';
|
||||
import AppsModel from '../models/AppsModel.js';
|
||||
import { createDirectoryModel } from '../models/DirectoryModel.js';
|
||||
|
||||
const API_ORIGIN = import.meta.env.VITE_API_ORIGIN ? import.meta.env.VITE_API_ORIGIN : window.location.origin;
|
||||
@@ -73,7 +73,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
accessToken: localStorage.token,
|
||||
appModel: null,
|
||||
appsModel: null,
|
||||
directoryModel: null,
|
||||
fatalError: false,
|
||||
busyRestart: false,
|
||||
@@ -197,7 +197,7 @@ export default {
|
||||
if (!confirmed) return;
|
||||
|
||||
this.busyRestart = true;
|
||||
await this.appModel.restart();
|
||||
await this.appsModel.restart();
|
||||
this.busyRestart = false;
|
||||
},
|
||||
async connect(retry = false) {
|
||||
@@ -269,11 +269,11 @@ export default {
|
||||
this.id = id;
|
||||
this.name = id;
|
||||
|
||||
this.appModel = create(API_ORIGIN, this.accessToken, this.id);
|
||||
this.appsModel = AppsModel.create(API_ORIGIN, this.accessToken, this.id);
|
||||
this.directoryModel = createDirectoryModel(API_ORIGIN, this.accessToken, `apps/${id}`);
|
||||
|
||||
try {
|
||||
const app = await this.appModel.get();
|
||||
const app = await this.appsModel.get();
|
||||
this.name = `${app.label || app.fqdn} (${app.manifest.title})`;
|
||||
this.addons = app.manifest.addons;
|
||||
this.manifestVersion = app.manifest.manifestVersion;
|
||||
|
||||
Reference in New Issue
Block a user