Add appstore category dropdown

This commit is contained in:
Johannes Zellner
2025-05-21 16:36:09 +02:00
parent 048afdc232
commit 08294f5f39

View File

@@ -4,8 +4,9 @@ import { useI18n } from 'vue-i18n';
const i18n = useI18n();
const t = i18n.t;
import { ref, computed, useTemplateRef, onMounted, inject } from 'vue';
import { Button, TextInput, Spinner, InputDialog } from 'pankow';
import moment from 'moment';
import { ref, computed, useTemplateRef, onMounted, inject, watch } from 'vue';
import { Button, TextInput, Spinner, InputDialog, SingleSelect } from 'pankow';
import AppsModel from '../models/AppsModel.js';
import AppstoreModel from '../models/AppstoreModel.js';
import AppInstallDialog from '../components/AppInstallDialog.vue';
@@ -20,7 +21,37 @@ const ready = ref(false);
const proxyApp = ref();
const apps = ref([]);
const search = ref('');
// clear category on search
watch(search, (newValue) => {
if (newValue) category.value ='';
});
function filterForNewApps(apps) {
var minApps = apps.length < 12 ? apps.length : 12; // prevent endless loop
var tmp = [];
var i = 0;
do {
var offset = moment().subtract(i++, 'days');
tmp = apps.filter(function (app) { return moment(app.publishedAt).isAfter(offset); });
} while(tmp.length < minApps);
return tmp;
}
const filteredApps = computed(() => {
if (category.value) {
if (category.value === 'new') {
return filterForNewApps(apps.value);
} else {
return apps.value.filter(a => {
if (a.manifest.tags.join().toLowerCase().indexOf(category.value) !== -1) return true;
return false;
});
}
}
if (!search.value) return apps.value;
const s = search.value.toLowerCase();
@@ -45,6 +76,36 @@ const inputDialog = useTemplateRef('inputDialog');
const features = inject('features');
const installedApps = ref([]);
const category = ref('');
const categories = [
{ id: '', label: t('appstore.category.all') },
{ id: 'new', label: t('appstore.category.newApps') },
{ id: 'analytics', label: 'Analytics'},
{ id: 'automation', label: 'Automation'},
{ id: 'blog', label: 'Blog'},
{ id: 'chat', label: 'Chat'},
{ id: 'crm', label: 'CRM'},
{ id: 'document', label: 'Documents'},
{ id: 'email', label: 'Email'},
{ id: 'federated', label: 'Federated'},
{ id: 'finance', label: 'Finance'},
{ id: 'forum', label: 'Forum'},
{ id: 'fun', label: 'Fun'},
{ id: 'gallery', label: 'Gallery'},
{ id: 'game', label: 'Games'},
{ id: 'git', label: 'Code Hosting'},
{ id: 'hosting', label: 'Web Hosting'},
{ id: 'learning', label: 'Learning'},
{ id: 'media', label: 'Media'},
{ id: 'no-code', label: 'No-code'},
{ id: 'notes', label: 'Notes'},
{ id: 'project', label: 'Project Management'},
{ id: 'sync', label: 'File Sync'},
{ id: 'voip', label: 'VoIP'},
{ id: 'vpn', label: 'VPN'},
{ id: 'wiki', label: 'Wiki'},
];
function onAppInstallDialogClose() {
window.location.href = '#/appstore';
}
@@ -127,14 +188,15 @@ onMounted(async () => {
<AppInstallDialog ref="appInstallDialog" @close="onAppInstallDialogClose"/>
<ApplinkDialog ref="applinkDialog" @success="onApplinkDialogSuccess()"/>
<div class="filter-bar">
<div></div>
<Spinner v-if="!ready" class="pankow-spinner-large"/>
<TextInput v-show="ready" ref="searchInput" @keydown.esc="search = ''" v-model="search" :placeholder="$t('appstore.searchPlaceholder')" style="max-width: 100%; width: 500px;"/>
<div>
<Button secondary plain icon="fas fa-exchange-alt" @click="onInstall(proxyApp)">{{ $t('apps.addAppproxyAction') }}</Button>
<Button secondary plain icon="fas fa-link" @click="onApplinkDialogOpen()">{{ $t('apps.addApplinkAction') }}</Button>
</div>
<div class="filter-bar" :disabled="!ready" :style="{ 'pointer-events': ready ? null : 'none' }">
<SingleSelect @select="onCategory" v-model="category" :options="categories" option-key="id" option-label="label"/>
<TextInput ref="searchInput" @keydown.esc="search = ''" v-model="search" :placeholder="$t('appstore.searchPlaceholder')" style="flex-grow: 1;"/>
<Button secondary plain icon="fas fa-exchange-alt" @click="onInstall(proxyApp)">{{ $t('apps.addAppproxyAction') }}</Button>
<Button secondary plain icon="fas fa-link" @click="onApplinkDialogOpen()">{{ $t('apps.addApplinkAction') }}</Button>
</div>
<div v-if="!ready" style="width: 100%; text-align: center;">
<Spinner class="pankow-spinner-large"/>
</div>
<div v-if="!search && ready">
@@ -178,11 +240,10 @@ onMounted(async () => {
.filter-bar {
width: 100%;
display: flex;
gap: 6px;
justify-content: space-between;
margin-bottom: 30px;
padding-left: 20px;
padding-right: 20px;
}
}
.grid {
position: relative;