diff --git a/app/[locale]/contacts/page.tsx b/app/[locale]/contacts/page.tsx index 3fc1f59f..1785759e 100644 --- a/app/[locale]/contacts/page.tsx +++ b/app/[locale]/contacts/page.tsx @@ -2,7 +2,7 @@ import { useState, useEffect, useCallback, useRef, useMemo } from "react"; import { useTranslations } from "next-intl"; -import { ArrowLeft, Users } from "lucide-react"; +import { ArrowLeft, Users, AlertTriangle } from "lucide-react"; import { Button } from "@/components/ui/button"; import { ConfirmDialog } from "@/components/ui/confirm-dialog"; import { useConfirmDialog } from "@/hooks/use-confirm-dialog"; @@ -19,6 +19,7 @@ import { AppTopBannerSlot } from "@/components/plugins/app-top-banner-slot"; import { useContactStore, getContactDisplayName } from "@/stores/contact-store"; import { useAuthStore, redirectToLogin } from "@/stores/auth-store"; import { useEmailStore } from "@/stores/email-store"; +import { usePolicyStore } from "@/stores/policy-store"; import { toast } from "@/stores/toast-store"; import { cn, generateUUID } from "@/lib/utils"; import { NavigationRail } from "@/components/layout/navigation-rail"; @@ -43,6 +44,7 @@ type View = export default function ContactsPage() { const t = useTranslations("contacts"); + const contactsEnabled = usePolicyStore((s) => s.isFeatureEnabled('contactsEnabled')); const { client, isAuthenticated, logout, checkAuth, isLoading: authLoading } = useAuthStore(); const { showAppsModal, inlineApp, loadedApps, handleManageApps, handleInlineApp, closeInlineApp, closeAppsModal } = useSidebarApps(); const [initialCheckDone, setInitialCheckDone] = useState(() => useAuthStore.getState().isAuthenticated && !!useAuthStore.getState().client); @@ -641,6 +643,18 @@ export default function ContactsPage() { } }; + if (!contactsEnabled) { + return ( +
+
+ +

Contacts feature is disabled by your administrator

+

Please contact your administrator if you need access.

+
+
+ ); + } + const showListPanel = !isMobile || view === "list"; const showRightPanel = !isMobile || view !== "list"; diff --git a/app/[locale]/settings/page.tsx b/app/[locale]/settings/page.tsx index d349d383..32aad8d7 100644 --- a/app/[locale]/settings/page.tsx +++ b/app/[locale]/settings/page.tsx @@ -590,7 +590,7 @@ export default function SettingsPage() { // Apps ...(supportsCalendar ? [{ id: 'calendar' as Tab, label: t('tabs.calendar'), icon: tabIcons.calendar, group: 'apps' as TabGroup }] : []), - { id: 'contacts', label: t('tabs.contacts'), icon: tabIcons.contacts, group: 'apps' }, + ...(isFeatureEnabled('contactsEnabled') ? [{ id: 'contacts' as Tab, label: t('tabs.contacts'), icon: tabIcons.contacts, group: 'apps' as TabGroup }] : []), ...(supportsFiles && isFeatureEnabled('filesEnabled') ? [{ id: 'files' as Tab, label: t('tabs.files'), icon: tabIcons.files, group: 'apps' as TabGroup }] : []), ...(isFeatureEnabled('sidebarAppsEnabled') ? [{ id: 'sidebar_apps' as Tab, label: t('tabs.sidebar_apps'), icon: tabIcons.sidebar_apps, group: 'apps' as TabGroup }] : []), diff --git a/app/admin/_tabs/auth.tsx b/app/admin/_tabs/auth.tsx index 95aca959..b56a66f6 100644 --- a/app/admin/_tabs/auth.tsx +++ b/app/admin/_tabs/auth.tsx @@ -273,6 +273,8 @@ export function AuthTab() { + +
diff --git a/components/layout/navigation-rail.tsx b/components/layout/navigation-rail.tsx index d4a7d3a8..d7a08cf1 100644 --- a/components/layout/navigation-rail.tsx +++ b/components/layout/navigation-rail.tsx @@ -175,6 +175,7 @@ export function NavigationRail({ const showRailAccountList = useSettingsStore((s) => s.showRailAccountList); const sidebarAppsEnabled = usePolicyStore((s) => s.isFeatureEnabled('sidebarAppsEnabled')); const filesEnabled = usePolicyStore((s) => s.isFeatureEnabled('filesEnabled')); + const contactsEnabled = usePolicyStore((s) => s.isFeatureEnabled('contactsEnabled')); const visibleSidebarApps = sidebarAppsEnabled ? sidebarApps : []; const inboxUnread = mailboxes.find(m => m.role === "inbox")?.unreadEmails || 0; const [isStalwartAdmin, setIsStalwartAdmin] = useState(false); @@ -254,7 +255,7 @@ export function NavigationRail({ const navItems: NavItem[] = [ { id: "mail", icon: Mail, labelKey: "mail", href: "/", badge: inboxUnread }, { id: "calendar", icon: Calendar, labelKey: "calendar", href: "/calendar", hidden: !supportsCalendar }, - { id: "contacts", icon: BookUser, labelKey: "contacts", href: "/contacts", hidden: !supportsContacts }, + { id: "contacts", icon: BookUser, labelKey: "contacts", href: "/contacts", hidden: !supportsContacts || !contactsEnabled }, { id: "files", icon: HardDrive, labelKey: "files", href: "/files", hidden: !supportsFiles || !filesEnabled }, ]; diff --git a/components/settings/content-senders-settings.tsx b/components/settings/content-senders-settings.tsx index 7fd02d79..bd72b083 100644 --- a/components/settings/content-senders-settings.tsx +++ b/components/settings/content-senders-settings.tsx @@ -12,7 +12,7 @@ import { useContactStore } from '@/stores/contact-store'; export function ContentSendersSettings() { const t = useTranslations('settings.email_behavior'); const [showTrustedModal, setShowTrustedModal] = useState(false); - const { isSettingLocked, isSettingHidden } = usePolicyStore(); + const { isSettingLocked, isSettingHidden, isFeatureEnabled } = usePolicyStore(); const { externalContentPolicy, @@ -32,7 +32,7 @@ export function ContentSendersSettings() { return ( - {!isSettingHidden('externalContentPolicy') && ( + {isFeatureEnabled('externalContentEnabled') && !isSettingHidden('externalContentPolicy') && (