diff --git a/components/settings/filter-settings.tsx b/components/settings/filter-settings.tsx index 1799f26e..a7101d55 100644 --- a/components/settings/filter-settings.tsx +++ b/components/settings/filter-settings.tsx @@ -9,6 +9,7 @@ import { SieveEditorModal } from "@/components/filters/sieve-editor-modal"; import { useFilterStore } from "@/stores/filter-store"; import { useAuthStore } from "@/stores/auth-store"; import { useEmailStore } from "@/stores/email-store"; +import { useSettingsStore } from "@/stores/settings-store"; import { toast } from "@/stores/toast-store"; import type { FilterRule } from "@/lib/jmap/sieve-types"; import { @@ -25,31 +26,98 @@ import { function RuleSummary({ rule }: { rule: FilterRule }) { const t = useTranslations("settings.filters"); - const conditionSummary = rule.conditions - .slice(0, 2) - .map((c) => { - const field = t(`condition_fields.${c.field}`); - const comparator = t(`comparators.${c.comparator}`); - return `${field} ${comparator} "${c.value}"`; - }) - .join(rule.matchType === "all" ? ` ${t("and")} ` : ` ${t("or")} `); + const conditions = rule.conditions.slice(0, 2).map((c) => { + const field = t(`condition_fields.${c.field}`); + const comparator = t(`comparators.${c.comparator}`); + return `${field} ${comparator} "${c.value}"`; + }); + + const joiner = rule.matchType === "all" ? t("and") : t("or"); const extra = rule.conditions.length > 2 ? ` (+${rule.conditions.length - 2})` : ""; - const actionSummary = rule.actions - .slice(0, 2) - .map((a) => { - const action = t(`action_types.${a.type}`); - return a.value ? `${action} "${a.value}"` : action; - }) - .join(", "); + const actions = rule.actions.slice(0, 2).map((a) => { + const action = t(`action_types.${a.type}`); + return a.value ? `${action} "${a.value}"` : action; + }); return ( - - {conditionSummary}{extra} → {actionSummary} - +
+ + {conditions.map((cond, i) => ( + + {i > 0 && {joiner} } + {cond} + + ))} + {extra} + + + + {actions.map((act, i) => ( + + {i > 0 && ", "} + {act} + + ))} + +
+ ); +} + +function VisualRuleSummary({ rule }: { rule: FilterRule }) { + const t = useTranslations("settings.filters"); + + const joiner = rule.matchType === "all" ? t("and") : t("or"); + const matchLabel = rule.matchType === "all" ? t("match_all_conditions") : t("match_any_condition"); + + return ( +
+
+ + {t("if")} + + {rule.conditions.map((c, i) => { + const field = t(`condition_fields.${c.field}`); + const comparator = t(`comparators.${c.comparator}`); + return ( + + {i > 0 && ( + {joiner} + )} + + {field} + {comparator} + “{c.value}” + + + ); + })} + ({matchLabel}) +
+ +
+ + {t("then")} + + {rule.actions.map((a, i) => { + const action = t(`action_types.${a.type}`); + return ( + + {i > 0 && ( + + )} + + {action} + {a.value && “{a.value}”} + + + ); + })} +
+
); } @@ -58,6 +126,8 @@ export function FilterSettings() { const tNotifications = useTranslations("notifications"); const { client } = useAuthStore(); const mailboxes = useEmailStore((s) => s.mailboxes); + const expandedFilterView = useSettingsStore((s) => s.expandedFilterView); + const updateSetting = useSettingsStore((s) => s.updateSetting); const { rules, @@ -337,23 +407,25 @@ export function FilterSettings() { onDragOver={(e) => handleDragOver(e, index)} onDrop={(e) => handleDrop(e, index)} onDragEnd={handleDragEnd} - className={`flex items-center gap-3 p-3 rounded-md border transition-colors ${ + className={`flex items-start gap-3 p-3 rounded-md border transition-colors ${ dragOverIndex === index ? "border-primary bg-primary/5" : "border-border hover:bg-muted/50" } ${!rule.enabled ? "opacity-60" : ""}`} >
- handleToggle(rule.id)} - /> +
+ handleToggle(rule.id)} + /> +
{rule.name}

- + {expandedFilterView ? ( + + ) : ( + + )}
{deleteConfirmId === rule.id ? ( @@ -435,12 +511,23 @@ export function FilterSettings() { - {isSaving && ( -
- - {t("saving")} -
- )} +
+ {isSaving && ( +
+ + {t("saving")} +
+ )} + {!isOpaque && rules.length > 0 && ( +
+ {t("expanded_view")} + updateSetting("expandedFilterView", v)} + /> +
+ )} +
{showRuleModal && ( diff --git a/locales/de/common.json b/locales/de/common.json index e346d95d..810f9138 100644 --- a/locales/de/common.json +++ b/locales/de/common.json @@ -1183,6 +1183,12 @@ "opaque_warning": "Dieses Skript wurde außerhalb des visuellen Builders bearbeitet. Nur die Sieve-Skriptbearbeitung ist verfügbar.", "open_sieve_editor": "Sieve-Skript-Editor öffnen", "fetch_error": "Filter konnten nicht geladen werden", + "expanded_view": "Erweiterte Ansicht", + "expanded_view_description": "Filterregeln mit detaillierten Bedingungs- und Aktionsblöcken anzeigen", + "if": "Wenn", + "then": "Dann", + "match_all_conditions": "alle zutreffen", + "match_any_condition": "eine zutrifft", "and": "und", "or": "oder", "cancel": "Abbrechen", diff --git a/locales/en/common.json b/locales/en/common.json index 3c1c3a78..a28fe788 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -1183,6 +1183,12 @@ "opaque_warning": "This script was edited outside the visual builder. Only raw Sieve editing is available.", "open_sieve_editor": "Open raw Sieve editor", "fetch_error": "Failed to load filters", + "expanded_view": "Expanded view", + "expanded_view_description": "Show filter rules with detailed condition and action blocks", + "if": "If", + "then": "Then", + "match_all_conditions": "all match", + "match_any_condition": "any matches", "and": "and", "or": "or", "cancel": "Cancel", diff --git a/locales/es/common.json b/locales/es/common.json index 70186d5f..607d0b97 100644 --- a/locales/es/common.json +++ b/locales/es/common.json @@ -1183,6 +1183,12 @@ "opaque_warning": "Este script fue editado fuera del constructor visual. Solo está disponible la edición Sieve sin formato.", "open_sieve_editor": "Abrir editor Sieve", "fetch_error": "Error al cargar los filtros", + "expanded_view": "Vista expandida", + "expanded_view_description": "Mostrar reglas de filtro con bloques detallados de condiciones y acciones", + "if": "Si", + "then": "Entonces", + "match_all_conditions": "todas coinciden", + "match_any_condition": "alguna coincide", "and": "y", "or": "o", "cancel": "Cancelar", diff --git a/locales/fr/common.json b/locales/fr/common.json index 62c22f76..518c219f 100644 --- a/locales/fr/common.json +++ b/locales/fr/common.json @@ -1183,6 +1183,12 @@ "opaque_warning": "Ce script a été modifié en dehors du constructeur visuel. Seule l'édition Sieve brute est disponible.", "open_sieve_editor": "Ouvrir l'éditeur Sieve brut", "fetch_error": "Échec du chargement des filtres", + "expanded_view": "Vue étendue", + "expanded_view_description": "Afficher les règles de filtre avec des blocs de conditions et d'actions détaillés", + "if": "Si", + "then": "Alors", + "match_all_conditions": "toutes correspondent", + "match_any_condition": "une correspond", "and": "et", "or": "ou", "cancel": "Annuler", diff --git a/locales/it/common.json b/locales/it/common.json index 677512b2..3a204e69 100644 --- a/locales/it/common.json +++ b/locales/it/common.json @@ -1183,6 +1183,12 @@ "opaque_warning": "Questo script è stato modificato al di fuori del costruttore visuale. È disponibile solo la modifica Sieve grezza.", "open_sieve_editor": "Apri editor Sieve", "fetch_error": "Impossibile caricare i filtri", + "expanded_view": "Vista espansa", + "expanded_view_description": "Mostra le regole dei filtri con blocchi dettagliati di condizioni e azioni", + "if": "Se", + "then": "Allora", + "match_all_conditions": "tutte corrispondono", + "match_any_condition": "una corrisponde", "and": "e", "or": "o", "cancel": "Annulla", diff --git a/locales/ja/common.json b/locales/ja/common.json index 7711294e..5834dd51 100644 --- a/locales/ja/common.json +++ b/locales/ja/common.json @@ -1183,6 +1183,12 @@ "opaque_warning": "このスクリプトはビジュアルビルダーの外部で編集されました。Sieveスクリプトの直接編集のみ可能です。", "open_sieve_editor": "Sieveスクリプトエディタを開く", "fetch_error": "フィルターの読み込みに失敗しました", + "expanded_view": "詳細表示", + "expanded_view_description": "フィルタールールを条件とアクションのブロックで表示", + "if": "条件", + "then": "実行", + "match_all_conditions": "すべて一致", + "match_any_condition": "いずれか一致", "and": "かつ", "or": "または", "cancel": "キャンセル", diff --git a/locales/nl/common.json b/locales/nl/common.json index 73b0b0cd..edd39f68 100644 --- a/locales/nl/common.json +++ b/locales/nl/common.json @@ -1183,6 +1183,12 @@ "opaque_warning": "Dit script is buiten de visuele builder bewerkt. Alleen Sieve-scriptbewerking is beschikbaar.", "open_sieve_editor": "Sieve-scripteditor openen", "fetch_error": "Filters konden niet worden geladen", + "expanded_view": "Uitgebreide weergave", + "expanded_view_description": "Filterregels weergeven met gedetailleerde voorwaarde- en actieblokken", + "if": "Als", + "then": "Dan", + "match_all_conditions": "alle overeenkomen", + "match_any_condition": "een overeenkomt", "and": "en", "or": "of", "cancel": "Annuleren", diff --git a/locales/pt/common.json b/locales/pt/common.json index 1a5b55a8..c7f0e5e4 100644 --- a/locales/pt/common.json +++ b/locales/pt/common.json @@ -1183,6 +1183,12 @@ "opaque_warning": "Este script foi editado fora do construtor visual. Apenas a edição Sieve bruta está disponível.", "open_sieve_editor": "Abrir editor Sieve", "fetch_error": "Falha ao carregar filtros", + "expanded_view": "Vista expandida", + "expanded_view_description": "Mostrar regras de filtro com blocos detalhados de condições e ações", + "if": "Se", + "then": "Então", + "match_all_conditions": "todas correspondem", + "match_any_condition": "uma corresponde", "and": "e", "or": "ou", "cancel": "Cancelar", diff --git a/stores/settings-store.ts b/stores/settings-store.ts index 7b85f8d2..84b5fce3 100644 --- a/stores/settings-store.ts +++ b/stores/settings-store.ts @@ -119,6 +119,9 @@ interface SettingsState { sessionTimeout: number; // minutes (0 = never) trustedSenders: string[]; // Email addresses that can load external content + // Filters + expandedFilterView: boolean; + // Calendar showTimeInMonthView: boolean; showWeekNumbers: boolean; @@ -220,6 +223,9 @@ const DEFAULT_SETTINGS = { sessionTimeout: 0, // Never trustedSenders: [] as string[], + // Filters + expandedFilterView: false, + // Calendar showTimeInMonthView: false, showWeekNumbers: false, @@ -308,6 +314,7 @@ export const useSettingsStore = create()( calendarNotificationsEnabled: state.calendarNotificationsEnabled, calendarNotificationSound: state.calendarNotificationSound, calendarInvitationParsingEnabled: state.calendarInvitationParsingEnabled, + expandedFilterView: state.expandedFilterView, showTimeInMonthView: state.showTimeInMonthView, showWeekNumbers: state.showWeekNumbers, toolbarPosition: state.toolbarPosition,