mirror of
https://github.com/InvoiceShelf/InvoiceShelf.git
synced 2026-04-19 11:14:06 +00:00
Add Admin Fonts settings page to install CJK font packages
Adds AdminFontView with package list, install buttons, status indicators and toast notifications backed by /api/v1/fonts/status and /api/v1/fonts/{package}/install. Wires the new admin.settings.fonts lazy route and a Languages-icon menu entry under Admin → Settings.
This commit is contained in:
@@ -11,6 +11,7 @@ const AdminMailConfigView = () => import('./views/settings/AdminMailConfigView.v
|
|||||||
const AdminPdfGenerationView = () => import('./views/settings/AdminPdfGenerationView.vue')
|
const AdminPdfGenerationView = () => import('./views/settings/AdminPdfGenerationView.vue')
|
||||||
const AdminBackupView = () => import('./views/settings/AdminBackupView.vue')
|
const AdminBackupView = () => import('./views/settings/AdminBackupView.vue')
|
||||||
const AdminFileDiskView = () => import('./views/settings/AdminFileDiskView.vue')
|
const AdminFileDiskView = () => import('./views/settings/AdminFileDiskView.vue')
|
||||||
|
const AdminFontView = () => import('./views/settings/AdminFontView.vue')
|
||||||
const AdminUpdateAppView = () => import('./views/settings/AdminUpdateAppView.vue')
|
const AdminUpdateAppView = () => import('./views/settings/AdminUpdateAppView.vue')
|
||||||
|
|
||||||
export const adminRoutes: RouteRecordRaw[] = [
|
export const adminRoutes: RouteRecordRaw[] = [
|
||||||
@@ -107,6 +108,14 @@ export const adminRoutes: RouteRecordRaw[] = [
|
|||||||
},
|
},
|
||||||
component: AdminFileDiskView,
|
component: AdminFileDiskView,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'fonts',
|
||||||
|
name: 'admin.settings.fonts',
|
||||||
|
meta: {
|
||||||
|
isSuperAdmin: true,
|
||||||
|
},
|
||||||
|
component: AdminFontView,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'update-app',
|
path: 'update-app',
|
||||||
name: 'admin.settings.update',
|
name: 'admin.settings.update',
|
||||||
|
|||||||
@@ -88,6 +88,11 @@ const menuItems = computed<SettingsMenuItem[]>(() => [
|
|||||||
link: '/admin/administration/settings/file-disk',
|
link: '/admin/administration/settings/file-disk',
|
||||||
icon: 'FolderIcon',
|
icon: 'FolderIcon',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t('settings.menu_title.fonts'),
|
||||||
|
link: '/admin/administration/settings/fonts',
|
||||||
|
icon: 'LanguageIcon',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t('settings.menu_title.update_app'),
|
title: t('settings.menu_title.update_app'),
|
||||||
link: '/admin/administration/settings/update-app',
|
link: '/admin/administration/settings/update-app',
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { useNotificationStore } from '@v2/stores/notification.store'
|
||||||
|
import { client } from '@v2/api/client'
|
||||||
|
import { API } from '@v2/api/endpoints'
|
||||||
|
|
||||||
|
interface FontPackage {
|
||||||
|
key: string
|
||||||
|
name: string
|
||||||
|
family: string
|
||||||
|
locales: string[]
|
||||||
|
size: string
|
||||||
|
installed: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
const notificationStore = useNotificationStore()
|
||||||
|
|
||||||
|
const packages = ref<FontPackage[]>([])
|
||||||
|
const isLoading = ref(false)
|
||||||
|
const installing = ref<Set<string>>(new Set())
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await loadStatus()
|
||||||
|
})
|
||||||
|
|
||||||
|
async function loadStatus(): Promise<void> {
|
||||||
|
isLoading.value = true
|
||||||
|
try {
|
||||||
|
const { data } = await client.get(API.FONTS_STATUS)
|
||||||
|
packages.value = data.packages
|
||||||
|
} catch {
|
||||||
|
// Silently fail
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function installFont(pkg: FontPackage): Promise<void> {
|
||||||
|
installing.value.add(pkg.key)
|
||||||
|
|
||||||
|
try {
|
||||||
|
await client.post(`${API.FONTS_INSTALL}/${pkg.key}/install`)
|
||||||
|
|
||||||
|
pkg.installed = true
|
||||||
|
|
||||||
|
notificationStore.showNotification({
|
||||||
|
type: 'success',
|
||||||
|
message: t('settings.fonts.download_complete', { name: pkg.name }),
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
notificationStore.showNotification({
|
||||||
|
type: 'error',
|
||||||
|
message: t('settings.fonts.download_failed', { name: pkg.name }),
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
installing.value.delete(pkg.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<BaseSettingCard
|
||||||
|
:title="$t('settings.fonts.title')"
|
||||||
|
:description="$t('settings.fonts.description')"
|
||||||
|
>
|
||||||
|
<p class="text-sm text-muted mb-6">
|
||||||
|
{{ $t('settings.fonts.bundled_info') }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="space-y-3">
|
||||||
|
<div
|
||||||
|
v-for="pkg in packages"
|
||||||
|
:key="pkg.key"
|
||||||
|
class="flex items-center justify-between p-4 border border-line-light rounded-lg"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div class="font-medium text-heading">{{ pkg.name }}</div>
|
||||||
|
<div class="text-xs text-subtle mt-1">
|
||||||
|
{{ pkg.locales.join(', ') }} — {{ pkg.size }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<span
|
||||||
|
v-if="pkg.installed"
|
||||||
|
class="inline-flex items-center rounded-full bg-success px-2.5 py-1 text-xs font-medium text-status-green"
|
||||||
|
>
|
||||||
|
{{ $t('settings.fonts.installed') }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<BaseButton
|
||||||
|
v-else
|
||||||
|
size="sm"
|
||||||
|
variant="primary-outline"
|
||||||
|
:loading="installing.has(pkg.key)"
|
||||||
|
:disabled="installing.has(pkg.key)"
|
||||||
|
@click="installFont(pkg)"
|
||||||
|
>
|
||||||
|
<template #left="slotProps">
|
||||||
|
<BaseIcon
|
||||||
|
v-if="!installing.has(pkg.key)"
|
||||||
|
name="CloudArrowDownIcon"
|
||||||
|
:class="slotProps.class"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
{{ installing.has(pkg.key) ? $t('settings.fonts.downloading') : $t('settings.fonts.install') }}
|
||||||
|
</BaseButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="!packages.length && !isLoading" class="text-center py-8 text-muted">
|
||||||
|
{{ $t('settings.fonts.no_packages') }}
|
||||||
|
</div>
|
||||||
|
</BaseSettingCard>
|
||||||
|
</template>
|
||||||
Reference in New Issue
Block a user