Implement update indicator and filter in apps view

This commit is contained in:
Johannes Zellner
2025-04-23 11:42:28 +02:00
parent 4a8e9fef99
commit 5cdbfc0af7
3 changed files with 50 additions and 13 deletions

View File

@@ -7,12 +7,14 @@ import AppsModel from '../models/AppsModel.js';
import ApplinksModel from '../models/ApplinksModel.js';
import DomainsModel from '../models/DomainsModel.js';
import ProfileModel from '../models/ProfileModel.js';
import UpdaterModel from '../models/UpdaterModel.js';
import ApplinkDialog from '../components/ApplinkDialog.vue';
const appsModel = AppsModel.create();
const domainsModel = DomainsModel.create();
const applinksModel = ApplinksModel.create();
const profileModel = ProfileModel.create();
const updaterModel = UpdaterModel.create();
const VIEW_TYPE = {
LIST: 'list',
@@ -111,10 +113,7 @@ const filteredApps = computed(() => {
if (stateFilter.value === 'running') return a.runState === RSTATES.RUNNING && a.health === HSTATES.HEALTHY && a.installationState === ISTATES.INSTALLED;
if (stateFilter.value === 'stopped') return a.runState === RSTATES.STOPPED;
// TODO implement this
// if (stateFilter.value === 'update_available') return !!(Client.getConfig().update[a.id] && Client.getConfig().update[a.id].manifest.version && Client.getConfig().update[a.id].manifest.version !== a.manifest.version);
if (stateFilter.value === 'update_available') return false;
if (stateFilter.value === 'update_available') return a.updateAvailable;
return a.runState === RSTATES.RUNNING && (a.health !== HSTATES.HEALTHY || a.installationState !== ISTATES.INSTALLED); // not responding
});
@@ -124,6 +123,8 @@ const installationStateLabel = AppsModel.installationStateLabel;
const installationActive = AppsModel.installationActive;
const appProgressMessage = AppsModel.appProgressMessage;
const updateInfo = ref({});
const applinkDialog = useTemplateRef('applinkDialog');
// hook for applinks otherwise it is a link
@@ -171,6 +172,11 @@ async function refreshApps() {
const [error, result] = await appsModel.list();
if (error) return console.error(error);
// amend update info
result.forEach((a) => {
a.updateAvailable = !!(updateInfo.value[a.id] && updateInfo.value[a.id].manifest.version && updateInfo.value[a.id].manifest.version !== a.manifest.version);
});
const [applinkError, applinks] = await applinksModel.list();
if (applinkError) return console.error(applinkError);
@@ -201,7 +207,12 @@ function toggleView() {
}
onMounted(async () => {
let [error, result] = await profileModel.get();
let [error, result] = await updaterModel.info();
if (error) return console.error(error);
updateInfo.value = result;
[error, result] = await profileModel.get();
if (error) return console.error(error);
profile.value = result;
@@ -246,13 +257,14 @@ onUnmounted(() => {
<div v-show="ready">
<TransitionGroup name="grid-animation" tag="div" class="grid" v-if="viewType === VIEW_TYPE.GRID">
<a v-for="app in filteredApps" :key="app.id" class="grid-item" @click="onOpenApp(app, $event)" :href="'https://' + app.fqdn" target="_blank">
<a class="config" v-show="isOperator(app)" @click="openAppEdit(app, $event)" :href="`#/app/${app.id}/info`"><Icon icon="fa-solid fa-cog" /></a>
<img :src="app.iconUrl" v-fallback-image="API_ORIGIN + '/img/appicon_fallback.png'"/>
<div class="grid-item-label">{{ app.label || app.subdomain || app.fqdn }}</div>
<div class="grid-item-task-label">{{ installationStateLabel(app) }}</div>
<div class="apps-progress" v-show="app.progress && isOperator(app)">
<div class="apps-progress-filled" :style="{ width: app.progress+'%' }"></div>
</div>
<a class="config" v-show="isOperator(app)" @click="openAppEdit(app, $event)" :href="`#/app/${app.id}/info`"><Icon icon="fa-solid fa-cog" /></a>
<a class="update-indicator" v-if="app.updateAvailable" :href="isOperator(app) ? `#/app/${app.id}/updates` : null" v-tooltip="$t('app.updateAvailableTooltip')"><i class="fa-fw fa-solid fa-arrow-up"/></a>
</a>
</TransitionGroup>
@@ -294,13 +306,15 @@ onUnmounted(() => {
</template>
<template #actions="app">
<div class="table-actions">
<Button small success tool v-if="app.updateAvailable" :href="`#/app/${app.id}/updates`" v-tooltip="$t('app.updateAvailableTooltip')" icon="fa-fw fa-solid fa-arrow-up"></Button>
<ButtonGroup>
<Button small secondary tool v-if="app.type !== APP_TYPES.LINK" :href="'/logs.html?appId=' + app.id" target="_blank" v-tooltip="$t('app.logsActionTooltip')" icon="fas fa-align-left"></Button>
<Button small secondary tool v-if="app.type !== APP_TYPES.PROXIED && app.type !== APP_TYPES.LINK" :href="'/terminal.html?id=' + app.id" target="_blank" v-tooltip="$t('app.terminalActionTooltip')" icon="fa fa-terminal"></Button>
<Button small secondary tool v-if="app.manifest.addons.localstorage" :href="'/filemanager.html#/home/app/' + app.id" target="_blank" v-tooltip="$t('app.filemanagerActionTooltip')" icon="fas fa-folder"></Button>
<Button small secondary tool v-if="app.type !== APP_TYPES.LINK" :href="'/logs.html?appId=' + app.id" target="_blank" v-tooltip="$t('app.logsActionTooltip')" icon="fa-fw fa-solid fa-align-left"></Button>
<Button small secondary tool v-if="app.type !== APP_TYPES.PROXIED && app.type !== APP_TYPES.LINK" :href="'/terminal.html?id=' + app.id" target="_blank" v-tooltip="$t('app.terminalActionTooltip')" icon="fa-fw fa fa-terminal"></Button>
<Button small secondary tool v-if="app.manifest.addons.localstorage" :href="'/filemanager.html#/home/app/' + app.id" target="_blank" v-tooltip="$t('app.filemanagerActionTooltip')" icon="fa-fw fa-solid fa-folder"></Button>
</ButtonGroup>
<Button small secondary tool @click="openAppEdit(app, $event)" :href="`#/app/${app.id}/info`" icon="fa-solid fa-cog"></Button>
<Button small secondary tool @click="openAppEdit(app, $event)" :href="`#/app/${app.id}/info`" icon="fa-fw fa-solid fa-cog"></Button>
</div>
</template>
</TableView>
@@ -457,6 +471,7 @@ onUnmounted(() => {
.config:focus,
.config:hover {
opacity: 1;
color: var(--pankow-color-primary-hover);
}
@media (hover: none) {
@@ -470,6 +485,27 @@ onUnmounted(() => {
opacity: 1;
}
.update-indicator {
position: absolute;
color: var(--pankow-color-success);
font-size: 18px;
cursor: pointer;
width: 50px;
height: 50px;
border-top-right-radius: 10px;
left: 0;
top: 0;
display: flex;
justify-content: center;
align-items: center;
}
.update-indicator:focus,
.update-indicator:hover {
opacity: 1;
color: var(--pankow-color-success-hover);
}
.empty-placeholder {
font-size: 18px;
margin: 10px;