Merge branch 'style2'

This commit is contained in:
Kirill
2026-05-14 22:03:10 +05:00
23 changed files with 354 additions and 224 deletions
-1
View File
@@ -17,7 +17,6 @@
"dependencies": { "dependencies": {
"@emotion/react": "^11.14.0", "@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1", "@emotion/styled": "^11.14.1",
"@mui/icons-material": "^9.0.0",
"@mui/material": "^9.0.0", "@mui/material": "^9.0.0",
"@tanstack/react-query": "^5.100.5", "@tanstack/react-query": "^5.100.5",
"@tiptap/extension-placeholder": "^3.22.5", "@tiptap/extension-placeholder": "^3.22.5",
+16 -6
View File
@@ -1,6 +1,4 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import Inventory2OutlinedIcon from '@mui/icons-material/Inventory2Outlined'
import MenuRoundedIcon from '@mui/icons-material/MenuRounded'
import AppBar from '@mui/material/AppBar' import AppBar from '@mui/material/AppBar'
import Badge from '@mui/material/Badge' import Badge from '@mui/material/Badge'
import Box from '@mui/material/Box' import Box from '@mui/material/Box'
@@ -13,6 +11,7 @@ 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 { Menu, Package } from 'lucide-react'
import { Link as RouterLink, useNavigate } from 'react-router-dom' import { Link as RouterLink, useNavigate } from 'react-router-dom'
import { useThemeController } from '@/app/providers/theme-controller' import { useThemeController } from '@/app/providers/theme-controller'
import { fetchMyCart } from '@/entities/cart/api/cart-api' import { fetchMyCart } from '@/entities/cart/api/cart-api'
@@ -101,7 +100,7 @@ export function AppHeader() {
edge="start" edge="start"
sx={{ mr: 1 }} sx={{ mr: 1 }}
> >
<MenuRoundedIcon /> <Menu />
</IconButton> </IconButton>
)} )}
@@ -137,7 +136,7 @@ export function AppHeader() {
<Tooltip title="Заказы"> <Tooltip title="Заказы">
<IconButton color="inherit" sx={{ ml: 1 }} onClick={() => navigate('/me/orders')} aria-label="Заказы"> <IconButton color="inherit" sx={{ ml: 1 }} onClick={() => navigate('/me/orders')} aria-label="Заказы">
<Badge color="secondary" badgeContent={activeOrdersCount} invisible={activeOrdersCount === 0}> <Badge color="secondary" badgeContent={activeOrdersCount} invisible={activeOrdersCount === 0}>
<Inventory2OutlinedIcon /> <Package />
</Badge> </Badge>
</IconButton> </IconButton>
</Tooltip> </Tooltip>
@@ -156,8 +155,19 @@ export function AppHeader() {
)} )}
{!isMobile && ( {!isMobile && (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, ml: 1.5 }}> <Box sx={{ display: 'flex', alignItems: 'center', gap: 1, ml: 1.5 }}>
<SchemeSwitcher value={scheme} onChange={(s: ColorScheme) => setScheme(s)} /> <Box
sx={{
display: 'flex',
alignItems: 'center',
bgcolor: 'rgba(255, 255, 255, 0.25)',
borderRadius: 3,
px: 0.5,
py: 0.5,
}}
>
<SchemeSwitcher value={scheme} onChange={(s: ColorScheme) => setScheme(s)} />
</Box>
<ModeSwitcher mode={mode} resolvedMode={resolvedMode} onCycleMode={cycleMode} /> <ModeSwitcher mode={mode} resolvedMode={resolvedMode} onCycleMode={cycleMode} />
</Box> </Box>
)} )}
+14 -4
View File
@@ -5,10 +5,11 @@ import Divider from '@mui/material/Divider'
import Grid from '@mui/material/Grid' import Grid from '@mui/material/Grid'
import Link from '@mui/material/Link' import Link from '@mui/material/Link'
import Stack from '@mui/material/Stack' import Stack from '@mui/material/Stack'
import SvgIcon from '@mui/material/SvgIcon'
import Typography from '@mui/material/Typography' import Typography from '@mui/material/Typography'
import { Link as RouterLink } from 'react-router-dom' import { Link as RouterLink } from 'react-router-dom'
import { AppHeader } from '@/app/layout/AppHeader' import { AppHeader } from '@/app/layout/AppHeader'
import { STORE_EMAIL, STORE_NAME, STORE_PHONE, STORE_SOCIAL_NOTE } from '@/shared/config' import { STORE_EMAIL, STORE_NAME, STORE_PHONE, VK_URL } from '@/shared/config'
export function MainLayout({ children }: PropsWithChildren) { export function MainLayout({ children }: PropsWithChildren) {
const year = new Date().getFullYear() const year = new Date().getFullYear()
@@ -82,9 +83,18 @@ export function MainLayout({ children }: PropsWithChildren) {
{STORE_PHONE} {STORE_PHONE}
</Link> </Link>
</Typography> </Typography>
<Typography variant="caption" color="text.secondary"> <Link
{STORE_SOCIAL_NOTE} href={VK_URL}
</Typography> target="_blank"
rel="noopener noreferrer"
color="text.secondary"
sx={{ display: 'inline-flex', alignItems: 'center', gap: 0.5, '&:hover': { color: '#4A76A8' } }}
>
<SvgIcon sx={{ fontSize: 20 }}>
<path d="M12.776 2.553a.267.267 0 0 0-.104-.002C10.184 2.973 4.08 7.575 2.34 9.478c-.19.208-.33.505-.33.85 0 .344.122.634.3.85.556.688 2.005 1.759 2.005 1.759s1.168 2.52 1.76 3.677c.232.463.45.858.604 1.13.348.611.534.857.926 1.077.392.22.776.22 1.194.01.417-.212 2.452-1.61 3.46-2.36.256-.19.49-.19.73.01 1.517 1.256 3.2 2.743 4.003 3.586.373.391.701.548 1.104.548.402 0 .683-.283.805-.86.053-.25 1.025-5.076 1.025-5.076s.633-2.24 2.763-3.897c.292-.228.462-.57.462-.95 0-.38-.143-.68-.38-.895-1.198-1.088-5.9-3.039-6.365-3.226z" />
</SvgIcon>
VK
</Link>
</Stack> </Stack>
</Grid> </Grid>
</Grid> </Grid>
+25
View File
@@ -94,6 +94,31 @@ function AppThemeInner({ children }: PropsWithChildren) {
h4: { fontWeight: 700 }, h4: { fontWeight: 700 },
h5: { fontWeight: 600 }, h5: { fontWeight: 600 },
}, },
components: {
MuiButton: {
styleOverrides: {
root: { textTransform: 'none', borderRadius: 12, fontWeight: 600 },
contained: {
boxShadow: '0 4px 14px 0 rgba(0,0,0,0.15)',
'&:hover': {
boxShadow: '0 6px 20px 0 rgba(0,0,0,0.25)',
transform: 'translateY(-1px)',
},
},
outlined: {
'&:hover': { boxShadow: '0 2px 8px 0 rgba(0,0,0,0.1)' },
},
},
},
MuiIconButton: {
styleOverrides: {
root: {
transition: 'all 0.2s ease',
'&:hover': { transform: 'scale(1.1)' },
},
},
},
},
}), }),
[controller.resolvedMode, controller.scheme], [controller.resolvedMode, controller.scheme],
) )
@@ -1,8 +1,6 @@
import { apiClient } from '@/shared/api/client' import { apiClient } from '@/shared/api/client'
import type { InfoPageBlock } from '../model/types' import type { InfoPageBlock } from '../model/types'
export async function fetchPublicInfoBlocks(): Promise<{ items: InfoPageBlock[] }> { export async function fetchPublicInfoBlocks(): Promise<{ items: InfoPageBlock[] }> {
const { data } = await apiClient.get<{ items: InfoPageBlock[] }>('info-page/blocks') const { data } = await apiClient.get<{ items: InfoPageBlock[] }>('info-page/blocks')
return data return data
@@ -1,7 +1,7 @@
import ShoppingCartRoundedIcon from '@mui/icons-material/ShoppingCartRounded'
import Badge from '@mui/material/Badge' import Badge from '@mui/material/Badge'
import IconButton from '@mui/material/IconButton' import IconButton from '@mui/material/IconButton'
import Tooltip from '@mui/material/Tooltip' import Tooltip from '@mui/material/Tooltip'
import { ShoppingCart } from 'lucide-react'
import type { AuthUser } from '@/shared/model/auth' import type { AuthUser } from '@/shared/model/auth'
type Props = { type Props = {
@@ -23,7 +23,7 @@ export function CartBadge({ user, cartCount, onNavigate }: Props) {
aria-label="Корзина" aria-label="Корзина"
> >
<Badge color="secondary" badgeContent={user ? cartCount : 0} invisible={!user || cartCount === 0}> <Badge color="secondary" badgeContent={user ? cartCount : 0} invisible={!user || cartCount === 0}>
<ShoppingCartRoundedIcon /> <ShoppingCart />
</Badge> </Badge>
</IconButton> </IconButton>
</Tooltip> </Tooltip>
@@ -1,10 +1,8 @@
import AddShoppingCartOutlinedIcon from '@mui/icons-material/AddShoppingCartOutlined'
import ShoppingCartOutlinedIcon from '@mui/icons-material/ShoppingCartOutlined'
import ShoppingCartRoundedIcon from '@mui/icons-material/ShoppingCartRounded'
import IconButton from '@mui/material/IconButton' import IconButton from '@mui/material/IconButton'
import Tooltip from '@mui/material/Tooltip' import Tooltip from '@mui/material/Tooltip'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { useUnit } from 'effector-react' import { useUnit } from 'effector-react'
import { ShoppingCart } from 'lucide-react'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import { addToCart, fetchMyCart, removeCartItem } from '@/entities/cart/api/cart-api' import { addToCart, fetchMyCart, removeCartItem } from '@/entities/cart/api/cart-api'
import { $user } from '@/shared/model/auth' import { $user } from '@/shared/model/auth'
@@ -65,7 +63,7 @@ export function ToggleCartIcon(props: {
<Tooltip title={tooltip}> <Tooltip title={tooltip}>
<span> <span>
<IconButton size={size} onClick={onClick} disabled={disabled || busy} aria-label={tooltip} type="button"> <IconButton size={size} onClick={onClick} disabled={disabled || busy} aria-label={tooltip} type="button">
{user ? inCart ? <ShoppingCartRoundedIcon /> : <AddShoppingCartOutlinedIcon /> : <ShoppingCartOutlinedIcon />} {user ? (inCart ? <ShoppingCart fill="currentColor" /> : <ShoppingCart />) : <ShoppingCart />}
</IconButton> </IconButton>
</span> </span>
</Tooltip> </Tooltip>
@@ -1,10 +1,10 @@
import { useState } from 'react' import { useState } from 'react'
import PersonOutlineRoundedIcon from '@mui/icons-material/PersonOutlineRounded'
import Badge from '@mui/material/Badge' import Badge from '@mui/material/Badge'
import IconButton from '@mui/material/IconButton' import IconButton from '@mui/material/IconButton'
import ListItemText from '@mui/material/ListItemText' import ListItemText from '@mui/material/ListItemText'
import Menu from '@mui/material/Menu' import Menu from '@mui/material/Menu'
import MenuItem from '@mui/material/MenuItem' import MenuItem from '@mui/material/MenuItem'
import { User } from 'lucide-react'
import type { AuthUser } from '@/shared/model/auth' import type { AuthUser } from '@/shared/model/auth'
type Props = { type Props = {
@@ -40,7 +40,7 @@ export function UserMenu({ user, onNavigate, onLogout }: Props) {
invisible={!user} invisible={!user}
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }} anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
> >
<PersonOutlineRoundedIcon /> <User />
</Badge> </Badge>
</IconButton> </IconButton>
@@ -1,12 +1,5 @@
import type { ReactNode } from 'react' import type { ReactNode } from 'react'
import { useMemo, useState } from 'react' import { useMemo, useState } from 'react'
import AdminPanelSettingsOutlinedIcon from '@mui/icons-material/AdminPanelSettingsOutlined'
import AssignmentOutlinedIcon from '@mui/icons-material/AssignmentOutlined'
import DescriptionOutlinedIcon from '@mui/icons-material/DescriptionOutlined'
import PeopleOutlinedIcon from '@mui/icons-material/PeopleOutlined'
import PhotoLibraryOutlinedIcon from '@mui/icons-material/PhotoLibraryOutlined'
import RateReviewOutlinedIcon from '@mui/icons-material/RateReviewOutlined'
import StorefrontOutlinedIcon from '@mui/icons-material/StorefrontOutlined'
import Badge from '@mui/material/Badge' import Badge from '@mui/material/Badge'
import Box from '@mui/material/Box' import Box from '@mui/material/Box'
import Divider from '@mui/material/Divider' import Divider from '@mui/material/Divider'
@@ -22,6 +15,7 @@ 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 { FileText, Image, LayoutGrid, ListOrdered, MessageSquare, Store, Users } from 'lucide-react'
import { Navigate, Route, Routes, useLocation, useNavigate } from 'react-router-dom' import { Navigate, Route, Routes, useLocation, useNavigate } from 'react-router-dom'
import { fetchAdminOrdersSummary } from '@/entities/order/api/admin-order-api' import { fetchAdminOrdersSummary } from '@/entities/order/api/admin-order-api'
import { AdminCategoriesPage } from '@/pages/admin-categories' import { AdminCategoriesPage } from '@/pages/admin-categories'
@@ -60,13 +54,13 @@ export function AdminLayoutPage() {
const navItems: NavItem[] = useMemo( const navItems: NavItem[] = useMemo(
() => [ () => [
{ to: '/admin', label: 'Товары', icon: <StorefrontOutlinedIcon /> }, { to: '/admin', label: 'Товары', icon: <Store /> },
{ to: '/admin/categories', label: 'Категории', icon: <AdminPanelSettingsOutlinedIcon /> }, { to: '/admin/categories', label: 'Категории', icon: <LayoutGrid /> },
{ to: '/admin/gallery', label: 'Галерея', icon: <PhotoLibraryOutlinedIcon /> }, { to: '/admin/gallery', label: 'Галерея', icon: <Image /> },
{ to: '/admin/orders', label: 'Заказы', icon: <AssignmentOutlinedIcon /> }, { to: '/admin/orders', label: 'Заказы', icon: <ListOrdered /> },
{ to: '/admin/reviews', label: 'Отзывы', icon: <RateReviewOutlinedIcon /> }, { to: '/admin/reviews', label: 'Отзывы', icon: <MessageSquare /> },
{ to: '/admin/users', label: 'Пользователи', icon: <PeopleOutlinedIcon /> }, { to: '/admin/users', label: 'Пользователи', icon: <Users /> },
{ to: '/admin/info', label: 'Инфо-страница', icon: <DescriptionOutlinedIcon /> }, { to: '/admin/info', label: 'Инфо-страница', icon: <FileText /> },
], ],
[], [],
) )
@@ -143,7 +137,7 @@ export function AdminLayoutPage() {
bgcolor: 'warning.50', bgcolor: 'warning.50',
}} }}
> >
<AdminPanelSettingsOutlinedIcon /> <LayoutGrid />
</IconButton> </IconButton>
<Typography variant="h5" sx={{ fontWeight: 700 }}> <Typography variant="h5" sx={{ fontWeight: 700 }}>
Админка Админка
+4 -6
View File
@@ -1,6 +1,3 @@
import AddIcon from '@mui/icons-material/Add'
import DeleteOutlineOutlinedIcon from '@mui/icons-material/DeleteOutlineOutlined'
import RemoveIcon from '@mui/icons-material/Remove'
import Alert from '@mui/material/Alert' import Alert from '@mui/material/Alert'
import Box from '@mui/material/Box' import Box from '@mui/material/Box'
import Button from '@mui/material/Button' import Button from '@mui/material/Button'
@@ -11,6 +8,7 @@ import Tooltip from '@mui/material/Tooltip'
import Typography from '@mui/material/Typography' import Typography from '@mui/material/Typography'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { useUnit } from 'effector-react' import { useUnit } from 'effector-react'
import { Minus, Plus, Trash2 } from 'lucide-react'
import { Link as RouterLink } from 'react-router-dom' import { Link as RouterLink } from 'react-router-dom'
import { fetchMyCart, removeCartItem, setCartQty } from '@/entities/cart/api/cart-api' import { fetchMyCart, removeCartItem, setCartQty } from '@/entities/cart/api/cart-api'
import { formatPriceRub } from '@/shared/lib/format-price' import { formatPriceRub } from '@/shared/lib/format-price'
@@ -115,7 +113,7 @@ export function CartPage() {
size="small" size="small"
sx={{ width: 32, height: 32 }} sx={{ width: 32, height: 32 }}
> >
<RemoveIcon /> <Minus />
</IconButton> </IconButton>
<Typography <Typography
sx={{ sx={{
@@ -137,7 +135,7 @@ export function CartPage() {
size="small" size="small"
sx={{ width: 32, height: 32 }} sx={{ width: 32, height: 32 }}
> >
<AddIcon /> <Plus />
</IconButton> </IconButton>
</span> </span>
</Tooltip> </Tooltip>
@@ -149,7 +147,7 @@ export function CartPage() {
size="small" size="small"
sx={{ width: 32, height: 32 }} sx={{ width: 32, height: 32 }}
> >
<DeleteOutlineOutlinedIcon /> <Trash2 />
</IconButton> </IconButton>
</Stack> </Stack>
</Stack> </Stack>
@@ -24,8 +24,8 @@ export function useProductFilters() {
return () => window.clearTimeout(t) return () => window.clearTimeout(t)
}, [qInput]) }, [qInput])
const handleCategoryChange = (e: SelectChangeEvent<string>) => { const handleCategoryChange = (slug: string) => {
setCategorySlug(e.target.value) setCategorySlug(slug)
setPage(1) setPage(1)
} }
+7 -5
View File
@@ -79,11 +79,13 @@ export function HomePage() {
Игрушки, сувениры и другие изделия ручной работы. Игрушки, сувениры и другие изделия ручной работы.
</Typography> </Typography>
<ProductFilters <Box sx={{ mb: 3 }}>
{...filters} <ProductFilters
categories={categoriesQuery.data ?? []} {...filters}
categoriesLoading={categoriesQuery.isLoading} categories={categoriesQuery.data ?? []}
/> categoriesLoading={categoriesQuery.isLoading}
/>
</Box>
{productsQuery.isLoading && ( {productsQuery.isLoading && (
<Grid container spacing={2} sx={{ mt: 2 }}> <Grid container spacing={2} sx={{ mt: 2 }}>
+125 -121
View File
@@ -1,9 +1,11 @@
import { useMemo } from 'react' import { useMemo } from 'react'
import Box from '@mui/material/Box' import Box from '@mui/material/Box'
import Button from '@mui/material/Button' import Button from '@mui/material/Button'
import Chip from '@mui/material/Chip'
import Collapse from '@mui/material/Collapse' import Collapse from '@mui/material/Collapse'
import Divider from '@mui/material/Divider' import Divider from '@mui/material/Divider'
import FormControl from '@mui/material/FormControl' import FormControl from '@mui/material/FormControl'
import InputAdornment from '@mui/material/InputAdornment'
import InputLabel from '@mui/material/InputLabel' import InputLabel from '@mui/material/InputLabel'
import MenuItem from '@mui/material/MenuItem' import MenuItem from '@mui/material/MenuItem'
import Paper from '@mui/material/Paper' import Paper from '@mui/material/Paper'
@@ -13,6 +15,7 @@ import TextField from '@mui/material/TextField'
import ToggleButton from '@mui/material/ToggleButton' import ToggleButton from '@mui/material/ToggleButton'
import ToggleButtonGroup from '@mui/material/ToggleButtonGroup' import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'
import Typography from '@mui/material/Typography' import Typography from '@mui/material/Typography'
import { Search, SlidersHorizontal } from 'lucide-react'
import type { Category } from '@/entities/product/model/types' import type { Category } from '@/entities/product/model/types'
import type { UseProductFiltersResult } from '../lib/use-product-filters' import type { UseProductFiltersResult } from '../lib/use-product-filters'
@@ -60,31 +63,20 @@ export function ProductFilters({
spacing={2} spacing={2}
sx={{ alignItems: { md: 'center' }, flexWrap: { md: 'wrap' } }} sx={{ alignItems: { md: 'center' }, flexWrap: { md: 'wrap' } }}
> >
<FormControl sx={{ minWidth: 220 }} size="small">
<InputLabel id="category-filter-label">Категория</InputLabel>
<Select<string>
labelId="category-filter-label"
label="Категория"
value={categorySlug}
onChange={handleCategoryChange}
disabled={categoriesLoading}
>
<MenuItem value="">
<em>Все</em>
</MenuItem>
{categoriesForFilter.map((c) => (
<MenuItem key={c.id} value={c.slug}>
{c.name}
</MenuItem>
))}
</Select>
</FormControl>
<TextField <TextField
size="small" size="small"
label="Поиск" placeholder="Поиск"
value={qInput} value={qInput}
onChange={(e) => setQInput(e.target.value)} onChange={(e) => setQInput(e.target.value)}
slotProps={{
input: {
startAdornment: (
<InputAdornment position="start">
<Search size={18} />
</InputAdornment>
),
},
}}
sx={{ flexGrow: 1, minWidth: { xs: '100%', md: 360 } }} sx={{ flexGrow: 1, minWidth: { xs: '100%', md: 360 } }}
/> />
</Stack> </Stack>
@@ -93,7 +85,7 @@ export function ProductFilters({
variant="outlined" variant="outlined"
sx={{ sx={{
p: 1.5, p: 1.5,
borderRadius: 2, borderRadius: 3,
bgcolor: 'background.paper', bgcolor: 'background.paper',
display: 'flex', display: 'flex',
flexDirection: { xs: 'column', sm: 'row' }, flexDirection: { xs: 'column', sm: 'row' },
@@ -102,11 +94,35 @@ export function ProductFilters({
justifyContent: 'space-between', justifyContent: 'space-between',
}} }}
> >
<Box> <Box
<Typography variant="subtitle2">Наличие</Typography> sx={{
<Typography variant="caption" color="text.secondary"> display: 'flex',
Быстрый фильтр по наличию flexDirection: { xs: 'column', sm: 'row' },
</Typography> alignItems: { sm: 'center' },
gap: 1.5,
flexGrow: 1,
}}
>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.75, alignItems: 'center' }}>
<Chip
label="Все"
size="small"
variant={categorySlug === '' ? 'filled' : 'outlined'}
color={categorySlug === '' ? 'primary' : 'default'}
onClick={() => handleCategoryChange('')}
/>
{categoriesForFilter.map((c) => (
<Chip
key={c.id}
label={c.name}
size="small"
variant={categorySlug === c.slug ? 'filled' : 'outlined'}
color={categorySlug === c.slug ? 'primary' : 'default'}
onClick={() => handleCategoryChange(c.slug)}
disabled={categoriesLoading}
/>
))}
</Box>
</Box> </Box>
<ToggleButtonGroup <ToggleButtonGroup
@@ -116,7 +132,7 @@ export function ProductFilters({
onChange={(_, v) => handleAvailabilityChange(v)} onChange={(_, v) => handleAvailabilityChange(v)}
sx={{ sx={{
alignSelf: { xs: 'flex-start', sm: 'auto' }, alignSelf: { xs: 'flex-start', sm: 'auto' },
'& .MuiToggleButton-root': { px: 2, fontWeight: 700, letterSpacing: 0.2, textTransform: 'none' }, '& .MuiToggleButton-root': { px: 1.5, fontWeight: 600, textTransform: 'none' },
'& .MuiToggleButton-root.Mui-selected': { '& .MuiToggleButton-root.Mui-selected': {
bgcolor: 'primary.main', bgcolor: 'primary.main',
color: 'primary.contrastText', color: 'primary.contrastText',
@@ -135,114 +151,102 @@ export function ProductFilters({
spacing={1.5} spacing={1.5}
sx={{ alignItems: { sm: 'center' }, justifyContent: 'space-between', flexWrap: 'wrap' }} sx={{ alignItems: { sm: 'center' }, justifyContent: 'space-between', flexWrap: 'wrap' }}
> >
<Button variant="text" onClick={() => setMoreOpen((v) => !v)} sx={{ alignSelf: { xs: 'flex-start' } }}>
{moreOpen ? 'Скрыть фильтры' : 'Фильтры и сортировка'}
</Button>
<Button <Button
variant="outlined" variant="text"
onClick={resetFilters} onClick={() => setMoreOpen((v) => !v)}
startIcon={<SlidersHorizontal size={18} />}
sx={{ alignSelf: { xs: 'flex-start' } }} sx={{ alignSelf: { xs: 'flex-start' } }}
> >
{moreOpen ? 'Скрыть' : 'Фильтры'}
</Button>
<Button variant="outlined" onClick={resetFilters} sx={{ alignSelf: { xs: 'flex-start' } }}>
Сбросить Сбросить
</Button> </Button>
</Stack> </Stack>
<Collapse in={moreOpen} unmountOnExit> <Collapse in={moreOpen} unmountOnExit>
<Stack <Paper variant="outlined" sx={{ p: 2, borderRadius: 3, display: 'flex', flexDirection: 'column', gap: 2 }}>
direction={{ xs: 'column', md: 'row' }} <Stack
spacing={2} direction={{ xs: 'column', md: 'row' }}
sx={{ mt: 2, alignItems: { md: 'center' }, flexWrap: { md: 'wrap' } }} spacing={2}
> sx={{ alignItems: { md: 'center' }, flexWrap: { md: 'wrap' } }}
<FormControl sx={{ minWidth: 220 }} size="small"> >
<InputLabel id="sort-label">Сортировка</InputLabel> <FormControl sx={{ minWidth: 200 }} size="small">
<Select<string> labelId="sort-label" label="Сортировка" value={sort} onChange={handleSortChange}> <InputLabel id="sort-label">Сортировка</InputLabel>
<MenuItem value=""> <Select<string> labelId="sort-label" label="Сортировка" value={sort} onChange={handleSortChange}>
<em>Сначала новые</em> <MenuItem value="">
</MenuItem> <em>Сначала новые</em>
<MenuItem value="price_asc">Цена: по возрастанию</MenuItem>
<MenuItem value="price_desc">Цена: по убыванию</MenuItem>
</Select>
</FormControl>
<TextField
size="small"
label="Цена от, ₽"
value={priceMinRub}
onChange={(e) => handlePriceMinChange(e.target.value)}
sx={{ width: { xs: '100%', md: 180 } }}
/>
<TextField
size="small"
label="Цена до, ₽"
value={priceMaxRub}
onChange={(e) => handlePriceMaxChange(e.target.value)}
sx={{ width: { xs: '100%', md: 180 } }}
/>
<FormControl sx={{ minWidth: 220 }} size="small">
<InputLabel id="page-size-label">На странице</InputLabel>
<Select<string>
labelId="page-size-label"
label="На странице"
value={String(pageSize)}
onChange={handlePageSizeChange}
>
{[6, 12, 18, 24].map((n) => (
<MenuItem key={n} value={String(n)}>
{n}
</MenuItem> </MenuItem>
))} <MenuItem value="price_asc">Цена: по возрастанию</MenuItem>
</Select> <MenuItem value="price_desc">Цена: по убыванию</MenuItem>
</FormControl> </Select>
</Stack> </FormControl>
<Divider sx={{ my: 2 }} /> <TextField
size="small"
label="Цена от, ₽"
value={priceMinRub}
onChange={(e) => handlePriceMinChange(e.target.value)}
sx={{ width: { xs: '100%', md: 160 } }}
/>
<TextField
size="small"
label="Цена до, ₽"
value={priceMaxRub}
onChange={(e) => handlePriceMaxChange(e.target.value)}
sx={{ width: { xs: '100%', md: 160 } }}
/>
<Paper <FormControl sx={{ minWidth: 160 }} size="small">
variant="outlined" <InputLabel id="page-size-label">На странице</InputLabel>
sx={{ <Select<string>
p: 1.5, labelId="page-size-label"
borderRadius: 2, label="На странице"
bgcolor: 'background.paper', value={String(pageSize)}
display: 'flex', onChange={handlePageSizeChange}
flexDirection: { xs: 'column', sm: 'row' }, >
gap: 1.5, {[6, 12, 18, 24].map((n) => (
alignItems: { sm: 'center' }, <MenuItem key={n} value={String(n)}>
justifyContent: 'space-between', {n}
}} </MenuItem>
> ))}
<Box> </Select>
<Typography variant="subtitle2">Масштаб карточек</Typography> </FormControl>
<Typography variant="caption" color="text.secondary"> </Stack>
Выберите размер карточек в каталоге
</Typography>
</Box>
<ToggleButtonGroup <Divider />
exclusive
size="small" <Box
value={cardScale}
onChange={(_, v) => handleCardScaleChange(v)}
sx={{ sx={{
alignSelf: { xs: 'flex-start', sm: 'auto' }, display: 'flex',
'& .MuiToggleButton-root': { flexDirection: { xs: 'column', sm: 'row' },
px: 2, gap: 1.5,
fontWeight: 700, alignItems: { sm: 'center' },
letterSpacing: 0.2, justifyContent: 'space-between',
textTransform: 'none',
},
'& .MuiToggleButton-root.Mui-selected': {
bgcolor: 'primary.main',
color: 'primary.contrastText',
'&:hover': { bgcolor: 'primary.dark' },
},
}} }}
> >
<ToggleButton value={70}>S</ToggleButton> <Typography variant="subtitle2">Масштаб карточек</Typography>
<ToggleButton value={90}>M</ToggleButton> <ToggleButtonGroup
<ToggleButton value={110}>L</ToggleButton> exclusive
<ToggleButton value={130}>XL</ToggleButton> size="small"
</ToggleButtonGroup> value={cardScale}
onChange={(_, v) => handleCardScaleChange(v)}
sx={{
alignSelf: { xs: 'flex-start', sm: 'auto' },
'& .MuiToggleButton-root': { px: 1.5, fontWeight: 600, textTransform: 'none' },
'& .MuiToggleButton-root.Mui-selected': {
bgcolor: 'primary.main',
color: 'primary.contrastText',
'&:hover': { bgcolor: 'primary.dark' },
},
}}
>
<ToggleButton value={70}>S</ToggleButton>
<ToggleButton value={90}>M</ToggleButton>
<ToggleButton value={110}>L</ToggleButton>
<ToggleButton value={130}>XL</ToggleButton>
</ToggleButtonGroup>
</Box>
</Paper> </Paper>
</Collapse> </Collapse>
</Stack> </Stack>
+6 -10
View File
@@ -1,10 +1,5 @@
import type { ReactNode } from 'react' import type { ReactNode } from 'react'
import { useMemo, useState } from 'react' import { useMemo, useState } from 'react'
import ChatOutlinedIcon from '@mui/icons-material/ChatOutlined'
import LocalShippingOutlinedIcon from '@mui/icons-material/LocalShippingOutlined'
import PlaceOutlinedIcon from '@mui/icons-material/PlaceOutlined'
import SettingsOutlinedIcon from '@mui/icons-material/SettingsOutlined'
import TuneOutlinedIcon from '@mui/icons-material/TuneOutlined'
import Alert from '@mui/material/Alert' import Alert from '@mui/material/Alert'
import Badge from '@mui/material/Badge' import Badge from '@mui/material/Badge'
import Box from '@mui/material/Box' import Box from '@mui/material/Box'
@@ -21,6 +16,7 @@ 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 { 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'
@@ -56,10 +52,10 @@ export function MeLayoutPage() {
const navItems: NavItem[] = useMemo( const navItems: NavItem[] = useMemo(
() => [ () => [
{ to: '/me/orders', label: 'Заказы', icon: <LocalShippingOutlinedIcon /> }, { to: '/me/orders', label: 'Заказы', icon: <Truck /> },
{ to: '/me/messages', label: 'Сообщения', icon: <ChatOutlinedIcon /> }, { to: '/me/messages', label: 'Сообщения', icon: <MessageCircle /> },
{ to: '/me/settings', label: 'Настройки', icon: <SettingsOutlinedIcon /> }, { to: '/me/settings', label: 'Настройки', icon: <Settings /> },
{ to: '/me/addresses', label: 'Адреса доставки', icon: <PlaceOutlinedIcon /> }, { to: '/me/addresses', label: 'Адреса доставки', icon: <MapPin /> },
], ],
[], [],
) )
@@ -143,7 +139,7 @@ export function MeLayoutPage() {
bgcolor: 'background.paper', bgcolor: 'background.paper',
}} }}
> >
<TuneOutlinedIcon /> <SlidersHorizontal />
</IconButton> </IconButton>
<Typography variant="h5" sx={{ fontWeight: 700 }}> <Typography variant="h5" sx={{ fontWeight: 700 }}>
Профиль Профиль
+6 -7
View File
@@ -1,6 +1,4 @@
import { useMemo, useState } from 'react' import { useMemo, useState } from 'react'
import CloseIcon from '@mui/icons-material/Close'
import StarRoundedIcon from '@mui/icons-material/StarRounded'
import Alert from '@mui/material/Alert' import Alert from '@mui/material/Alert'
import Box from '@mui/material/Box' import Box from '@mui/material/Box'
import Chip from '@mui/material/Chip' import Chip from '@mui/material/Chip'
@@ -14,6 +12,7 @@ import Stack from '@mui/material/Stack'
import Typography from '@mui/material/Typography' import Typography from '@mui/material/Typography'
import { useQuery } from '@tanstack/react-query' import { useQuery } from '@tanstack/react-query'
import { useUnit } from 'effector-react' import { useUnit } from 'effector-react'
import { Star, X } from 'lucide-react'
import { useParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
import { Navigation } from 'swiper/modules' import { Navigation } from 'swiper/modules'
import { Swiper, SwiperSlide } from 'swiper/react' import { Swiper, SwiperSlide } from 'swiper/react'
@@ -180,8 +179,8 @@ export function ProductPage() {
value={p.reviewsSummary.avgRating ?? 0} value={p.reviewsSummary.avgRating ?? 0}
readOnly readOnly
precision={0.25} precision={0.25}
icon={<StarRoundedIcon fontSize="inherit" />} icon={<Star fontSize="inherit" />}
emptyIcon={<StarRoundedIcon fontSize="inherit" />} emptyIcon={<Star fontSize="inherit" />}
/> />
<Typography variant="body2" color="text.secondary"> <Typography variant="body2" color="text.secondary">
{reviewsCountRu(p.reviewsSummary.approvedReviewCount)} {reviewsCountRu(p.reviewsSummary.approvedReviewCount)}
@@ -211,8 +210,8 @@ export function ProductPage() {
value={rv.rating} value={rv.rating}
readOnly readOnly
size="small" size="small"
icon={<StarRoundedIcon fontSize="inherit" />} icon={<Star fontSize="inherit" />}
emptyIcon={<StarRoundedIcon fontSize="inherit" />} emptyIcon={<Star fontSize="inherit" />}
/> />
{body ? ( {body ? (
<Box sx={{ color: 'text.secondary' }}> <Box sx={{ color: 'text.secondary' }}>
@@ -259,7 +258,7 @@ export function ProductPage() {
sx={{ position: 'absolute', top: 12, right: 12, zIndex: 2, color: 'white' }} sx={{ position: 'absolute', top: 12, right: 12, zIndex: 2, color: 'white' }}
aria-label="Закрыть" aria-label="Закрыть"
> >
<CloseIcon /> <X />
</IconButton> </IconButton>
<Swiper <Swiper
+1 -1
View File
@@ -15,4 +15,4 @@ export const STORE_PUBLIC_SITE_URL = (() => {
/** Демо-контакты для футера; при необходимости задайте через VITE_* в `.env`. */ /** Демо-контакты для футера; при необходимости задайте через VITE_* в `.env`. */
export const STORE_EMAIL = import.meta.env.VITE_STORE_EMAIL ?? 'hello@example.com' export const STORE_EMAIL = import.meta.env.VITE_STORE_EMAIL ?? 'hello@example.com'
export const STORE_PHONE = import.meta.env.VITE_STORE_PHONE ?? '+7 (900) 000-00-00' export const STORE_PHONE = import.meta.env.VITE_STORE_PHONE ?? '+7 (900) 000-00-00'
export const STORE_SOCIAL_NOTE = import.meta.env.VITE_STORE_SOCIAL_NOTE ?? 'Соцсети: укажите ссылки при публикации' export const VK_URL = import.meta.env.VITE_VK_URL ?? '#'
@@ -1,7 +1,6 @@
import DarkModeOutlinedIcon from '@mui/icons-material/DarkModeOutlined'
import LightModeOutlinedIcon from '@mui/icons-material/LightModeOutlined'
import IconButton from '@mui/material/IconButton' import IconButton from '@mui/material/IconButton'
import Tooltip from '@mui/material/Tooltip' import Tooltip from '@mui/material/Tooltip'
import { Monitor, Moon, Sun } from 'lucide-react'
import type { ThemeModePreference } from '@/shared/model/theme' import type { ThemeModePreference } from '@/shared/model/theme'
type Props = { type Props = {
@@ -22,10 +21,12 @@ function getModeLabel(mode: ThemeModePreference, resolvedMode: 'light' | 'dark')
} }
export function ModeSwitcher({ mode, resolvedMode, onCycleMode }: Props) { export function ModeSwitcher({ mode, resolvedMode, onCycleMode }: Props) {
const icon = mode === 'system' ? <Monitor /> : resolvedMode === 'dark' ? <Sun /> : <Moon />
return ( return (
<Tooltip title={`Тема: ${getModeLabel(mode, resolvedMode)}`}> <Tooltip title={`Тема: ${getModeLabel(mode, resolvedMode)}`}>
<IconButton color="inherit" onClick={onCycleMode} aria-label="Переключить тему"> <IconButton color="inherit" onClick={onCycleMode} aria-label="Переключить тему">
{resolvedMode === 'dark' ? <LightModeOutlinedIcon /> : <DarkModeOutlinedIcon />} {icon}
</IconButton> </IconButton>
</Tooltip> </Tooltip>
) )
@@ -1,13 +1,11 @@
import { useEffect } from 'react' import { useEffect } from 'react'
import FormatBoldOutlinedIcon from '@mui/icons-material/FormatBoldOutlined'
import FormatItalicOutlinedIcon from '@mui/icons-material/FormatItalicOutlined'
import FormatListBulletedOutlinedIcon from '@mui/icons-material/FormatListBulletedOutlined'
import Box from '@mui/material/Box' import Box from '@mui/material/Box'
import IconButton from '@mui/material/IconButton' import IconButton from '@mui/material/IconButton'
import Stack from '@mui/material/Stack' import Stack from '@mui/material/Stack'
import Placeholder from '@tiptap/extension-placeholder' import Placeholder from '@tiptap/extension-placeholder'
import { EditorContent, useEditor } from '@tiptap/react' import { EditorContent, useEditor } from '@tiptap/react'
import TiptapStarterKit from '@tiptap/starter-kit' import TiptapStarterKit from '@tiptap/starter-kit'
import { Bold, Italic, List } from 'lucide-react'
type RichTextMessageEditorProps = { type RichTextMessageEditorProps = {
value: string value: string
@@ -59,7 +57,7 @@ export function RichTextMessageEditor({
disabled={disabled} disabled={disabled}
aria-label="Жирный" aria-label="Жирный"
> >
<FormatBoldOutlinedIcon fontSize="small" /> <Bold size={18} />
</IconButton> </IconButton>
<IconButton <IconButton
size="small" size="small"
@@ -68,7 +66,7 @@ export function RichTextMessageEditor({
disabled={disabled} disabled={disabled}
aria-label="Курсив" aria-label="Курсив"
> >
<FormatItalicOutlinedIcon fontSize="small" /> <Italic size={18} />
</IconButton> </IconButton>
<IconButton <IconButton
size="small" size="small"
@@ -77,7 +75,7 @@ export function RichTextMessageEditor({
disabled={disabled} disabled={disabled}
aria-label="Список" aria-label="Список"
> >
<FormatListBulletedOutlinedIcon fontSize="small" /> <List size={18} />
</IconButton> </IconButton>
</Stack> </Stack>
@@ -1,4 +1,3 @@
import CheckCircleRoundedIcon from '@mui/icons-material/CheckCircleRounded'
import Box from '@mui/material/Box' import Box from '@mui/material/Box'
import IconButton from '@mui/material/IconButton' import IconButton from '@mui/material/IconButton'
import { Cherry, Hammer, Trees, WavesHorizontal } from 'lucide-react' import { Cherry, Hammer, Trees, WavesHorizontal } from 'lucide-react'
@@ -11,10 +10,10 @@ type Props = {
} }
const SCHEMES: { key: ColorScheme; color: string; label: string; icon: React.ReactNode }[] = [ const SCHEMES: { key: ColorScheme; color: string; label: string; icon: React.ReactNode }[] = [
{ key: 'craft', color: '#6D4C41', label: 'Крафт', icon: <Hammer size={14} /> }, { key: 'craft', color: '#6D4C41', label: 'Крафт', icon: <Hammer size={16} /> },
{ key: 'forest', color: '#2E7D32', label: 'Лес', icon: <Trees size={14} /> }, { key: 'forest', color: '#2E7D32', label: 'Лес', icon: <Trees size={16} /> },
{ key: 'ocean', color: '#1565C0', label: 'Океан', icon: <WavesHorizontal size={14} /> }, { key: 'ocean', color: '#1565C0', label: 'Океан', icon: <WavesHorizontal size={16} /> },
{ key: 'berry', color: '#7B1FA2', label: 'Ягоды', icon: <Cherry size={14} /> }, { key: 'berry', color: '#7B1FA2', label: 'Ягоды', icon: <Cherry size={16} /> },
] ]
export function SchemeSwitcher({ value, onChange, orientation = 'horizontal' }: Props) { export function SchemeSwitcher({ value, onChange, orientation = 'horizontal' }: Props) {
@@ -23,7 +22,7 @@ export function SchemeSwitcher({ value, onChange, orientation = 'horizontal' }:
sx={{ sx={{
display: 'flex', display: 'flex',
flexDirection: orientation === 'vertical' ? 'column' : 'row', flexDirection: orientation === 'vertical' ? 'column' : 'row',
gap: 0.5, gap: 1,
alignItems: 'center', alignItems: 'center',
}} }}
> >
@@ -36,23 +35,24 @@ export function SchemeSwitcher({ value, onChange, orientation = 'horizontal' }:
size="small" size="small"
title={s.label} title={s.label}
sx={{ sx={{
width: 30, width: 36,
height: 30, height: 36,
minWidth: 30, minWidth: 36,
bgcolor: s.color, bgcolor: 'transparent',
border: 2, border: 2,
borderColor: active ? 'common.white' : 'rgba(255,255,255,0.4)', borderColor: active ? s.color : 'transparent',
boxShadow: active ? `0 0 0 1.5px ${s.color}, 0 0 8px ${s.color}99` : 'none', boxShadow: active ? `0 0 8px ${s.color}66` : 'none',
transform: active ? 'scale(1.1)' : 'scale(1)', transform: active ? 'scale(1.15)' : 'scale(1)',
color: 'common.white', color: active ? s.color : 'text.secondary',
transition: 'all 0.2s ease', transition: 'all 0.2s ease',
'&:hover': { '&:hover': {
transform: 'scale(1.2)', transform: 'scale(1.2)',
bgcolor: s.color, borderColor: s.color,
color: s.color,
}, },
}} }}
> >
{active ? <CheckCircleRoundedIcon sx={{ fontSize: 14 }} /> : s.icon} {s.icon}
</IconButton> </IconButton>
) )
})} })}
@@ -80,24 +80,22 @@ function CatalogSliderInner({ slides }: { slides: CatalogSliderSlide[] }) {
<Box <Box
sx={{ sx={{
position: 'absolute', position: 'absolute',
bottom: 0,
left: 0, left: 0,
right: 0, right: 0,
top: slides.length > 1 ? 20 : 0,
zIndex: 4, zIndex: 4,
pt: 4, background: 'linear-gradient(transparent, rgba(0,0,0,0.75))',
pb: slides.length > 1 ? 2 : 2, px: 3,
WebkitTextStroke: '0.5px white', py: 2.5,
// background: 'linear-gradient(to top, rgba(0,0,0,0.82) 0%, rgba(0,0,0,0.4) 50%, transparent 100%)',
}} }}
> >
<Typography <Typography
color="common.white" color="common.white"
variant="subtitle1"
sx={{ sx={{
fontWeight: 700, fontWeight: 800,
textShadow: '0 1px 4px rgba(0,0,0,0.75)',
textAlign: 'center', textAlign: 'center',
fontSize: { xs: '1.25rem', sm: '1.5rem' }, fontSize: { xs: '1.5rem', sm: '2rem', md: '2.5rem' },
lineHeight: 1.2,
}} }}
> >
{captionText} {captionText}
@@ -92,8 +92,8 @@ export function NavigationDrawer({
<Divider sx={{ my: 2 }} /> <Divider sx={{ my: 2 }} />
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, alignItems: 'flex-start' }}> <Box sx={{ display: 'flex', flexDirection: 'row', gap: 2, alignItems: 'center', flexWrap: 'wrap' }}>
<SchemeSwitcher value={scheme} onChange={onSchemeChange} orientation="vertical" /> <SchemeSwitcher value={scheme} onChange={onSchemeChange} orientation="horizontal" />
<ModeSwitcher mode={mode} resolvedMode={resolvedMode} onCycleMode={onCycleMode} /> <ModeSwitcher mode={mode} resolvedMode={resolvedMode} onCycleMode={onCycleMode} />
</Box> </Box>
</Box> </Box>
@@ -0,0 +1,45 @@
# UI Style Refresh — Implementation Plan
**Goal:** Замена иконок MUI→Lucide, редизайн темы/слайдера/фильтров/кнопок, VK в футер.
**Architecture:** 6 независимых этапов: подготовка (package.json, config), замена иконок в ~12 файлах, визуальный редизайн 3 компонентов.
**Tech Stack:** React, MUI, Lucide React
---
### Task A1: package.json + shared/config
- Удалить `@mui/icons-material` из package.json
- Заменить `STORE_SOCIAL_NOTE` на `VK_URL` в config
### Task A2: MUI-тема (AppProviders)
- Добавить `components.MuiButton` styleOverrides (тени, hover)
- Добавить `components.MuiIconButton` styleOverrides (scale)
### Task B3: Хедер + меню
- `AppHeader.tsx``Menu`, `Package` (lucide)
- `CartBadge.tsx``ShoppingCart` (lucide)
- `UserMenu.tsx``User` (lucide)
- `ToggleCartIcon.tsx``ShoppingCart`, `ShoppingCartOff` (lucide)
### Task B4: Админка
- `AdminLayoutPage.tsx``Store`, `LayoutGrid`, `Image`, `ListOrdered`, `MessageSquare`, `People`, `FileText` (lucide)
### Task B5: Личный кабинет
- `MeLayoutPage.tsx``Truck`, `MessageCircle`, `Settings`, `MapPin`, `SlidersHorizontal` (lucide)
### Task B6: Остальные иконки
- `ProductPage.tsx``Star`, `X` (lucide)
- `CartPage.tsx``Plus`, `Minus`, `Trash2` (lucide)
- `ModeSwitcher.tsx``Sun`, `Moon`, `Monitor` (lucide, 3 режима)
- `SchemeSwitcher.tsx` — без bgcolor, цветная обводка
- `RichTextMessageEditor.tsx``Bold`, `Italic`, `List` (lucide)
### Task C7: VK в футер
- `MainLayout.tsx` — кастомный SvgIcon VK, удалить SOCIAL_NOTE
### Task C8: ProductFilters (каталог)
- Search с иконкой, Chips категорий, компактные фильтры
### Task C9: CatalogSlider (слайдер)
- Подложка с градиентом, новый стиль текста
@@ -0,0 +1,55 @@
# UI Style Refresh
**Дата:** 2026-05-14
**Статус:** Утверждён
## Мотивация
Освежить визуальный стиль сайта: заменить иконки на Lucide, переработать тему/переключатели схем, добавить VK в футер, обновить стиль каталога, слайдера и кнопок.
## 1. Иконки: MUI → Lucide React (везде)
Удалить зависимость `@mui/icons-material` из `client/package.json`. Все иконки MUI заменить на Lucide React.
## 2. Theme/SchemeSwitcher
Горизонтальное расположение. ModeSwitcher: `Sun`/`Moon`/`Monitor`. SchemeSwitcher: без bgcolor, цветная обводка у активной.
## 3. VK в футер
Кастомный SVG-логотип VK (`SvgIcon`), ссылка `href={VITE_VK_URL}`, удалить `STORE_SOCIAL_NOTE`.
## 4. ProductFilters (каталог)
Визуальный редизайн: Search с иконкой, Chips категорий, компактные фильтры, Material 3 стиль.
## 5. CatalogSlider (слайдер)
Подложка `rgba(0,0,0,0.7)` под текст, новый размер/вес шрифта, убрать WebkitTextStroke.
## 6. Кнопки (MUI-тема)
MuiButton: тени, градиент, hover-эффект. MuiIconButton: scale на hover.
## Файлы для изменения
| Файл | Изменение |
|---|---|
| `client/package.json` | Удалить `@mui/icons-material` |
| `client/src/app/providers/AppProviders.tsx` | MuiButton/MuiIconButton styleOverrides |
| `client/src/shared/config/index.ts` | Добавить VK_URL, удалить SOCIAL_NOTE |
| `client/src/app/layout/AppHeader.tsx` | MUI→Lucide иконки |
| `client/src/app/layout/MainLayout.tsx` | VK иконка, удалить SOCIAL_NOTE |
| `client/src/shared/ui/ModeSwitcher/ModeSwitcher.tsx` | Lucide + 3 режима |
| `client/src/shared/ui/SchemeSwitcher/SchemeSwitcher.tsx` | Убрать bgcolor, обводка |
| `client/src/features/cart/cart-badge/ui/CartBadge.tsx` | Lucide `ShoppingCart` |
| `client/src/features/cart/toggle-cart-icon/ui/ToggleCartIcon.tsx` | Lucide иконки |
| `client/src/features/user/user-menu/ui/UserMenu.tsx` | Lucide `User` |
| `client/src/widgets/navigation-drawer/ui/NavigationDrawer.tsx` | Lucide иконки |
| `client/src/widgets/catalog-slider/ui/CatalogSlider.tsx` | Подложка текста |
| `client/src/pages/home/ui/ProductFilters.tsx` | Визуальный редизайн |
| `client/src/pages/admin-layout/ui/AdminLayoutPage.tsx` | Lucide иконки |
| `client/src/pages/me/ui/MeLayoutPage.tsx` | Lucide иконки |
| `client/src/pages/products/ui/ProductPage.tsx` | Lucide `Star`, `X` |
| `client/src/pages/cart/ui/CartPage.tsx` | Lucide `Trash2`, `Plus`, `Minus` |
| `client/src/shared/ui/RichTextMessageEditor.tsx` | Lucide `Bold`, `Italic`, `List` |