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"]