+ {groupNames.map((groupName, gi) => (
+
+ {gi > 0 && }
+
+ ))}
+
+ );
+}
diff --git a/frontend/src/pages/Mail.tsx b/frontend/src/pages/Mail.tsx
index ea8966a..faff3b9 100644
--- a/frontend/src/pages/Mail.tsx
+++ b/frontend/src/pages/Mail.tsx
@@ -15,6 +15,7 @@ import { MailDetail } from '@/components/mail/MailDetail';
import { ComposeModal, type ComposeMode } from '@/components/mail/ComposeModal';
import { SharedMailboxSelector } from '@/components/mail/SharedMailboxSelector';
import { MailSearchBar } from '@/components/mail/MailSearchBar';
+import { usePluginToolbarStore } from '@/store/pluginToolbarStore';
import {
fetchAccounts,
fetchFolders,
@@ -280,6 +281,57 @@ export function MailPage() {
setSearchQuery('');
}, []);
+ // Register toolbar items
+ const { registerItems, unregisterPlugin } = usePluginToolbarStore();
+ useEffect(() => {
+ registerItems('mail', [
+ {
+ id: 'account-select',
+ plugin: 'mail',
+ label: 'Account',
+ type: 'select',
+ group: 'account',
+ selectValue: selectedAccountId,
+ selectOptions: accounts.map((a) => ({ value: a.id, label: a.email })),
+ onSelect: handleAccountSwitch,
+ },
+ {
+ id: 'search',
+ plugin: 'mail',
+ label: 'Suchen',
+ type: 'search',
+ group: 'search',
+ searchPlaceholder: 'E-Mails durchsuchen...',
+ onSearch: handleSearch,
+ },
+ {
+ id: 'compose',
+ plugin: 'mail',
+ label: 'Verfassen',
+ group: 'actions',
+ icon: ,
+ onClick: handleCompose,
+ },
+ {
+ id: 'sync',
+ plugin: 'mail',
+ label: 'Sync',
+ group: 'actions',
+ icon: ,
+ onClick: () => { /* sync trigger */ },
+ },
+ {
+ id: 'settings',
+ plugin: 'mail',
+ label: 'Einstellungen',
+ group: 'actions',
+ icon: ,
+ onClick: () => window.location.href = '/mail/settings',
+ },
+ ]);
+ return () => unregisterPlugin('mail');
+ }, [registerItems, unregisterPlugin, accounts, selectedAccountId, handleAccountSwitch, handleSearch, handleCompose]);
+
if (loadingAccounts) {
return (
+ {groups[groupName].map((item) => {
+ if (item.type === 'search') return ;
+ if (item.type === 'select') return ;
+ return ;
+ })}
+
+
@@ -306,29 +358,6 @@ export function MailPage() {
return (
- {/* Top bar */}
-
) => void;
+}
+
+export const usePluginToolbarStore = create((set) => ({
+ items: [],
+ activePlugin: null,
+ registerItems: (plugin, newItems) =>
+ set((state) => ({
+ items: [...state.items.filter((i) => i.plugin !== plugin), ...newItems],
+ activePlugin: plugin,
+ })),
+ unregisterPlugin: (plugin) =>
+ set((state) => ({
+ items: state.items.filter((i) => i.plugin !== plugin),
+ activePlugin: state.activePlugin === plugin ? null : state.activePlugin,
+ })),
+ setActivePlugin: (plugin) => set({ activePlugin: plugin }),
+ updateItem: (plugin, id, updates) =>
+ set((state) => ({
+ items: state.items.map((i) =>
+ i.plugin === plugin && i.id === id ? { ...i, ...updates } : i
+ ),
+ })),
+}));
-
-
-
{error && (
-
-
-
-
-
- {t('mail.settings')}
-
-
- {error}
diff --git a/frontend/src/store/pluginToolbarStore.ts b/frontend/src/store/pluginToolbarStore.ts new file mode 100644 index 0000000..8801346 --- /dev/null +++ b/frontend/src/store/pluginToolbarStore.ts @@ -0,0 +1,49 @@ +import { create } from 'zustand'; + +export interface ToolbarItem { + id: string; + plugin: string; + label: string; + icon?: React.ReactNode; + onClick: () => void; + group?: string; + disabled?: boolean; + active?: boolean; + type?: 'button' | 'search' | 'select'; + searchPlaceholder?: string; + onSearch?: (query: string) => void; + selectOptions?: { value: string; label: string }[]; + selectValue?: string; + onSelect?: (value: string) => void; +} + +interface PluginToolbarState { + items: ToolbarItem[]; + activePlugin: string | null; + registerItems: (plugin: string, items: ToolbarItem[]) => void; + unregisterPlugin: (plugin: string) => void; + setActivePlugin: (plugin: string | null) => void; + updateItem: (plugin: string, id: string, updates: Partial