diff --git a/frontend/src/apps/mail/components/AppSidebar.vue b/frontend/src/apps/mail/components/AppSidebar.vue index bad7580c4b..dd578107ac 100644 --- a/frontend/src/apps/mail/components/AppSidebar.vue +++ b/frontend/src/apps/mail/components/AppSidebar.vue @@ -107,18 +107,30 @@ import QuotaBar from '@/apps/mail/components/QuotaBar.vue' import type { MailboxData } from '@/apps/mail/types' import BookUser from '~icons/lucide/book-user' +import Clock from '~icons/lucide/clock' import ContactRound from '~icons/lucide/contact-round' import Crown from '~icons/lucide/crown' import Ellipsis from '~icons/lucide/ellipsis' +import Flag from '~icons/lucide/flag' import Globe from '~icons/lucide/globe' +import KeyRound from '~icons/lucide/key-round' +import Lock from '~icons/lucide/lock' import LogOut from '~icons/lucide/log-out' import Mailbox from '~icons/lucide/mailbox' import Mails from '~icons/lucide/mails' +import Megaphone from '~icons/lucide/megaphone' import Plus from '~icons/lucide/plus' +import Radar from '~icons/lucide/radar' +import ScrollText from '~icons/lucide/scroll-text' import Settings from '~icons/lucide/settings' +import Shield from '~icons/lucide/shield' +import ShieldCheck from '~icons/lucide/shield-check' +import Signature from '~icons/lucide/signature' import Star from '~icons/lucide/star' import Trash2 from '~icons/lucide/trash-2' import Users from '~icons/lucide/users' +import UsersRound from '~icons/lucide/users-round' +import Wrench from '~icons/lucide/wrench' const route = useRoute() const router = useRouter() @@ -252,6 +264,42 @@ const menuItems = computed(() => [ const dashboardItems = [ { + label: __('Directory'), + items: [ + { + label: __('Members'), + icon: Users, + to: { name: 'mail-members' }, + activeFor: ['mail-members', 'mail-invites', 'mail-member'], + }, + { + label: __('Groups'), + icon: UsersRound, + to: { name: 'mail-groups' }, + activeFor: ['mail-groups', 'mail-group'], + }, + { + label: __('Mailing Lists'), + icon: Megaphone, + to: { name: 'mail-mailing-lists' }, + activeFor: ['mail-mailing-lists', 'mail-mailing-list'], + }, + { + label: __('Roles'), + icon: Shield, + to: { name: 'mail-roles' }, + activeFor: ['mail-roles', 'mail-role'], + }, + { + label: __('OAuth Clients'), + icon: KeyRound, + to: { name: 'mail-oauth-clients' }, + activeFor: ['mail-oauth-clients', 'mail-oauth-client'], + }, + ], + }, + { + label: __('Domains'), items: [ { label: __('Domains'), @@ -260,10 +308,89 @@ const dashboardItems = [ activeFor: ['mail-domains', 'mail-domain'], }, { - label: __('Members'), - icon: Users, - to: { name: 'mail-members' }, - activeFor: ['mail-members', 'mail-invites', 'mail-member'], + label: __('DKIM Signatures'), + icon: Signature, + to: { name: 'mail-dkim-signatures' }, + activeFor: ['mail-dkim-signatures', 'mail-dkim-signature'], + }, + ], + }, + { + label: __('Emails'), + items: [ + { + label: __('Queued'), + icon: Clock, + to: { name: 'mail-queued-messages' }, + activeFor: ['mail-queued-messages', 'mail-queued-message'], + }, + { + label: __('Delivery Test'), + icon: Radar, + to: { name: 'mail-delivery-test' }, + activeFor: ['mail-delivery-test'], + }, + ], + }, + { + label: __('Inbound Reports'), + items: [ + { + label: __('DMARC'), + icon: ShieldCheck, + to: { name: 'mail-reports-dmarc-inbound' }, + activeFor: ['mail-reports-dmarc-inbound'], + }, + { + label: __('TLS'), + icon: Lock, + to: { name: 'mail-reports-tls-inbound' }, + activeFor: ['mail-reports-tls-inbound'], + }, + { + label: __('ARF'), + icon: Flag, + to: { name: 'mail-reports-arf-inbound' }, + activeFor: ['mail-reports-arf-inbound'], + }, + ], + }, + { + label: __('Outbound Reports'), + items: [ + { + label: __('DMARC'), + icon: ShieldCheck, + to: { name: 'mail-reports-dmarc-outbound' }, + activeFor: ['mail-reports-dmarc-outbound'], + }, + { + label: __('TLS'), + icon: Lock, + to: { name: 'mail-reports-tls-outbound' }, + activeFor: ['mail-reports-tls-outbound'], + }, + ], + }, + { + label: __('Observability'), + items: [ + { + label: __('Logs'), + icon: ScrollText, + to: { name: 'mail-logs' }, + activeFor: ['mail-logs', 'mail-log'], + }, + ], + }, + { + label: __('Actions'), + items: [ + { + label: __('Actions'), + icon: Wrench, + to: { name: 'mail-actions' }, + activeFor: ['mail-actions'], }, ], }, diff --git a/frontend/src/apps/mail/components/DashboardPager.vue b/frontend/src/apps/mail/components/DashboardPager.vue new file mode 100644 index 0000000000..89e235da3a --- /dev/null +++ b/frontend/src/apps/mail/components/DashboardPager.vue @@ -0,0 +1,37 @@ + + + diff --git a/frontend/src/apps/mail/components/Modals/AddGroupEmailModal.vue b/frontend/src/apps/mail/components/Modals/AddGroupEmailModal.vue new file mode 100644 index 0000000000..d5f69cef31 --- /dev/null +++ b/frontend/src/apps/mail/components/Modals/AddGroupEmailModal.vue @@ -0,0 +1,75 @@ + + + diff --git a/frontend/src/apps/mail/components/Modals/AddGroupMembersModal.vue b/frontend/src/apps/mail/components/Modals/AddGroupMembersModal.vue new file mode 100644 index 0000000000..8673d4b3bb --- /dev/null +++ b/frontend/src/apps/mail/components/Modals/AddGroupMembersModal.vue @@ -0,0 +1,58 @@ + + + diff --git a/frontend/src/apps/mail/components/Modals/AddGroupModal.vue b/frontend/src/apps/mail/components/Modals/AddGroupModal.vue new file mode 100644 index 0000000000..1c7ba105d8 --- /dev/null +++ b/frontend/src/apps/mail/components/Modals/AddGroupModal.vue @@ -0,0 +1,102 @@ + + + diff --git a/frontend/src/apps/mail/components/Modals/AddMailingListEmailModal.vue b/frontend/src/apps/mail/components/Modals/AddMailingListEmailModal.vue new file mode 100644 index 0000000000..1b751102df --- /dev/null +++ b/frontend/src/apps/mail/components/Modals/AddMailingListEmailModal.vue @@ -0,0 +1,75 @@ + + + diff --git a/frontend/src/apps/mail/components/Modals/AddMailingListModal.vue b/frontend/src/apps/mail/components/Modals/AddMailingListModal.vue new file mode 100644 index 0000000000..83627f9927 --- /dev/null +++ b/frontend/src/apps/mail/components/Modals/AddMailingListModal.vue @@ -0,0 +1,84 @@ + + + diff --git a/frontend/src/apps/mail/components/Modals/AddMailingListRecipientsModal.vue b/frontend/src/apps/mail/components/Modals/AddMailingListRecipientsModal.vue new file mode 100644 index 0000000000..0c3d8342c6 --- /dev/null +++ b/frontend/src/apps/mail/components/Modals/AddMailingListRecipientsModal.vue @@ -0,0 +1,71 @@ + + + diff --git a/frontend/src/apps/mail/components/Modals/AddMemberEmailModal.vue b/frontend/src/apps/mail/components/Modals/AddMemberEmailModal.vue new file mode 100644 index 0000000000..b9b980de99 --- /dev/null +++ b/frontend/src/apps/mail/components/Modals/AddMemberEmailModal.vue @@ -0,0 +1,75 @@ + + + diff --git a/frontend/src/apps/mail/components/Modals/AddMemberGroupsModal.vue b/frontend/src/apps/mail/components/Modals/AddMemberGroupsModal.vue new file mode 100644 index 0000000000..6e8bf77399 --- /dev/null +++ b/frontend/src/apps/mail/components/Modals/AddMemberGroupsModal.vue @@ -0,0 +1,58 @@ + + + diff --git a/frontend/src/apps/mail/components/Modals/AddMemberMailingListsModal.vue b/frontend/src/apps/mail/components/Modals/AddMemberMailingListsModal.vue new file mode 100644 index 0000000000..050d44d42c --- /dev/null +++ b/frontend/src/apps/mail/components/Modals/AddMemberMailingListsModal.vue @@ -0,0 +1,58 @@ + + + diff --git a/frontend/src/apps/mail/components/Modals/AddMemberModal.vue b/frontend/src/apps/mail/components/Modals/AddMemberModal.vue index e9c4e75cac..b0c0d0dac0 100644 --- a/frontend/src/apps/mail/components/Modals/AddMemberModal.vue +++ b/frontend/src/apps/mail/components/Modals/AddMemberModal.vue @@ -14,26 +14,48 @@ > @@ -85,20 +140,26 @@ diff --git a/frontend/src/apps/mail/components/Modals/AddOAuthContactsModal.vue b/frontend/src/apps/mail/components/Modals/AddOAuthContactsModal.vue new file mode 100644 index 0000000000..836cd1819d --- /dev/null +++ b/frontend/src/apps/mail/components/Modals/AddOAuthContactsModal.vue @@ -0,0 +1,71 @@ + + + diff --git a/frontend/src/apps/mail/components/Modals/AddOAuthRedirectUrisModal.vue b/frontend/src/apps/mail/components/Modals/AddOAuthRedirectUrisModal.vue new file mode 100644 index 0000000000..fd25b77e4d --- /dev/null +++ b/frontend/src/apps/mail/components/Modals/AddOAuthRedirectUrisModal.vue @@ -0,0 +1,63 @@ + + + diff --git a/frontend/src/apps/mail/components/Modals/AddQueuedRecipientModal.vue b/frontend/src/apps/mail/components/Modals/AddQueuedRecipientModal.vue new file mode 100644 index 0000000000..383a8da2b6 --- /dev/null +++ b/frontend/src/apps/mail/components/Modals/AddQueuedRecipientModal.vue @@ -0,0 +1,46 @@ + + + diff --git a/frontend/src/apps/mail/components/Modals/AddRoleModal.vue b/frontend/src/apps/mail/components/Modals/AddRoleModal.vue new file mode 100644 index 0000000000..caa51a86b5 --- /dev/null +++ b/frontend/src/apps/mail/components/Modals/AddRoleModal.vue @@ -0,0 +1,84 @@ + + + diff --git a/frontend/src/apps/mail/components/Modals/ChangeMemberPasswordModal.vue b/frontend/src/apps/mail/components/Modals/ChangeMemberPasswordModal.vue new file mode 100644 index 0000000000..bcb866b987 --- /dev/null +++ b/frontend/src/apps/mail/components/Modals/ChangeMemberPasswordModal.vue @@ -0,0 +1,70 @@ + + + diff --git a/frontend/src/apps/mail/components/Modals/EditGroupModal.vue b/frontend/src/apps/mail/components/Modals/EditGroupModal.vue new file mode 100644 index 0000000000..accdeae4ef --- /dev/null +++ b/frontend/src/apps/mail/components/Modals/EditGroupModal.vue @@ -0,0 +1,87 @@ + + + diff --git a/frontend/src/apps/mail/components/Modals/EditGroupQuotaModal.vue b/frontend/src/apps/mail/components/Modals/EditGroupQuotaModal.vue new file mode 100644 index 0000000000..baaa95a827 --- /dev/null +++ b/frontend/src/apps/mail/components/Modals/EditGroupQuotaModal.vue @@ -0,0 +1,48 @@ + + + diff --git a/frontend/src/apps/mail/components/Modals/EditInviteModal.vue b/frontend/src/apps/mail/components/Modals/EditInviteModal.vue index 45ee0cc803..9b58f81d12 100644 --- a/frontend/src/apps/mail/components/Modals/EditInviteModal.vue +++ b/frontend/src/apps/mail/components/Modals/EditInviteModal.vue @@ -5,6 +5,15 @@ :options="{ title: __('Edit Invite'), actions: [ + ...(canSendInvite + ? [ + { + label: __('Send Invitation Email'), + loading: accountRequest.sendVerificationEmail?.loading, + onClick: sendInvitationEmail, + }, + ] + : []), { label: __('Save'), variant: 'solid', @@ -22,12 +31,23 @@ disabled /> + + + +
+ + + + @@ -51,7 +97,7 @@ diff --git a/frontend/src/apps/mail/components/Modals/EditMailingListModal.vue b/frontend/src/apps/mail/components/Modals/EditMailingListModal.vue new file mode 100644 index 0000000000..5a3b4456eb --- /dev/null +++ b/frontend/src/apps/mail/components/Modals/EditMailingListModal.vue @@ -0,0 +1,48 @@ + + + diff --git a/frontend/src/apps/mail/components/Modals/EditMemberModal.vue b/frontend/src/apps/mail/components/Modals/EditMemberModal.vue new file mode 100644 index 0000000000..03195c944c --- /dev/null +++ b/frontend/src/apps/mail/components/Modals/EditMemberModal.vue @@ -0,0 +1,84 @@ + + + diff --git a/frontend/src/apps/mail/components/Modals/EditMemberQuotaModal.vue b/frontend/src/apps/mail/components/Modals/EditMemberQuotaModal.vue new file mode 100644 index 0000000000..9919b348d9 --- /dev/null +++ b/frontend/src/apps/mail/components/Modals/EditMemberQuotaModal.vue @@ -0,0 +1,53 @@ + + + diff --git a/frontend/src/apps/mail/components/Modals/EditOAuthClientModal.vue b/frontend/src/apps/mail/components/Modals/EditOAuthClientModal.vue new file mode 100644 index 0000000000..4b909723d5 --- /dev/null +++ b/frontend/src/apps/mail/components/Modals/EditOAuthClientModal.vue @@ -0,0 +1,79 @@ + + + diff --git a/frontend/src/apps/mail/components/Modals/EditQueuedMessageModal.vue b/frontend/src/apps/mail/components/Modals/EditQueuedMessageModal.vue new file mode 100644 index 0000000000..33bf43a2ea --- /dev/null +++ b/frontend/src/apps/mail/components/Modals/EditQueuedMessageModal.vue @@ -0,0 +1,47 @@ + + + diff --git a/frontend/src/apps/mail/components/Modals/EditQueuedRecipientModal.vue b/frontend/src/apps/mail/components/Modals/EditQueuedRecipientModal.vue new file mode 100644 index 0000000000..e7eacca480 --- /dev/null +++ b/frontend/src/apps/mail/components/Modals/EditQueuedRecipientModal.vue @@ -0,0 +1,208 @@ + + + diff --git a/frontend/src/apps/mail/components/Modals/EditRoleModal.vue b/frontend/src/apps/mail/components/Modals/EditRoleModal.vue new file mode 100644 index 0000000000..039dfde50a --- /dev/null +++ b/frontend/src/apps/mail/components/Modals/EditRoleModal.vue @@ -0,0 +1,48 @@ + + + diff --git a/frontend/src/apps/mail/components/Modals/RunActionModal.vue b/frontend/src/apps/mail/components/Modals/RunActionModal.vue new file mode 100644 index 0000000000..2cbfd4faa2 --- /dev/null +++ b/frontend/src/apps/mail/components/Modals/RunActionModal.vue @@ -0,0 +1,167 @@ + + + diff --git a/frontend/src/apps/mail/composables/useAccountOptions.ts b/frontend/src/apps/mail/composables/useAccountOptions.ts new file mode 100644 index 0000000000..d96d662538 --- /dev/null +++ b/frontend/src/apps/mail/composables/useAccountOptions.ts @@ -0,0 +1,32 @@ +import { computed } from 'vue' +import { createResource } from 'frappe-ui' + +type Option = { value: string; label: string } + +// Locales and time zones are long, fixed lists (hundreds of entries each) shared by the member and +// group editors, so they are fetched once and cached rather than per dialog. +const accountOptions = createResource({ + url: 'suite.mail.api.admin.get_account_options', + cache: 'accountOptions', +}) + +const localeLabels = computed(() => { + const locales: Option[] = accountOptions.data?.locales || [] + return new Map(locales.map((o) => [o.value, o.label])) +}) + +export const useAccountOptions = () => { + if (!accountOptions.fetched && !accountOptions.loading) accountOptions.fetch() + + return { + localeOptions: computed(() => accountOptions.data?.locales || []), + // Accounts store the bare locale code, so the detail pages resolve it against the same list + // the picker uses. Falls back to the code itself until the list has loaded. + localeLabel: (value?: string | null) => (value ? localeLabels.value.get(value) || value : ''), + // The time zone is nullable on the server, so it needs a blank choice to clear it. + timeZoneOptions: computed(() => [ + { value: '', label: __('Not set') }, + ...(accountOptions.data?.time_zones || []), + ]), + } +} diff --git a/frontend/src/apps/mail/pages/InviteSetupView.vue b/frontend/src/apps/mail/pages/InviteSetupView.vue index 8aef997486..6fad39579a 100644 --- a/frontend/src/apps/mail/pages/InviteSetupView.vue +++ b/frontend/src/apps/mail/pages/InviteSetupView.vue @@ -31,6 +31,18 @@ autocomplete="current-password" required /> +
+ + +
+
+ + +