feat: roles SPA foundations

This commit is contained in:
Florian Sylvain committed 2026-10-27 22:21:12 +01:00
1 parent 80f7037d3a
commit 907949aa12
180 files changed
+4308 -835

No files matched your search

+22 -9
View File
@@ -3,7 +3,6 @@ import { formatNumber, t } from '@/i18n'
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'
import { useMediaQuery } from '@vueuse/core'
import { useRouter } from 'vue-router'
import { useLocalizedOverlay, useLocalizedToast } from '@/i18n/presentation'
import ConfirmDialog from '@/components/ConfirmDialog.vue'
import DashboardShell from '@/components/DashboardShell.vue'
@@ -13,7 +12,8 @@ import CacheSkeleton from '@/components/variants/CacheSkeleton.vue'
import CacheStatTile from '@/components/variants/CacheStatTile.vue'
import CacheTopImagesCard from '@/components/variants/CacheTopImagesCard.vue'
import TypeToConfirmDialog from '@/components/variants/TypeToConfirmDialog.vue'
import VariantsForbidden from '@/components/variants/VariantsForbidden.vue'
import NoAccessState from '@/components/NoAccessState.vue'
import ReadOnlyBanner from '@/components/ReadOnlyBanner.vue'
import VariantsTabs from '@/components/variants/VariantsTabs.vue'
import type {
CacheImageStat,
@@ -25,6 +25,7 @@ import { cacheSize } from '@/lib/variants/cacheFormat'
import { isHandledByApiClient, isStatus } from '@/lib/variants/variantErrors'
import { formatVariantSize } from '@/lib/variants/variantFormat'
import { useSessionStore } from '@/stores/session'
import { usePermissionsStore } from '@/stores/permissions'
import { useVariantCacheStore } from '@/stores/variantCache'
const DESKTOP_QUERY = '(min-width: 40rem)'
@@ -32,8 +33,8 @@ const SKELETON_DELAY_MS = 150
const NOT_FOUND = 404
const CONFIRM_WORD = 'purge'
const router = useRouter()
const session = useSessionStore()
const permissions = usePermissionsStore()
const store = useVariantCacheStore()
const overlay = useLocalizedOverlay()
const toast = useLocalizedToast()
@@ -47,9 +48,8 @@ let skeletonTimer: ReturnType<typeof setTimeout> | null = null
let requested = false
const sessionReady = computed(() => session.status === 'ready')
const canManage = computed(
() => session.session?.role === 'owner' || session.session?.role === 'admin',
)
const canManage = computed(() => permissions.can('settings.view'))
const canEdit = computed(() => permissions.can('settings.edit_general'))
const forbidden = computed(
() => (sessionReady.value && !canManage.value) || store.loadState === 'forbidden',
)
@@ -57,10 +57,15 @@ const stats = computed(() => store.stats)
const ready = computed(() => store.loadState === 'ready' && stats.value !== null)
const dimmed = computed(() => store.isPurging)
const purgeUnusedDisabled = computed(
() => !ready.value || store.isPurging || (stats.value?.unused.files ?? 0) === 0,
() =>
!canEdit.value || !ready.value || store.isPurging || (stats.value?.unused.files ?? 0) === 0,
)
const purgeAllDisabled = computed(
() => !ready.value || store.isPurging || (stats.value?.generated_files ?? 0) === 0,
() =>
!canEdit.value ||
!ready.value ||
store.isPurging ||
(stats.value?.generated_files ?? 0) === 0,
)
function clearSkeletonTimer() {
@@ -295,7 +300,7 @@ function onPurgeImage(item: CacheImageStat) {
/>
</template>
<VariantsForbidden v-if="forbidden" @home="router.push('/home')" />
<NoAccessState v-if="forbidden" />
<div v-else-if="sessionReady" class="flex flex-col gap-4">
<div class="flex flex-col gap-3 sm:flex-row sm:items-start sm:gap-4">
@@ -339,6 +344,14 @@ function onPurgeImage(item: CacheImageStat) {
</div>
</div>
<ReadOnlyBanner
v-if="!canEdit"
:title="t('You can read these settings but not change them')"
:description="
t('Ask the owner to change them, or ask for a role that can edit settings.')
"
/>
<VariantsTabs active="cache" />
<CacheSkeleton