init
This commit is contained in:
@@ -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>
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user