feat: integrate policy checks for feature enablement in calendar, folder, and plugins settings

This commit is contained in:
Linus Rath
2026-03-25 11:06:35 +01:00
parent 05f6d61cea
commit f64306be5e
4 changed files with 57 additions and 17 deletions
+17 -1
View File
@@ -129,8 +129,24 @@ export async function POST(request: NextRequest) {
const filteredSettings = { ...settings }; const filteredSettings = { ...settings };
for (const key of Object.keys(filteredSettings)) { for (const key of Object.keys(filteredSettings)) {
const restriction = policy.restrictions[key]; const restriction = policy.restrictions[key];
if (restriction?.locked) { if (!restriction) continue;
if (restriction.locked) {
delete filteredSettings[key]; delete filteredSettings[key];
continue;
}
const value = filteredSettings[key];
if (restriction.allowedValues && restriction.allowedValues.length > 0) {
if (!restriction.allowedValues.includes(value)) {
delete filteredSettings[key];
}
}
if (typeof value === 'number') {
if (restriction.min !== undefined && value < restriction.min) {
delete filteredSettings[key];
}
if (restriction.max !== undefined && value > restriction.max) {
delete filteredSettings[key];
}
} }
} }
@@ -3,6 +3,7 @@
import { useTranslations } from 'next-intl'; import { useTranslations } from 'next-intl';
import { useCalendarStore, CalendarViewMode } from '@/stores/calendar-store'; import { useCalendarStore, CalendarViewMode } from '@/stores/calendar-store';
import { useSettingsStore } from '@/stores/settings-store'; import { useSettingsStore } from '@/stores/settings-store';
import { usePolicyStore } from '@/stores/policy-store';
import { SettingsSection, SettingItem, Select, RadioGroup, ToggleSwitch } from './settings-section'; import { SettingsSection, SettingItem, Select, RadioGroup, ToggleSwitch } from './settings-section';
export function CalendarSettings() { export function CalendarSettings() {
@@ -20,6 +21,7 @@ export function CalendarSettings() {
showTasksOnCalendar, showTasksOnCalendar,
updateSetting, updateSetting,
} = useSettingsStore(); } = useSettingsStore();
const { isFeatureEnabled } = usePolicyStore();
return ( return (
<SettingsSection title={t('title')}> <SettingsSection title={t('title')}>
@@ -78,6 +80,8 @@ export function CalendarSettings() {
/> />
</SettingItem> </SettingItem>
{isFeatureEnabled('calendarTasksEnabled') && (
<>
<SettingItem <SettingItem
label={t('enable_tasks')} label={t('enable_tasks')}
description={t('enable_tasks_desc')} description={t('enable_tasks_desc')}
@@ -99,6 +103,8 @@ export function CalendarSettings() {
/> />
</SettingItem> </SettingItem>
)} )}
</>
)}
</SettingsSection> </SettingsSection>
); );
+28 -16
View File
@@ -5,6 +5,7 @@ import { useTranslations } from 'next-intl';
import { useEmailStore } from '@/stores/email-store'; import { useEmailStore } from '@/stores/email-store';
import { useAuthStore } from '@/stores/auth-store'; import { useAuthStore } from '@/stores/auth-store';
import { useSettingsStore } from '@/stores/settings-store'; import { useSettingsStore } from '@/stores/settings-store';
import { usePolicyStore } from '@/stores/policy-store';
import { toast } from '@/stores/toast-store'; import { toast } from '@/stores/toast-store';
import { SettingsSection, SettingItem, Select } from './settings-section'; import { SettingsSection, SettingItem, Select } from './settings-section';
import { import {
@@ -100,6 +101,8 @@ export function FolderSettings() {
const { client } = useAuthStore(); const { client } = useAuthStore();
const { mailboxes, createMailbox, renameMailbox, deleteMailbox, setMailboxRole } = useEmailStore(); const { mailboxes, createMailbox, renameMailbox, deleteMailbox, setMailboxRole } = useEmailStore();
const { folderIcons, setFolderIcon } = useSettingsStore(); const { folderIcons, setFolderIcon } = useSettingsStore();
const { isFeatureEnabled } = usePolicyStore();
const folderIconsAllowed = isFeatureEnabled('folderIconsEnabled');
const [isCreating, setIsCreating] = useState(false); const [isCreating, setIsCreating] = useState(false);
const [creatingParentId, setCreatingParentId] = useState<string | null>(null); const [creatingParentId, setCreatingParentId] = useState<string | null>(null);
@@ -380,22 +383,31 @@ export function FolderSettings() {
<span className="w-4.5 flex-shrink-0" /> <span className="w-4.5 flex-shrink-0" />
)} )}
<div className="relative flex-shrink-0"> <div className="relative flex-shrink-0">
<button {folderIconsAllowed ? (
onClick={() => setIconPickerId(iconPickerId === mb.id ? null : mb.id)} <button
className={cn( onClick={() => setIconPickerId(iconPickerId === mb.id ? null : mb.id)}
"p-1 rounded-md transition-colors", className={cn(
iconPickerId === mb.id "p-1 rounded-md transition-colors",
? "bg-accent" iconPickerId === mb.id
: "hover:bg-accent" ? "bg-accent"
)} : "hover:bg-accent"
title={t('change_icon')} )}
> title={t('change_icon')}
<Icon className={cn( >
"w-4 h-4", <Icon className={cn(
mb.role ? "text-primary" : "text-muted-foreground" "w-4 h-4",
)} /> mb.role ? "text-primary" : "text-muted-foreground"
</button> )} />
{iconPickerId === mb.id && ( </button>
) : (
<span className="p-1">
<Icon className={cn(
"w-4 h-4",
mb.role ? "text-primary" : "text-muted-foreground"
)} />
</span>
)}
{folderIconsAllowed && iconPickerId === mb.id && (
<IconPicker <IconPicker
currentIcon={getIconName(mb)} currentIcon={getIconName(mb)}
onSelect={(iconName) => { onSelect={(iconName) => {
+6
View File
@@ -2,6 +2,7 @@
import { useState, useRef } from 'react'; import { useState, useRef } from 'react';
import { usePluginStore } from '@/stores/plugin-store'; import { usePluginStore } from '@/stores/plugin-store';
import { usePolicyStore } from '@/stores/policy-store';
import { SettingsSection, SettingItem, ToggleSwitch } from './settings-section'; import { SettingsSection, SettingItem, ToggleSwitch } from './settings-section';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { Upload, Trash2, AlertTriangle, Puzzle } from 'lucide-react'; import { Upload, Trash2, AlertTriangle, Puzzle } from 'lucide-react';
@@ -19,10 +20,15 @@ const STATUS_COLORS: Record<PluginStatus, string> = {
export function PluginsSettings() { export function PluginsSettings() {
const { plugins, installPlugin, uninstallPlugin, enablePlugin, disablePlugin, updatePluginSettings } = usePluginStore(); const { plugins, installPlugin, uninstallPlugin, enablePlugin, disablePlugin, updatePluginSettings } = usePluginStore();
const { isFeatureEnabled } = usePolicyStore();
const [isUploading, setIsUploading] = useState(false); const [isUploading, setIsUploading] = useState(false);
const [expandedPlugin, setExpandedPlugin] = useState<string | null>(null); const [expandedPlugin, setExpandedPlugin] = useState<string | null>(null);
const fileInputRef = useRef<HTMLInputElement>(null); const fileInputRef = useRef<HTMLInputElement>(null);
if (!isFeatureEnabled('pluginsEnabled')) {
return null;
}
const handleUpload = async (e: React.ChangeEvent<HTMLInputElement>) => { const handleUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0]; const file = e.target.files?.[0];
if (!file) return; if (!file) return;