This commit is contained in:
2026-08-30 17:02:49 +02:00
parent 78cb70d8e8
commit 6b8cccc082
3 changed files with 191 additions and 5 deletions
@@ -0,0 +1,115 @@
<script lang="ts" setup>
import {ref, watch} from 'vue'
import {useChannelStore, type Channel} from '@/stores/channel'
const props = defineProps<{
modelValue: boolean
channel: Channel | null
}>()
const emit = defineEmits<{
(e: 'update:modelValue', value: boolean): void
(e: 'updated', channel: Channel): void
}>()
const channelStore = useChannelStore()
const formData = ref({name: '', channel_type: 'text'})
const isSubmitting = ref(false)
const error = ref<string | null>(null)
const channelTypeOptions = [
{title: 'Text', value: 'text'},
{title: 'Voice', value: 'voice'},
{title: 'DM', value: 'dm'},
]
function resetForm() {
formData.value = {
name: props.channel?.name ?? '',
channel_type: props.channel?.channel_type ?? 'text',
}
error.value = null
}
function handleClose() {
emit('update:modelValue', false)
resetForm()
}
async function handleSubmit() {
const channel = props.channel
const name = formData.value.name.trim()
if (!channel || !name) return
isSubmitting.value = true
error.value = null
try {
const updatedChannel = await channelStore.updateChannel(channel.id, {
name,
channel_type: formData.value.channel_type,
server_id: channel.server_id ?? null,
category_id: channel.category_id ?? null,
})
emit('updated', updatedChannel)
handleClose()
} catch (err) {
error.value = err instanceof Error ? err.message : 'Failed to update channel'
} finally {
isSubmitting.value = false
}
}
watch(() => [props.modelValue, props.channel] as const, ([isOpen]) => {
if (isOpen) resetForm()
})
</script>
<template>
<v-dialog :model-value="modelValue" width="400" @update:model-value="handleClose">
<v-card>
<v-card-title>Edit Channel</v-card-title>
<v-card-text>
<v-alert v-if="error" class="mb-4" density="compact" type="error" variant="tonal">
{{ error }}
</v-alert>
<div class="space-y-4">
<v-text-field
v-model="formData.name"
density="compact"
label="Channel Name"
outlined
@keyup.enter="handleSubmit"
/>
<v-select
v-model="formData.channel_type"
:items="channelTypeOptions"
density="compact"
label="Channel Type"
outlined
/>
</div>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn :disabled="isSubmitting" variant="text" @click="handleClose">Cancel</v-btn>
<v-btn
:disabled="!formData.name.trim()"
:loading="isSubmitting"
color="primary"
variant="tonal"
@click="handleSubmit"
>
Save
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<style scoped>
.space-y-4 {
display: flex;
flex-direction: column;
gap: 1rem;
}
</style>
+29 -4
View File
@@ -3,12 +3,14 @@ import {storeToRefs} from 'pinia'
import {useChannelStore} from '@/stores/channel'
import {useCategoryStore} from '@/stores/category'
import {computed, onMounted, onUnmounted, ref, watch} from 'vue'
import {useRoute} from 'vue-router'
import {useRoute, useRouter} from 'vue-router'
import CreateChannelDialog from '@/components/channel/CreateChannelDialog.vue'
import EditChannelDialog from '@/components/channel/EditChannelDialog.vue'
import CreateCategoryDialog from '@/components/category/CreateCategoryDialog.vue'
import {type MenuItem, useContextMenu} from '@/composables/useContextMenu'
import {useUserStore} from "@/stores/user.ts";
import {type OrderedResourceType, useServerStore} from "@/stores/server.ts";
import type {Channel} from '@/stores/channel'
import ChannelPermissionsDialog from '@/components/permissions/ChannelPermissionsDialog.vue'
import {useAuthStore} from '@/stores/auth'
import ServerSettingsDialog from '@/components/server/ServerSettingsDialog.vue'
@@ -20,6 +22,7 @@ const props = defineProps<{
}>()
const route = useRoute()
const router = useRouter()
const channelStore = useChannelStore()
const categoryStore = useCategoryStore()
const userStore = useUserStore()
@@ -29,6 +32,8 @@ const {openContextMenu} = useContextMenu()
const authStore = useAuthStore()
const showPermissionsDialog = ref(false)
const selectedChannel = ref<any | null>(null)
const channelToEdit = ref<Channel | null>(null)
const showEditChannelDialog = ref(false)
const showServerSettings = ref(false)
const serverName = computed(() => serverStore.servers.find(server => server.id === props.serverId)?.name || 'Serveur')
@@ -195,12 +200,26 @@ function onCategoryContextMenu(event: MouseEvent, category: any) {
}
// Right click menu (channel)
async function openEditDialog(channel: any) {
console.log("edit dialog clicked")
function openEditDialog(channel: Channel) {
channelToEdit.value = channel
showEditChannelDialog.value = true
}
async function deleteChannel(channelId: string) {
console.log("delete channel clicked")
const channel = channelStore.channels.find(item => item.id === channelId)
const channelName = channel?.name || 'ce canal'
if (!window.confirm(`Supprimer « ${channelName} » ? Cette action est irréversible.`)) return
try {
await channelStore.deleteChannel(channelId)
if (route.params.channelId === channelId) {
await router.push(`/server/${props.serverId}`)
}
await refreshServerTree()
} catch (error) {
console.error('Failed to delete channel:', error)
alert(error instanceof Error ? error.message : 'Failed to delete channel')
}
}
function onChannelContextMenu(event: MouseEvent, channel: any) {
@@ -342,6 +361,12 @@ function onChannelContextMenu(event: MouseEvent, channel: any) {
@created="refreshServerTree"
/>
<EditChannelDialog
v-model="showEditChannelDialog"
:channel="channelToEdit"
@updated="refreshServerTree"
/>
<CreateCategoryDialog
v-model="showCategoryDialog"
:server-id="serverId"
+47 -1
View File
@@ -1,7 +1,7 @@
import {defineStore} from 'pinia'
import {useApi} from "@/composables/useApi.ts";
interface Channel {
export interface Channel {
id: string
name?: string
channel_type: string
@@ -60,6 +60,52 @@ export const useChannelStore = defineStore('channel', {
this.loading = false;
}
},
async updateChannel(channelId: string, payload: {
name?: string;
channel_type: string;
server_id?: string | null;
category_id?: string | null;
}) {
this.loading = true;
this.error = null;
try {
const response = await useApi().put(`/channels/${channelId}`, payload);
if (!response.ok) {
const error = await response.json().catch(() => null);
throw new Error(error?.message || 'Failed to update channel');
}
const updatedChannel: Channel = await response.json();
const index = this.channels.findIndex(channel => channel.id === channelId);
if (index >= 0) {
updatedChannel.unread_count ??= this.channels[index].unread_count ?? 0;
this.channels[index] = updatedChannel;
}
return updatedChannel;
} catch (err) {
this.error = err instanceof Error ? err.message : 'Unknown error';
throw err;
} finally {
this.loading = false;
}
},
async deleteChannel(channelId: string) {
this.loading = true;
this.error = null;
try {
const response = await useApi().delete(`/channels/${channelId}`);
if (!response.ok) {
const error = await response.json().catch(() => null);
throw new Error(error?.message || 'Failed to delete channel');
}
this.channels = this.channels.filter(channel => channel.id !== channelId);
} catch (err) {
this.error = err instanceof Error ? err.message : 'Unknown error';
throw err;
} finally {
this.loading = false;
}
},
reset() {
this.channels = [];
this.loading = false;