import { useEffect, useState } from 'react' import { adminApi } from '../../../api/client' import type { TranslationFn } from '../../../types' import type { useToast } from '../../../components/shared/Toast' import { ADMIN_EVENT_LABEL_KEYS, ADMIN_CHANNEL_LABEL_KEYS } from '../../../pages/admin/AdminPage.constants' import MToggle from '../../components/MToggle' import { MAdminCard, MAdminCardHead } from './MAdminUi' interface ChannelInfo { id: string active: boolean } interface MatrixData { event_types: string[] channels?: ChannelInfo[] implemented_combos: Record preferences: Record> } const BUILTIN_CHANNELS = ['inapp', 'email', 'webhook', 'ntfy'] as const // Per-event × per-channel admin notification matrix — the mobile layout of // AdminNotificationsPanel. Loads its own data and auto-saves each toggle. export default function MAdminNotifyMatrix({ t, toast }: { t: TranslationFn; toast: ReturnType }) { const [matrix, setMatrix] = useState(null) useEffect(() => { adminApi.getNotificationPreferences().then((data: MatrixData) => setMatrix(data)).catch(() => {}) }, []) if (!matrix) { return (

{t('common.loading')}

) } if (matrix.event_types.length === 0) { return (

{t('settings.notificationPreferences.noChannels')}

) } // Admin-scoped events only go out over the built-in channels (plugin // channels are user-scoped), same rule as the desktop panel. const isActive = (id: string) => matrix.channels?.some((c) => c.id === id && c.active) ?? false const visibleChannels = BUILTIN_CHANNELS.filter( (ch) => isActive(ch) && matrix.event_types.some((evt) => matrix.implemented_combos[evt]?.includes(ch)), ) const toggle = async (eventType: string, channel: string) => { const current = matrix.preferences[eventType]?.[channel] ?? true const updated = { ...matrix.preferences, [eventType]: { ...matrix.preferences[eventType], [channel]: !current }, } setMatrix((m) => (m ? { ...m, preferences: updated } : m)) try { await adminApi.updateNotificationPreferences(updated) } catch { setMatrix((m) => (m ? { ...m, preferences: matrix.preferences } : m)) toast.error(t('common.error')) } } return (
{visibleChannels.map((ch) => ( {t(ADMIN_CHANNEL_LABEL_KEYS[ch])} ))}
{matrix.event_types.map((eventType) => { const implemented = matrix.implemented_combos[eventType] ?? [] // Known admin events use translated labels; anything else keeps its raw id. const labelKey = ADMIN_EVENT_LABEL_KEYS[eventType] return (
{labelKey ? t(labelKey) : eventType} {visibleChannels.map((ch) => ( {implemented.includes(ch) ? ( toggle(eventType, ch)} /> ) : ( — )} ))}
) })}
) }