uchill/front_material/components/profile/NotificationSettingsSection...

245 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import React from 'react';
import { Switch } from '@/components/common/Switch';
import type { NotificationPreference } from '@/api/notifications';
const NOTIFICATION_TYPES = [
{ key: 'lesson_created', label: 'Создание занятия', description: 'Уведомления о новых занятиях' },
{ key: 'lesson_cancelled', label: 'Отмена занятия', description: 'Уведомления об отмене занятий' },
{ key: 'lesson_reminder', label: 'Напоминания о занятиях', description: 'Напоминания о предстоящих занятиях' },
{ key: 'homework_assigned', label: 'Назначение ДЗ', description: 'Уведомления о новых домашних заданиях' },
{ key: 'homework_submitted', label: 'Сдача ДЗ', description: 'Уведомления о сданных домашних заданиях' },
{ key: 'homework_reviewed', label: 'Проверка ДЗ', description: 'Уведомления о проверенных домашних заданиях' },
{ key: 'message_received', label: 'Сообщения', description: 'Уведомления о новых сообщениях в чате' },
{ key: 'subscription_expiring', label: 'Истечение подписки', description: 'Уведомления об истечении подписки' },
{ key: 'subscription_expired', label: 'Подписка истекла', description: 'Уведомления об истекшей подписке' },
];
const PARENT_EXCLUDED_TYPES = [
'lesson_created',
'lesson_cancelled',
'lesson_reminder',
'homework_assigned',
'homework_submitted',
'homework_reviewed',
];
interface NotificationSettingsSectionProps {
preferences: NotificationPreference | null;
onChange: (prefs: NotificationPreference) => void;
userRole?: string;
hasTelegram?: boolean;
disabled?: boolean;
}
export function NotificationSettingsSection({
preferences,
onChange,
userRole = 'mentor',
hasTelegram = false,
disabled = false,
}: NotificationSettingsSectionProps) {
const types =
userRole === 'parent'
? NOTIFICATION_TYPES.filter((t) => !PARENT_EXCLUDED_TYPES.includes(t.key))
: NOTIFICATION_TYPES;
const toggleChannel = (field: keyof NotificationPreference, value: boolean) => {
if (!preferences) return;
onChange({ ...preferences, [field]: value });
};
const toggleType = (notificationType: string, channel: 'in_app' | 'email' | 'telegram', value: boolean) => {
if (!preferences) return;
const typePrefs = { ...(preferences.type_preferences || {}) };
if (!typePrefs[notificationType]) typePrefs[notificationType] = {};
typePrefs[notificationType] = { ...typePrefs[notificationType], [channel]: value };
onChange({ ...preferences, type_preferences: typePrefs });
};
const getTypeValue = (typeKey: string, channel: 'in_app' | 'email' | 'telegram') => {
const typePrefs = preferences?.type_preferences?.[typeKey];
if (typePrefs && typePrefs[channel] !== undefined) return typePrefs[channel];
if (channel === 'in_app') return preferences?.in_app_enabled ?? true;
if (channel === 'email') return preferences?.email_enabled ?? true;
return preferences?.telegram_enabled ?? false;
};
const isChannelDisabled = (channel: 'in_app' | 'email' | 'telegram') => {
if (preferences?.enabled === false) return true;
if (channel === 'in_app') return !(preferences?.in_app_enabled ?? true);
if (channel === 'email') return !(preferences?.email_enabled ?? true);
return !(preferences?.telegram_enabled ?? false) || !hasTelegram;
};
if (!preferences) return null;
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
{/* Глобальное включение */}
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '10px 0',
borderBottom: '1px solid var(--ios26-list-divider)',
}}
>
<div>
<div style={{ fontSize: 14, fontWeight: 600, color: 'var(--md-sys-color-on-surface)' }}>
Уведомления включены
</div>
<div style={{ fontSize: 11, color: 'var(--md-sys-color-on-surface-variant)', marginTop: 2 }}>
Полностью отключить все уведомления
</div>
</div>
<Switch
checked={preferences.enabled !== false}
onChange={(v) => toggleChannel('enabled', v)}
disabled={disabled}
size="compact"
/>
</div>
{/* Каналы */}
<div>
<div
style={{
fontSize: 10,
fontWeight: 600,
letterSpacing: '0.05em',
color: 'var(--md-sys-color-on-surface-variant)',
marginBottom: 8,
}}
>
КАНАЛЫ
</div>
<div
style={{
display: 'flex',
flexDirection: 'column',
gap: 6,
opacity: preferences.enabled === false ? 0.5 : 1,
}}
>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '6px 0' }}>
<div>
<div style={{ fontSize: 13, fontWeight: 500 }}>Email</div>
<div style={{ fontSize: 11, color: 'var(--md-sys-color-on-surface-variant)' }}>
По электронной почте
</div>
</div>
<Switch
checked={preferences.email_enabled ?? true}
onChange={(v) => toggleChannel('email_enabled', v)}
disabled={disabled || preferences.enabled === false}
size="compact"
/>
</div>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '6px 0' }}>
<div>
<div style={{ fontSize: 13, fontWeight: 500 }}>Telegram</div>
<div style={{ fontSize: 11, color: 'var(--md-sys-color-on-surface-variant)' }}>
{hasTelegram ? 'Подключен' : 'Не подключен'}
</div>
</div>
<Switch
checked={preferences.telegram_enabled ?? false}
onChange={(v) => toggleChannel('telegram_enabled', v)}
disabled={disabled || !hasTelegram || preferences.enabled === false}
size="compact"
/>
</div>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '6px 0' }}>
<div>
<div style={{ fontSize: 13, fontWeight: 500 }}>В приложении</div>
<div style={{ fontSize: 11, color: 'var(--md-sys-color-on-surface-variant)' }}>
Показывать в приложении
</div>
</div>
<Switch
checked={preferences.in_app_enabled ?? true}
onChange={(v) => toggleChannel('in_app_enabled', v)}
disabled={disabled || preferences.enabled === false}
size="compact"
/>
</div>
</div>
</div>
{/* Матрица типов по каналам */}
<div>
<div
style={{
fontSize: 10,
fontWeight: 600,
letterSpacing: '0.05em',
color: 'var(--md-sys-color-on-surface-variant)',
marginBottom: 8,
}}
>
ТИПЫ ПО КАНАЛАМ
</div>
<div
style={{
border: '1px solid var(--ios26-list-divider)',
borderRadius: 10,
overflow: 'hidden',
display: 'grid',
gridTemplateColumns: '1fr auto auto auto',
background: 'var(--md-sys-color-surface-container-low)',
}}
>
{/* Header */}
<div style={{ padding: 8, fontWeight: 600, fontSize: 11, color: 'var(--md-sys-color-on-surface-variant)' }}>Тип</div>
<div style={{ padding: 8, textAlign: 'center', fontWeight: 600, fontSize: 11, color: 'var(--md-sys-color-on-surface-variant)', minWidth: 64 }}>В приложении</div>
<div style={{ padding: 8, textAlign: 'center', fontWeight: 600, fontSize: 11, color: 'var(--md-sys-color-on-surface-variant)', minWidth: 64 }}>Email</div>
<div style={{ padding: 8, textAlign: 'center', fontWeight: 600, fontSize: 11, color: 'var(--md-sys-color-on-surface-variant)', minWidth: 64 }}>Telegram</div>
{/* Rows */}
{types.map((type) => (
<React.Fragment key={type.key}>
<div
style={{
padding: 8,
borderTop: '1px solid var(--ios26-list-divider)',
background: '#fff',
fontSize: 12,
}}
>
<div style={{ fontWeight: 500 }}>{type.label}</div>
<div style={{ fontSize: 10, color: 'var(--md-sys-color-on-surface-variant)', marginTop: 1 }}>{type.description}</div>
</div>
<div style={{ padding: 6, display: 'flex', alignItems: 'center', justifyContent: 'center', borderTop: '1px solid var(--ios26-list-divider)', background: '#fff' }}>
<Switch
checked={getTypeValue(type.key, 'in_app')}
onChange={(v) => toggleType(type.key, 'in_app', v)}
disabled={disabled || isChannelDisabled('in_app')}
size="compact"
/>
</div>
<div style={{ padding: 6, display: 'flex', alignItems: 'center', justifyContent: 'center', borderTop: '1px solid var(--ios26-list-divider)', background: '#fff' }}>
<Switch
checked={getTypeValue(type.key, 'email')}
onChange={(v) => toggleType(type.key, 'email', v)}
disabled={disabled || isChannelDisabled('email')}
size="compact"
/>
</div>
<div style={{ padding: 6, display: 'flex', alignItems: 'center', justifyContent: 'center', borderTop: '1px solid var(--ios26-list-divider)', background: '#fff' }}>
<Switch
checked={getTypeValue(type.key, 'telegram')}
onChange={(v) => toggleType(type.key, 'telegram', v)}
disabled={disabled || isChannelDisabled('telegram')}
size="compact"
/>
</div>
</React.Fragment>
))}
</div>
</div>
</div>
);
}