Use TableView also for ApiTokens and AppPasswords

This commit is contained in:
Johannes Zellner
2025-01-17 15:59:01 +01:00
parent 4534a729c7
commit f56eb0d791
4 changed files with 82 additions and 71 deletions
+38 -30
View File
@@ -43,35 +43,23 @@
</template>
<p v-html="$t('profile.apiTokens.description', { apiDocsLink: 'https://docs.cloudron.io/api.html' })"></p>
<table class="table table-hover" style="margin: 0;">
<thead>
<tr>
<th>{{ $t('profile.apiTokens.name') }}</th>
<th class="hide-mobile">{{ $t('profile.apiTokens.lastUsed') }}</th>
<th>{{ $t('profile.apiTokens.scope') }}</th>
<th class="text-right">{{ $t('main.actions') }}</th>
</tr>
</thead>
<tbody>
<tr v-show="apiTokens.length === 0">
<td colspan="3" class="text-center">{{ $t('profile.apiTokens.noTokensPlaceholder') }}</td>
</tr>
<tr v-for="token in apiTokens" :key="token.id">
<td class="elide-table-cell">{{ token.name || 'unnamed' }}</td>
<td class="elide-table-cell hide-mobile">
<span v-if="token.lastUsedTime">{{ prettyLongDate(token.lastUsedTime) }}</span>
<span v-else>{{ $t('profile.apiTokens.neverUsed') }}</span>
</td>
<td class="elide-table-cell">
<span v-if="token.scope['*'] === 'rw'">{{ $t('profile.apiTokens.readwrite') }}</span>
<span v-else>{{ $t('profile.apiTokens.readonly') }}</span>
</td>
<td class="text-right">
<Button small tool danger @click="onRevokeToken(token)" v-tooltip="$t('profile.apiTokens.revokeTokenTooltip')" icon="far fa-trash-alt" />
</td>
</tr>
</tbody>
</table>
<br/>
<TableView :columns="columns" :model="apiTokens">
<template #lastUsedTime="slotProps">
<span v-if="slotProps.lastUsedTime">{{ prettyLongDate(slotProps.lastUsedTime) }}</span>
<span v-else>{{ $t('profile.apiTokens.neverUsed') }}</span>
</template>
<template #scope="slotProps">
<span v-if="slotProps.scope['*'] === 'rw'">{{ $t('profile.apiTokens.readwrite') }}</span>
<span v-else>{{ $t('profile.apiTokens.readonly') }}</span>
</template>
<template #actions="slotProps">
<div class="table-actions">
<Button small outline tool danger @click="onRevokeToken(slotProps)" v-tooltip="$t('profile.apiTokens.revokeTokenTooltip')" icon="far fa-trash-alt" />
</div>
</template>
</TableView>
</Section>
</div>
</template>
@@ -84,8 +72,9 @@ import { useI18n } from 'vue-i18n';
const i18n = useI18n();
const t = i18n.t;
import moment from 'moment';
import { ref, onMounted, computed, useTemplateRef } from 'vue';
import { Button, Dialog, InputDialog, FormGroup, Radiobutton, TextInput } from 'pankow';
import { Button, Dialog, InputDialog, FormGroup, Radiobutton, TableView, TextInput } from 'pankow';
import { copyToClipboard, prettyLongDate } from 'pankow/utils';
import { TOKEN_TYPES } from '../constants.js';
import Section from './Section.vue';
@@ -99,6 +88,25 @@ const newDialog = useTemplateRef('newDialog');
const addedToken = ref('');
const tokenName = ref('');
const tokenScope = ref('r');
const columns = {
name: {
label: t('profile.apiTokens.name'),
sort: true
},
lastUsedTime: {
label: t('profile.apiTokens.lastUsed'),
sort(a, b) {
if (!a) return 1;
if (!b) return -1;
return moment(a).isBefore(b) ? 1 : -1;
}
},
scope: {
label: t('profile.apiTokens.scope'),
sort: true
},
actions: {}
};
const isValid = computed(() => {
if (!tokenName.value) return false;
+32 -25
View File
@@ -42,29 +42,16 @@
</template>
<p>{{ $t('profile.appPasswords.description') }}</p>
<table class="table table-hover">
<thead>
<tr>
<th>{{ $t('profile.appPasswords.name') }}</th>
<th>{{ $t('profile.appPasswords.app') }}</th>
<th>{{ $t('main.table.date') }}</th>
<th style="width: 30px" class="text-right">{{ $t('main.actions') }}</th>
</tr>
</thead>
<tbody>
<tr v-show="passwords.length === 0">
<td colspan="4" class="text-center">{{ $t('profile.appPasswords.noPasswordsPlaceholder') }}</td>
</tr>
<tr v-for="password in passwords" :key="password.id">
<td class="elide-table-cell">{{ password.name }}</td>
<td class="elide-table-cell">{{ password.label }}</td>
<td class="elide-table-cell">{{ prettyLongDate(password.creationTime) }}</td>
<td class="text-right">
<Button tool small danger @click="onRemove(password.id)" v-tooltip="$t('profile.appPasswords.deletePasswordTooltip')" icon="far fa-trash-alt" />
</td>
</tr>
</tbody>
</table>
<br/>
<TableView :columns="columns" :model="passwords" :placeholder="$t('profile.appPasswords.noPasswordsPlaceholder')">
<template #creationTime="slotProps">{{ prettyLongDate(slotProps.creationTime) }}</template>
<template #actions="slotProps">
<div class="table-actions">
<Button small outline tool danger @click="onRemove(slotProps.id)" v-tooltip="$t('profile.appPasswords.deletePasswordTooltip')" icon="far fa-trash-alt" />
</div>
</template>
</TableView>
</Section>
</div>
</template>
@@ -77,8 +64,9 @@ import { useI18n } from 'vue-i18n';
const i18n = useI18n();
const t = i18n.t;
import moment from 'moment';
import { ref, onMounted, useTemplateRef, computed } from 'vue';
import { Button, Dialog, Dropdown, FormGroup, TextInput, InputDialog } from 'pankow';
import { Button, Dialog, Dropdown, FormGroup, TextInput, TableView, InputDialog } from 'pankow';
import { prettyLongDate, copyToClipboard } from 'pankow/utils';
import Section from './Section.vue';
import AppPasswordsModel from '../models/AppPasswordsModel.js';
@@ -90,6 +78,25 @@ const appsModel = AppsModel.create(API_ORIGIN, localStorage.token);
const newDialog = useTemplateRef('newDialog');
const inputDialog = useTemplateRef('inputDialog');
const passwords = ref([]);
const columns = {
name: {
label: t('profile.appPasswords.name'),
sort: true
},
label: {
label: t('profile.appPasswords.app'),
sort: true
},
creationTime: {
label: t('main.table.date'),
sort(a, b) {
if (!a) return 1;
if (!b) return -1;
return moment(a).isBefore(b) ? 1 : -1;
}
},
actions: {}
};
// new dialog props
const addedPassword = ref('');
@@ -152,7 +159,7 @@ function onCopyToClipboard(password) {
async function onRemove(id) {
const yes = await inputDialog.value.confirm({
message: 'Really remove this token?', // TODO translate
message: 'Really remove this password?', // TODO translate
modal: true,
confirmStyle: 'danger',
confirmLabel: t('main.dialog.yes'),
+3 -16
View File
@@ -96,7 +96,7 @@
</div>
</template>
<template #actions="slotProps">
<div class="actions">
<div class="table-actions">
<ButtonGroup>
<Button tool secondary outline small icon="fa fa-sync-alt" v-if="slotProps.mountType === 'sshfs' || slotProps.mountType === 'cifs' || slotProps.mountType === 'nfs' || slotProps.mountType === 'ext4' || slotProps.mountType === 'xfs'" v-tooltip="$t('volumes.remountActionTooltip')" @click="remount(slotProps)"></Button>
<Button tool secondary outline small icon="fa fa-pencil-alt" v-if="slotProps.mountType === 'sshfs' || slotProps.mountType === 'cifs' || slotProps.mountType === 'nfs'" v-tooltip="$t('volumes.editActionTooltip')" @click="openVolumeDialog(slotProps)"></Button>
@@ -195,11 +195,11 @@ export default {
{ name: 'XFS', value: 'xfs' },
],
columns: {
status: { label: '' },
status: {},
name: { label: 'Name', sort: true },
mountType: { label: 'Type', sort: true },
target: { label: 'Target', sort: true },
actions: { label: '', sort: false }
actions: {}
},
volumes: [],
volumeDialogData: {
@@ -312,16 +312,3 @@ export default {
};
</script>
<style scoped>
.actions {
text-align: right;
visibility: hidden;
}
tr:hover .actions {
visibility: visible;
}
</style>