init
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
<script lang="ts" setup>
|
||||
import {computed, ref, watch} from 'vue'
|
||||
import {useUserStore} from '@/stores/user'
|
||||
import {useApi} from '@/composables/useApi'
|
||||
import {usePermissions} from '@/composables/usePermissions'
|
||||
import ChannelPermissionEditor from './ChannelPermissionEditor.vue'
|
||||
import {toChannelPermissionMask, type ChannelPermissionMask} from '@/types/permissions'
|
||||
|
||||
interface Role { id: string; server_id: string; name: string; is_default: boolean }
|
||||
interface Channel { id: string; name?: string | null }
|
||||
|
||||
const props = defineProps<{ modelValue: boolean; channel: Channel | null; serverId: string }>()
|
||||
const emit = defineEmits<{ 'update:modelValue': [value: boolean] }>()
|
||||
|
||||
const users = useUserStore()
|
||||
const api = useApi()
|
||||
const permissionsApi = usePermissions()
|
||||
const roles = ref<Role[]>([])
|
||||
const targetType = ref<'role' | 'user'>('role')
|
||||
const targetId = ref<string | null>(null)
|
||||
const mask = ref<ChannelPermissionMask>(toChannelPermissionMask(0))
|
||||
const loadingTargets = ref(false)
|
||||
const loadingPermission = ref(false)
|
||||
const saving = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
|
||||
const targets = computed(() => targetType.value === 'role' ? roles.value : users.users)
|
||||
const targetLabel = computed(() => targetType.value === 'role' ? 'Rôle' : 'Membre')
|
||||
const targetName = computed(() => {
|
||||
const target = targets.value.find(item => item.id === targetId.value)
|
||||
return targetType.value === 'role' ? (target as Role | undefined)?.name : (target as typeof users.users[number] | undefined)?.username
|
||||
})
|
||||
|
||||
watch(() => props.modelValue, async open => {
|
||||
if (open) await loadTargets()
|
||||
else reset()
|
||||
})
|
||||
watch(targetType, () => { targetId.value = null; mask.value = toChannelPermissionMask(0); error.value = null })
|
||||
watch(targetId, loadPermission)
|
||||
|
||||
async function loadTargets() {
|
||||
loadingTargets.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const [roleResponse] = await Promise.all([
|
||||
api.get('/groups'),
|
||||
users.fetchUsers(props.serverId),
|
||||
])
|
||||
if (!roleResponse.ok) throw new Error('Impossible de charger les rôles')
|
||||
roles.value = (await roleResponse.json() as Role[]).filter(role => role.server_id === props.serverId)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Erreur lors du chargement des cibles'
|
||||
} finally { loadingTargets.value = false }
|
||||
}
|
||||
|
||||
async function loadPermission() {
|
||||
if (!props.channel || !targetId.value) return
|
||||
loadingPermission.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const permission = targetType.value === 'role'
|
||||
? await permissionsApi.getChannelRolePermission(props.channel.id, targetId.value)
|
||||
: await permissionsApi.getChannelUserPermission(props.channel.id, targetId.value)
|
||||
mask.value = permission.permissions
|
||||
} catch (e) {
|
||||
// L'absence de ligne signifie un masque vide, pas une erreur d'interface.
|
||||
if (e instanceof Error && ((e as Error & { status?: number }).status === 404 || /404|introuvable/i.test(e.message))) mask.value = toChannelPermissionMask(0)
|
||||
else error.value = e instanceof Error ? e.message : 'Erreur lors du chargement des permissions'
|
||||
} finally { loadingPermission.value = false }
|
||||
}
|
||||
|
||||
async function save(value: ChannelPermissionMask) {
|
||||
if (!props.channel || !targetId.value) return
|
||||
saving.value = true; error.value = null
|
||||
try {
|
||||
if (targetType.value === 'role') await permissionsApi.setChannelRolePermission(props.channel.id, targetId.value, value)
|
||||
else await permissionsApi.setChannelUserPermission(props.channel.id, targetId.value, value)
|
||||
close()
|
||||
} catch (e) { error.value = e instanceof Error ? e.message : 'Erreur lors de la sauvegarde' }
|
||||
finally { saving.value = false }
|
||||
}
|
||||
|
||||
async function resetPermission() {
|
||||
if (!props.channel || !targetId.value) return
|
||||
saving.value = true; error.value = null
|
||||
try {
|
||||
if (targetType.value === 'role') await permissionsApi.removeChannelRolePermission(props.channel.id, targetId.value)
|
||||
else await permissionsApi.removeChannelUserPermission(props.channel.id, targetId.value)
|
||||
mask.value = toChannelPermissionMask(0)
|
||||
} catch (e) {
|
||||
if (!(e instanceof Error && ((e as Error & { status?: number }).status === 404 || /404|introuvable/i.test(e.message)))) error.value = e instanceof Error ? e.message : 'Erreur lors de la réinitialisation'
|
||||
} finally { saving.value = false }
|
||||
}
|
||||
|
||||
function close() { emit('update:modelValue', false) }
|
||||
function reset() { targetId.value = null; mask.value = toChannelPermissionMask(0); error.value = null }
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-dialog :model-value="modelValue" max-width="760" @update:model-value="emit('update:modelValue', $event)">
|
||||
<v-card>
|
||||
<v-card-title>Permissions — {{ channel?.name || 'Canal' }}</v-card-title>
|
||||
<v-card-text>
|
||||
<v-alert v-if="error" class="mb-4" type="error" density="compact">{{ error }}</v-alert>
|
||||
<v-tabs v-model="targetType" class="mb-4">
|
||||
<v-tab value="role">Rôles</v-tab>
|
||||
<v-tab value="user">Membres</v-tab>
|
||||
</v-tabs>
|
||||
<v-select
|
||||
v-model="targetId"
|
||||
:items="targets"
|
||||
:item-title="targetType === 'role' ? 'name' : 'username'"
|
||||
item-value="id"
|
||||
:label="targetLabel"
|
||||
:loading="loadingTargets"
|
||||
clearable
|
||||
/>
|
||||
<v-chip v-if="targetName" class="mb-3" color="primary" size="small">{{ targetName }}</v-chip>
|
||||
<ChannelPermissionEditor
|
||||
v-if="targetId"
|
||||
v-model="mask"
|
||||
:loading="loadingPermission || saving"
|
||||
title="Permissions du canal"
|
||||
@save="save"
|
||||
/>
|
||||
<v-btn v-if="targetId" class="mt-3" color="error" variant="text" :loading="saving" @click="resetPermission">
|
||||
Réinitialiser les permissions directes
|
||||
</v-btn>
|
||||
</v-card-text>
|
||||
<v-card-actions><v-spacer/><v-btn variant="text" @click="close">Fermer</v-btn></v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
@@ -22,10 +22,11 @@ export function usePermissions() {
|
||||
async function parseResponse<T>(response: Response): Promise<T> {
|
||||
if (!response.ok) {
|
||||
const error = await response.json().catch(() => null)
|
||||
|
||||
throw new Error(
|
||||
const exception = new Error(
|
||||
error?.message || 'Erreur lors de la gestion des permissions',
|
||||
)
|
||||
) as Error & { status?: number }
|
||||
exception.status = response.status
|
||||
throw exception
|
||||
}
|
||||
|
||||
if (response.status === 204) {
|
||||
@@ -232,4 +233,4 @@ export function usePermissions() {
|
||||
setChannelRolePermission,
|
||||
removeChannelRolePermission,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import CreateCategoryDialog from '@/components/category/CreateCategoryDialog.vue
|
||||
import {type MenuItem, useContextMenu} from '@/composables/useContextMenu'
|
||||
import {useUserStore} from "@/stores/user.ts";
|
||||
import {useServerStore} from "@/stores/server.ts";
|
||||
import ChannelPermissionsDialog from '@/components/permissions/ChannelPermissionsDialog.vue'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
|
||||
const props = defineProps<{
|
||||
serverId: string
|
||||
@@ -22,6 +24,9 @@ const userStore = useUserStore()
|
||||
const serverStore = useServerStore()
|
||||
const {currentTree} = storeToRefs(serverStore)
|
||||
const {openContextMenu} = useContextMenu()
|
||||
const authStore = useAuthStore()
|
||||
const showPermissionsDialog = ref(false)
|
||||
const selectedChannel = ref<any | null>(null)
|
||||
|
||||
|
||||
const loadServerData = async (targetServerId: string) => {
|
||||
@@ -111,6 +116,14 @@ async function deleteChannel(channelId: string) {
|
||||
|
||||
function onChannelContextMenu(event: MouseEvent, channel: any) {
|
||||
const menuItems: MenuItem[] = [
|
||||
...(authStore.isAdmin ? [{
|
||||
label: 'Gérer les permissions',
|
||||
icon: 'mdi-shield-key',
|
||||
action: () => {
|
||||
selectedChannel.value = channel
|
||||
showPermissionsDialog.value = true
|
||||
},
|
||||
}] : []),
|
||||
{
|
||||
label: 'Marquer comme lu',
|
||||
icon: 'mdi-check',
|
||||
@@ -194,6 +207,12 @@ function onChannelContextMenu(event: MouseEvent, channel: any) {
|
||||
@created="refreshServerTree"
|
||||
/>
|
||||
|
||||
<ChannelPermissionsDialog
|
||||
v-model="showPermissionsDialog"
|
||||
:channel="selectedChannel"
|
||||
:server-id="serverId"
|
||||
/>
|
||||
|
||||
<v-main>
|
||||
<router-view/>
|
||||
</v-main>
|
||||
|
||||
Reference in New Issue
Block a user