feat: add user notification settings page
This commit is contained in:
@@ -16,11 +16,12 @@ import Typography from '@mui/material/Typography'
|
|||||||
import useMediaQuery from '@mui/material/useMediaQuery'
|
import useMediaQuery from '@mui/material/useMediaQuery'
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { useUnit } from 'effector-react'
|
import { useUnit } from 'effector-react'
|
||||||
import { MapPin, MessageCircle, Settings, SlidersHorizontal, Truck } from 'lucide-react'
|
import { MapPin, MessageCircle, Settings, SlidersHorizontal, Truck, Bell } from 'lucide-react'
|
||||||
import { Navigate, Route, Routes, useLocation, useNavigate } from 'react-router-dom'
|
import { Navigate, Route, Routes, useLocation, useNavigate } from 'react-router-dom'
|
||||||
import { fetchUnreadMessageCount } from '@/entities/user/api/messages-api'
|
import { fetchUnreadMessageCount } from '@/entities/user/api/messages-api'
|
||||||
import { AddressesPage } from '@/pages/me/ui/sections/AddressesPage'
|
import { AddressesPage } from '@/pages/me/ui/sections/AddressesPage'
|
||||||
import { MessagesPage } from '@/pages/me/ui/sections/MessagesPage'
|
import { MessagesPage } from '@/pages/me/ui/sections/MessagesPage'
|
||||||
|
import { NotificationsPage } from '@/pages/me/ui/sections/NotificationsPage'
|
||||||
import { OrderDetailPage } from '@/pages/me/ui/sections/OrderDetailPage'
|
import { OrderDetailPage } from '@/pages/me/ui/sections/OrderDetailPage'
|
||||||
import { OrdersPage } from '@/pages/me/ui/sections/OrdersPage'
|
import { OrdersPage } from '@/pages/me/ui/sections/OrdersPage'
|
||||||
import { SettingsPage } from '@/pages/me/ui/sections/SettingsPage'
|
import { SettingsPage } from '@/pages/me/ui/sections/SettingsPage'
|
||||||
@@ -56,6 +57,7 @@ export function MeLayoutPage() {
|
|||||||
{ to: '/me/messages', label: 'Сообщения', icon: <MessageCircle /> },
|
{ to: '/me/messages', label: 'Сообщения', icon: <MessageCircle /> },
|
||||||
{ to: '/me/settings', label: 'Настройки', icon: <Settings /> },
|
{ to: '/me/settings', label: 'Настройки', icon: <Settings /> },
|
||||||
{ to: '/me/addresses', label: 'Адреса доставки', icon: <MapPin /> },
|
{ to: '/me/addresses', label: 'Адреса доставки', icon: <MapPin /> },
|
||||||
|
{ to: '/me/notifications', label: 'Оповещения', icon: <Bell /> },
|
||||||
],
|
],
|
||||||
[],
|
[],
|
||||||
)
|
)
|
||||||
@@ -189,6 +191,7 @@ export function MeLayoutPage() {
|
|||||||
<Route path="messages" element={<MessagesPage />} />
|
<Route path="messages" element={<MessagesPage />} />
|
||||||
<Route path="settings" element={<SettingsPage />} />
|
<Route path="settings" element={<SettingsPage />} />
|
||||||
<Route path="addresses" element={<AddressesPage />} />
|
<Route path="addresses" element={<AddressesPage />} />
|
||||||
|
<Route path="notifications" element={<NotificationsPage />} />
|
||||||
<Route path="*" element={<Navigate to="/me/settings" replace />} />
|
<Route path="*" element={<Navigate to="/me/settings" replace />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import { useState } from 'react'
|
||||||
|
import Alert from '@mui/material/Alert'
|
||||||
|
import Box from '@mui/material/Box'
|
||||||
|
import FormControlLabel from '@mui/material/FormControlLabel'
|
||||||
|
import Stack from '@mui/material/Stack'
|
||||||
|
import Switch from '@mui/material/Switch'
|
||||||
|
import Typography from '@mui/material/Typography'
|
||||||
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||||
|
import {
|
||||||
|
fetchUserNotificationSettings,
|
||||||
|
updateUserNotificationSettings,
|
||||||
|
} from '@/entities/notification/api/notifications-api'
|
||||||
|
|
||||||
|
const eventFields = [
|
||||||
|
{ key: 'orderCreated' as const, label: 'Заказ создан' },
|
||||||
|
{ key: 'orderStatusChanged' as const, label: 'Изменение статуса заказа' },
|
||||||
|
{ key: 'orderMessageReceived' as const, label: 'Сообщение в чате заказа' },
|
||||||
|
{ key: 'paymentStatusChanged' as const, label: 'Изменение статуса оплаты' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export function NotificationsPage() {
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
const [error, setError] = useState<string | null>(null)
|
||||||
|
|
||||||
|
const { data, isLoading } = useQuery({
|
||||||
|
queryKey: ['me', 'notifications', 'settings'],
|
||||||
|
queryFn: fetchUserNotificationSettings,
|
||||||
|
})
|
||||||
|
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationFn: updateUserNotificationSettings,
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['me', 'notifications', 'settings'] })
|
||||||
|
},
|
||||||
|
onError: (err: { response?: { data?: { error?: string } } }) => {
|
||||||
|
setError(err.response?.data?.error || 'Ошибка сохранения')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (isLoading) return <Typography>Загрузка...</Typography>
|
||||||
|
|
||||||
|
const settings = data?.settings
|
||||||
|
if (!settings) return <Alert severity="error">Не удалось загрузить настройки</Alert>
|
||||||
|
|
||||||
|
const handleToggle = (field: string, value: boolean) => {
|
||||||
|
setError(null)
|
||||||
|
mutation.mutate({ [field]: value } as Record<string, boolean>)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Typography variant="h4" gutterBottom>
|
||||||
|
Оповещения
|
||||||
|
</Typography>
|
||||||
|
<Typography color="text.secondary" sx={{ mb: 3 }}>
|
||||||
|
Настройте, какие уведомления вы хотите получать на почту.
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
{error && (
|
||||||
|
<Alert severity="error" sx={{ mb: 2 }}>
|
||||||
|
{error}
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Stack spacing={3} sx={{ maxWidth: 480 }}>
|
||||||
|
<Box>
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Switch
|
||||||
|
checked={settings.globalEnabled}
|
||||||
|
onChange={(e) => handleToggle('globalEnabled', e.target.checked)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={<Typography sx={{ fontWeight: 600 }}>Получать оповещения</Typography>}
|
||||||
|
/>
|
||||||
|
<Typography variant="body2" color="text.secondary" sx={{ ml: 4 }}>
|
||||||
|
Включите, чтобы получать уведомления о заказах на почту.
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box sx={{ pl: 4 }}>
|
||||||
|
{eventFields.map(({ key, label }) => (
|
||||||
|
<FormControlLabel
|
||||||
|
key={key}
|
||||||
|
control={
|
||||||
|
<Switch
|
||||||
|
checked={settings[key]}
|
||||||
|
disabled={!settings.globalEnabled}
|
||||||
|
onChange={(e) => handleToggle(key, e.target.checked)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={label}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user