diff --git a/client/src/app/App.tsx b/client/src/app/App.tsx
index e54e482..64654c5 100644
--- a/client/src/app/App.tsx
+++ b/client/src/app/App.tsx
@@ -1,7 +1,6 @@
import { BrowserRouter } from 'react-router-dom'
import { AppProviders } from '@/app/providers/AppProviders'
import { AppRoutes } from '@/app/routes'
-import { CartSnackbar } from '@/shared/ui/CartSnackbar'
import { NotificationStack } from '@/shared/ui/NotificationStack'
import { ErrorBoundary } from '@/shared/ui/ErrorBoundary'
import { NoiseOverlay } from '@/shared/ui/NoiseOverlay'
@@ -13,7 +12,6 @@ export function App() {
-
diff --git a/client/src/features/cart/add-to-cart/ui/AddToCartButton.tsx b/client/src/features/cart/add-to-cart/ui/AddToCartButton.tsx
index a799881..d68cf17 100644
--- a/client/src/features/cart/add-to-cart/ui/AddToCartButton.tsx
+++ b/client/src/features/cart/add-to-cart/ui/AddToCartButton.tsx
@@ -4,7 +4,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'
import { useUnit } from 'effector-react'
import { addToCart } from '@/entities/cart/api/cart-api'
import { $user } from '@/shared/model/auth'
-import { cartAdded } from '@/shared/model/cart-notifications'
+import { addNotification } from '@/shared/model/notification'
type Props = {
productId: string
@@ -21,7 +21,7 @@ export function AddToCartButton(props: Props) {
mutationFn: () => addToCart({ productId, qty }),
onSuccess: () => {
void qc.invalidateQueries({ queryKey: ['me', 'cart'] })
- cartAdded()
+ addNotification({ type: 'info', message: 'Товар добавлен в корзину' })
},
})
diff --git a/client/src/features/cart/add-to-cart/ui/__tests__/AddToCartButton.test.tsx b/client/src/features/cart/add-to-cart/ui/__tests__/AddToCartButton.test.tsx
index ee2050d..c1f16e2 100644
--- a/client/src/features/cart/add-to-cart/ui/__tests__/AddToCartButton.test.tsx
+++ b/client/src/features/cart/add-to-cart/ui/__tests__/AddToCartButton.test.tsx
@@ -1,7 +1,7 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { render, screen, fireEvent } from '@testing-library/react'
import { describe, it, expect, vi, beforeEach } from 'vitest'
-import * as notifications from '@/shared/model/cart-notifications'
+import * as notifications from '@/shared/model/notification'
import { AddToCartButton } from '../AddToCartButton'
vi.mock('@/entities/cart/api/cart-api', () => ({
@@ -21,8 +21,8 @@ describe('AddToCartButton', () => {
qc.clear()
})
- it('calls cartAdded after successful add', async () => {
- const spy = vi.spyOn(notifications, 'cartAdded')
+ it('calls addNotification after successful add', async () => {
+ const spy = vi.spyOn(notifications, 'addNotification')
render(
@@ -32,7 +32,7 @@ describe('AddToCartButton', () => {
fireEvent.click(screen.getByRole('button', { name: /в корзину/i }))
await vi.waitFor(() => {
- expect(spy).toHaveBeenCalled()
+ expect(spy).toHaveBeenCalledWith({ type: 'info', message: 'Товар добавлен в корзину' })
})
})
})
diff --git a/client/src/features/cart/toggle-cart-icon/ui/ToggleCartIcon.tsx b/client/src/features/cart/toggle-cart-icon/ui/ToggleCartIcon.tsx
index af6ef01..fe51801 100644
--- a/client/src/features/cart/toggle-cart-icon/ui/ToggleCartIcon.tsx
+++ b/client/src/features/cart/toggle-cart-icon/ui/ToggleCartIcon.tsx
@@ -6,7 +6,7 @@ import { ShoppingCart } from 'lucide-react'
import { useNavigate } from 'react-router-dom'
import { addToCart, fetchMyCart, removeCartItem } from '@/entities/cart/api/cart-api'
import { $user } from '@/shared/model/auth'
-import { cartAdded } from '@/shared/model/cart-notifications'
+import { addNotification } from '@/shared/model/notification'
export function ToggleCartIcon(props: {
productId: string
@@ -31,7 +31,7 @@ export function ToggleCartIcon(props: {
mutationFn: () => addToCart({ productId, qty: 1 }),
onSuccess: () => {
void qc.invalidateQueries({ queryKey: ['me', 'cart'] })
- cartAdded()
+ addNotification({ type: 'info', message: 'Товар добавлен в корзину' })
},
})
diff --git a/client/src/features/cart/toggle-cart-icon/ui/__tests__/ToggleCartIcon.test.tsx b/client/src/features/cart/toggle-cart-icon/ui/__tests__/ToggleCartIcon.test.tsx
index d90671b..225e153 100644
--- a/client/src/features/cart/toggle-cart-icon/ui/__tests__/ToggleCartIcon.test.tsx
+++ b/client/src/features/cart/toggle-cart-icon/ui/__tests__/ToggleCartIcon.test.tsx
@@ -3,7 +3,7 @@ import { render, screen, fireEvent } from '@testing-library/react'
import { MemoryRouter } from 'react-router-dom'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import * as api from '@/entities/cart/api/cart-api'
-import * as notifications from '@/shared/model/cart-notifications'
+import * as notifications from '@/shared/model/notification'
import { ToggleCartIcon } from '../ToggleCartIcon'
vi.mock('@/entities/cart/api/cart-api', () => ({
@@ -25,8 +25,8 @@ describe('ToggleCartIcon', () => {
qc.clear()
})
- it('calls cartAdded after successful add', async () => {
- const spy = vi.spyOn(notifications, 'cartAdded')
+ it('calls addNotification after successful add', async () => {
+ const spy = vi.spyOn(notifications, 'addNotification')
render(
@@ -38,15 +38,15 @@ describe('ToggleCartIcon', () => {
fireEvent.click(screen.getByRole('button', { name: /в корзину/i }))
await vi.waitFor(() => {
- expect(spy).toHaveBeenCalled()
+ expect(spy).toHaveBeenCalledWith({ type: 'info', message: 'Товар добавлен в корзину' })
})
})
- it('does not call cartAdded on remove', async () => {
+ it('does not call addNotification on remove', async () => {
vi.mocked(api.fetchMyCart).mockResolvedValueOnce({
items: [{ id: 'cart-1', qty: 1, product: { id: 'test-product' } as never }],
})
- const spy = vi.spyOn(notifications, 'cartAdded')
+ const spy = vi.spyOn(notifications, 'addNotification')
render(
diff --git a/client/src/shared/model/__tests__/cart-notifications.test.ts b/client/src/shared/model/__tests__/cart-notifications.test.ts
deleted file mode 100644
index 1c8f1c9..0000000
--- a/client/src/shared/model/__tests__/cart-notifications.test.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { allSettled, fork } from 'effector'
-import { describe, it, expect } from 'vitest'
-import { $cartSnackOpen, cartAdded, cartDismissed } from '../cart-notifications'
-
-describe('cart-notifications store', () => {
- it('opens on cartAdded', async () => {
- const scope = fork()
- await allSettled(cartAdded, { scope })
- expect(scope.getState($cartSnackOpen)).toBe(true)
- })
-
- it('closes on cartDismissed', async () => {
- const scope = fork()
- await allSettled(cartAdded, { scope })
- await allSettled(cartDismissed, { scope })
- expect(scope.getState($cartSnackOpen)).toBe(false)
- })
-
- it('starts closed by default', () => {
- const scope = fork()
- expect(scope.getState($cartSnackOpen)).toBe(false)
- })
-})
diff --git a/client/src/shared/model/cart-notifications.ts b/client/src/shared/model/cart-notifications.ts
deleted file mode 100644
index 64f48f0..0000000
--- a/client/src/shared/model/cart-notifications.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { createEvent, createStore } from 'effector'
-
-export const cartAdded = createEvent()
-export const cartDismissed = createEvent()
-
-export const $cartSnackOpen = createStore(false)
- .on(cartAdded, () => true)
- .on(cartDismissed, () => false)
diff --git a/client/src/shared/ui/CartSnackbar.tsx b/client/src/shared/ui/CartSnackbar.tsx
deleted file mode 100644
index 27b673f..0000000
--- a/client/src/shared/ui/CartSnackbar.tsx
+++ /dev/null
@@ -1,90 +0,0 @@
-import Alert from '@mui/material/Alert'
-import Button from '@mui/material/Button'
-import Snackbar from '@mui/material/Snackbar'
-import { useUnit } from 'effector-react'
-import { useNavigate } from 'react-router-dom'
-import { $cartSnackOpen, cartDismissed } from '@/shared/model/cart-notifications'
-
-export function CartSnackbar() {
- const open = useUnit($cartSnackOpen)
- const navigate = useNavigate()
-
- const handleClose = (_event: React.SyntheticEvent | Event, reason?: string) => {
- if (reason === 'clickaway') return
- cartDismissed()
- }
-
- const handleGoToCart = () => {
- cartDismissed()
- navigate('/cart')
- }
-
- return (
-
-
- Перейти в корзину
-
- }
- >
- Товар добавлен в корзину
-
-
- )
-}
diff --git a/client/src/shared/ui/__tests__/CartSnackbar.test.tsx b/client/src/shared/ui/__tests__/CartSnackbar.test.tsx
deleted file mode 100644
index b163e27..0000000
--- a/client/src/shared/ui/__tests__/CartSnackbar.test.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-import { render, screen, fireEvent, act } from '@testing-library/react'
-import { MemoryRouter } from 'react-router-dom'
-import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
-import { cartAdded, cartDismissed } from '@/shared/model/cart-notifications'
-import { CartSnackbar } from '@/shared/ui/CartSnackbar'
-
-const navigateMock = vi.fn()
-
-vi.mock('react-router-dom', async (importOriginal) => {
- const mod = await importOriginal()
- return { ...mod, useNavigate: () => navigateMock }
-})
-
-beforeEach(() => {
- navigateMock.mockClear()
-})
-
-afterEach(() => {
- vi.useRealTimers()
-})
-
-function renderWithRouter() {
- render(
-
-
- ,
- )
-}
-
-describe('CartSnackbar', () => {
- it('is hidden when store is false', () => {
- cartDismissed()
- renderWithRouter()
- expect(screen.queryByText(/товар добавлен/i)).not.toBeInTheDocument()
- })
-
- it('shows snackbar when cartAdded is fired', () => {
- renderWithRouter()
- cartAdded()
- expect(screen.getByText(/товар добавлен/i)).toBeInTheDocument()
- expect(screen.getByRole('button', { name: /перейти в корзину/i })).toBeInTheDocument()
- })
-
- it('closes on dismiss button click', () => {
- renderWithRouter()
- cartAdded()
- const closeBtn = screen.getByLabelText(/закрыть/i)
- fireEvent.click(closeBtn)
- expect(screen.queryByText(/товар добавлен/i)).not.toBeInTheDocument()
- })
-
- it('auto-closes after 4 seconds', () => {
- vi.useFakeTimers()
- renderWithRouter()
- cartAdded()
- act(() => {
- vi.advanceTimersByTime(4000)
- })
- expect(screen.queryByText(/товар добавлен/i)).not.toBeInTheDocument()
- })
-
- it('navigates to /cart and closes on "Перейти в корзину" click', () => {
- renderWithRouter()
- cartAdded()
- fireEvent.click(screen.getByRole('button', { name: /перейти в корзину/i }))
- expect(navigateMock).toHaveBeenCalledWith('/cart')
- expect(screen.queryByText(/товар добавлен/i)).not.toBeInTheDocument()
- })
-})