From d9f37acb2bc843ec98602eb348ce00e97a87ef8c Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Thu, 3 Jul 2025 14:34:28 +0200 Subject: [PATCH] Fixed various frontend issues --- frontend/src/App.vue | 7 +-- .../src/components/calendars/CalendarForm.vue | 45 ++++++++++++------- .../src/components/calendars/EventForm.vue | 33 ++++++++------ frontend/src/components/shared/TopMenu.vue | 10 +++-- frontend/src/layouts/admin/AdminLayout.vue | 2 - .../src/layouts/webmail/WebmailLayout.vue | 16 +++---- frontend/src/router/index.js | 14 +++++- frontend/src/stores/domains.store.js | 2 +- frontend/src/views/account/ParametersView.vue | 6 ++- .../views/admin/domains/DomainEditView.vue | 3 ++ frontend/src/views/calendars/CalendarView.vue | 3 +- .../migrations/0011_auto_20250609_0851.py | 2 +- 12 files changed, 90 insertions(+), 53 deletions(-) diff --git a/frontend/src/App.vue b/frontend/src/App.vue index d372e964f..84c12b591 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,6 +1,7 @@ - + diff --git a/frontend/src/components/calendars/CalendarForm.vue b/frontend/src/components/calendars/CalendarForm.vue index 8a1d755b3..168497494 100644 --- a/frontend/src/components/calendars/CalendarForm.vue +++ b/frontend/src/components/calendars/CalendarForm.vue @@ -34,8 +34,8 @@ - {{ $gettext('Close') }} - + {{ $gettext('Close') }} + {{ submitLabel }} @@ -64,14 +64,17 @@ const { $gettext } = useGettext() const busStore = useBusStore() onMounted(() => { - calendar.value = { ...props.initialCalendar } - shared.value = props.initialCalendar.domain !== undefined + if (props.initialCalendar) { + calendar.value = { ...props.initialCalendar } + shared.value = props.initialCalendar.domain !== undefined + } }) const calendar = ref({}) const shared = ref(false) const formErrors = ref({}) const formRef = ref() +const working = ref(false) const submitLabel = computed(() => { if (calendar.value.pk) { @@ -93,19 +96,27 @@ async function submit() { return } let msg - if (calendar.value.pk) { - await calendarsApi.updateUserCalendar(calendar.value.pk, calendar.value) - msg = $gettext('Calendar updated') - } else { - await calendarsApi.createUserCalendar(calendar.value) - msg = $gettext('Calendar created') + working.value = true + try { + if (calendar.value.pk) { + await calendarsApi.updateUserCalendar(calendar.value.pk, calendar.value) + msg = $gettext('Calendar updated') + } else { + await calendarsApi.createUserCalendar(calendar.value) + msg = $gettext('Calendar created') + } + if ( + props.initialCalendar && + props.initialCalendar.color !== calendar.value.color + ) { + emit('colorChanged', calendar.value) + } + close() + busStore.displayNotification({ + msg, + }) + } finally { + working.value = false } - if (props.initialCalendar.color !== calendar.value.color) { - emit('colorChanged', calendar.value) - } - close() - busStore.displayNotification({ - msg, - }) } diff --git a/frontend/src/components/calendars/EventForm.vue b/frontend/src/components/calendars/EventForm.vue index 4334b3a1c..cd7f7708d 100644 --- a/frontend/src/components/calendars/EventForm.vue +++ b/frontend/src/components/calendars/EventForm.vue @@ -82,8 +82,8 @@ - {{ $gettext('Close') }} - + {{ $gettext('Close') }} + {{ $gettext('Save') }} @@ -119,6 +119,7 @@ const userCalendars = ref([]) const form = ref({}) const formErrors = ref({}) const formRef = ref() +const working = ref(false) const title = computed(() => { return props.event ? $gettext('Edit event') : $gettext('New event') @@ -148,7 +149,6 @@ watch( 'yyyy-MM-dd HH:mm' ) form.value.end = DateTime.fromISO(value.end).toFormat('yyyy-MM-dd HH:mm') - console.log(form.value) api.getAttendees().then((resp) => { attendees.value = resp.data }) @@ -166,17 +166,22 @@ async function saveEvent() { if (!valid) { return } - var data = JSON.parse(JSON.stringify(form.value)) - if (!props.event) { - api.createUserEvent(form.value.calendar, data).then(() => { - busStore.displayNotification({ msg: $gettext('Event added') }) - close() - }) - } else { - api.patchUserEvent(form.value.calendar, props.event.id, data).then(() => { - busStore.displayNotification({ msg: $gettext('Event updated') }) - close() - }) + working.value = true + try { + var data = JSON.parse(JSON.stringify(form.value)) + if (!props.event) { + api.createUserEvent(form.value.calendar, data).then(() => { + busStore.displayNotification({ msg: $gettext('Event added') }) + close() + }) + } else { + api.patchUserEvent(form.value.calendar, props.event.id, data).then(() => { + busStore.displayNotification({ msg: $gettext('Event updated') }) + close() + }) + } + } finally { + working.value = false } } diff --git a/frontend/src/components/shared/TopMenu.vue b/frontend/src/components/shared/TopMenu.vue index fc844b669..65f55a55f 100644 --- a/frontend/src/components/shared/TopMenu.vue +++ b/frontend/src/components/shared/TopMenu.vue @@ -78,9 +78,13 @@ const { $gettext } = useGettext() const applications = ref([]) const userInitials = computed(() => { - return props.user.first_name && props.user.last_name - ? `${props.user.first_name[0].toUpperCase()}${props.user.last_name[0].toUpperCase()}` - : props.user.username.slice(0, 2).toUpperCase() + if (props.user.first_name && props.user.last_name) { + return `${props.user.first_name[0].toUpperCase()}${props.user.last_name[0].toUpperCase()}` + } + if (props.user.username) { + props.user.username.slice(0, 2).toUpperCase() + } + return '' }) const userMenuItems = computed(() => { diff --git a/frontend/src/layouts/admin/AdminLayout.vue b/frontend/src/layouts/admin/AdminLayout.vue index e129947bc..09b7cb640 100644 --- a/frontend/src/layouts/admin/AdminLayout.vue +++ b/frontend/src/layouts/admin/AdminLayout.vue @@ -5,14 +5,12 @@ diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index d12e30212..c11c7c43f 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -25,6 +25,9 @@ const routes = [ { path: '/admin', component: () => import('@/layouts/admin/AdminLayout.vue'), + meta: { + allowedRoles: ['DomainAdmins', 'Resellers', 'SuperAdmins'], + }, children: [ { path: '', @@ -287,6 +290,7 @@ const routes = [ component: () => import('@/layouts/user/UserLayout.vue'), meta: { requiresAuth: true, + requiresMailbox: true, }, children: [ { @@ -304,6 +308,7 @@ const routes = [ component: () => import('@/layouts/user/UserLayout.vue'), meta: { requiresAuth: true, + requiresMailbox: true, }, children: [ { @@ -312,6 +317,7 @@ const routes = [ component: () => import('@/views/calendars/CalendarView.vue'), meta: { requiresAuth: true, + requiresMailbox: true, }, }, ], @@ -321,6 +327,7 @@ const routes = [ component: () => import('@/layouts/webmail/WebmailLayout.vue'), meta: { requiresAuth: true, + requiresMailbox: true, }, children: [ { @@ -329,6 +336,7 @@ const routes = [ component: () => import('@/views/webmail/MailboxView.vue'), meta: { requiresAuth: true, + requiresMailbox: true, }, }, { @@ -337,6 +345,7 @@ const routes = [ component: () => import('@/views/webmail/EmailView.vue'), meta: { requiresAuth: true, + requiresMailbox: true, }, }, { @@ -345,6 +354,7 @@ const routes = [ component: () => import('@/views/webmail/ComposeEmailView.vue'), meta: { requiresAuth: true, + requiresMailbox: true, }, }, { @@ -353,6 +363,7 @@ const routes = [ component: () => import('@/views/webmail/ReplyEmailView.vue'), meta: { requiresAuth: true, + requiresMailbox: true, }, }, { @@ -361,6 +372,7 @@ const routes = [ component: () => import('@/views/webmail/ForwardEmailView.vue'), meta: { requiresAuth: true, + requiresMailbox: true, }, }, ], @@ -391,7 +403,7 @@ router.beforeEach(async (to, from, next) => { if (isAuth) { if (to.meta.allowedRoles !== undefined) { if (to.meta.allowedRoles.indexOf(authStore.authUser.role) === -1) { - next({ name: 'Dashboard' }) + next({ name: 'AccountSettings' }) return } } diff --git a/frontend/src/stores/domains.store.js b/frontend/src/stores/domains.store.js index 954b9af2b..d717accb3 100644 --- a/frontend/src/stores/domains.store.js +++ b/frontend/src/stores/domains.store.js @@ -5,7 +5,7 @@ import domainApi from '@/api/domains' export const useDomainsStore = defineStore('domains', () => { const domainsLoaded = ref(false) - const domains = ref({}) + const domains = ref([]) async function $reset() { domainsLoaded.value = false diff --git a/frontend/src/views/account/ParametersView.vue b/frontend/src/views/account/ParametersView.vue index 7fee58675..545650da7 100644 --- a/frontend/src/views/account/ParametersView.vue +++ b/frontend/src/views/account/ParametersView.vue @@ -14,7 +14,7 @@ diff --git a/frontend/src/views/admin/domains/DomainEditView.vue b/frontend/src/views/admin/domains/DomainEditView.vue index b6a8b64ca..a0adccd5a 100644 --- a/frontend/src/views/admin/domains/DomainEditView.vue +++ b/frontend/src/views/admin/domains/DomainEditView.vue @@ -11,12 +11,15 @@ import DomainEditForm from '@/components/admin/domains/DomainEditForm' import { useRoute } from 'vue-router' import { ref } from 'vue' import domainsApi from '@/api/domains' +import { useDomainsStore } from '@/stores' const { $gettext } = useGettext() const route = useRoute() +const domainsStore = useDomainsStore() const domain = ref({}) +domainsStore.getDomains() domainsApi.getDomain(route.params.id).then((resp) => { domain.value = resp.data }) diff --git a/frontend/src/views/calendars/CalendarView.vue b/frontend/src/views/calendars/CalendarView.vue index 32a6ad201..347a4bb08 100644 --- a/frontend/src/views/calendars/CalendarView.vue +++ b/frontend/src/views/calendars/CalendarView.vue @@ -252,8 +252,9 @@ async function deleteCalendar(calendarPk) { } await api.deleteUserCalendar(calendarPk) const fullc = calendarRef.value.getApi() - const evtSource = fullc.getEventSourceById(`user-${calendar.pk}`) + const evtSource = fullc.getEventSourceById(`user-${calendarPk}`) evtSource.remove() + fetchUserCalendars() busStore.displayNotification({ msg: $gettext('Calendar deleted') }) } diff --git a/modoboa/autoreply/migrations/0011_auto_20250609_0851.py b/modoboa/autoreply/migrations/0011_auto_20250609_0851.py index b35b2f9ce..4609147dc 100644 --- a/modoboa/autoreply/migrations/0011_auto_20250609_0851.py +++ b/modoboa/autoreply/migrations/0011_auto_20250609_0851.py @@ -14,7 +14,7 @@ def cleanup_and_rename(apps, schema_editor): # Rename parameters LocalConfig = apps.get_model("core", "LocalConfig") cfg = LocalConfig.objects.first() - if not cfg: + if not cfg or "postfix_autoreply" not in cfg._parameters: return cfg._parameters["autoreply"] = copy.deepcopy(cfg._parameters["postfix_autoreply"]) del cfg._parameters["postfix_autoreply"]