Files
cloudron-box/dashboard/src/components/AppsView.vue

171 lines
3.4 KiB
Vue
Raw Normal View History

2024-12-29 00:36:48 +01:00
<template>
<div class="content">
2024-12-29 19:19:03 +01:00
<h1 class="section-header">
{{ $t('apps.title') }}
<TextInput v-model="filter" placeholder="Filter ..." />
</h1>
2024-12-29 00:36:48 +01:00
2024-12-29 19:19:03 +01:00
<TransitionGroup name="grid-animation" tag="div" class="grid">
<a v-for="app in filteredApps" :key="app.id" class="item" :href="'https://' + app.fqdn" target="_blank" v-tooltip="app.fqdn">
2024-12-29 00:36:48 +01:00
<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>
2024-12-29 19:19:03 +01:00
</TransitionGroup>
<!-- TODO check user permissions -->
<div class="empty-placeholder" v-if="filteredApps.length === 0">
<!-- for admins -->
<div v-if="false">
<h4><i class="fa fa-cloud-download fa-fw"></i> {{ $t('apps.noApps.title') }}</h4>
<h5 v-html="$t('apps.noApps.description', { appStoreLink: '#/appstore' })"></h5>
</div>
<!-- for non-admins -->
<div v-if="true">
<h4>{{ $t('apps.noAccess.title') }}</h4>
<h5>{{ $t('apps.noAccess.description') }}</h5>
</div>
</div>
2024-12-29 00:36:48 +01:00
</div>
</template>
<script>
2024-12-29 19:19:03 +01:00
import { Button, Icon, TextInput } from 'pankow';
2024-12-29 00:36:48 +01:00
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,
2024-12-29 19:19:03 +01:00
TextInput,
2024-12-29 00:36:48 +01:00
},
data() {
return {
API_ORIGIN,
ready: false,
2024-12-29 19:19:03 +01:00
filter: '',
2024-12-29 00:36:48 +01:00
apps: [],
};
},
2024-12-29 19:19:03 +01:00
computed: {
filteredApps() {
return this.apps.filter(a => {
return a.fqdn.indexOf(this.filter) !== -1;
});
}
},
2024-12-29 00:36:48 +01:00
methods: {
},
async mounted() {
this.apps = await appsModel.list();
console.log(this.apps)
this.ready = true;
}
};
</script>
<style scoped>
2024-12-29 19:19:03 +01:00
.grid-animation-enter-active,
.grid-animation-leave-active {
transition: all 0.2s ease;
}
.grid-animation-enter-from,
.grid-animation-leave-to {
opacity: 0;
transform: translateX(30px);
}
2024-12-29 00:36:48 +01:00
.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;
}
.empty-placeholder {
margin-top: 20px;
}
2024-12-29 00:36:48 +01:00
</style>