docs: cart added snackbar design spec
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
# Cart Added Snackbar — Design Spec
|
||||
|
||||
**Date:** 2026-05-25
|
||||
|
||||
## Goal
|
||||
|
||||
При добавлении товара в корзину показывать глобальное уведомление (Snackbar) с кнопкой «Перейти в корзину».
|
||||
|
||||
## Scope
|
||||
|
||||
- `AddToCartButton` (каталог, карточки товаров)
|
||||
- `ToggleCartIcon` (страница товара, toggle add/remove)
|
||||
- Глобальный Snackbar, рендерится один раз
|
||||
|
||||
## Architecture
|
||||
|
||||
### 1. Effector store — `shared/model/cart-notifications.ts`
|
||||
|
||||
Минимальный стор: только булев флаг открытия.
|
||||
|
||||
```ts
|
||||
import { createEvent, createStore } from 'effector'
|
||||
|
||||
export const cartAdded = createEvent()
|
||||
export const cartDismissed = createEvent()
|
||||
|
||||
export const $cartSnackOpen = createStore(false)
|
||||
.on(cartAdded, () => true)
|
||||
.on(cartDismissed, () => false)
|
||||
```
|
||||
|
||||
### 2. UI Component — `shared/ui/CartSnackbar.tsx`
|
||||
|
||||
Компонент подписывается на `$cartSnackOpen` через `useUnit`.
|
||||
|
||||
- **Текст:** «Товар добавлен в корзину»
|
||||
- **Кнопка действия:** «Перейти в корзину» → `navigate('/cart')` + `cartDismissed()`
|
||||
- **Закрытие (крестик):** `cartDismissed()`
|
||||
- **Авто-закрытие:** 4 секунды через `setTimeout`
|
||||
- **Позиция:** `anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}`
|
||||
- **Стиль:** MUI `<Snackbar>` + `<Alert severity="success">`
|
||||
|
||||
Если пользователь быстро добавит несколько товаров — каждый `cartAdded()` перезапускает таймер. Очередь не нужна.
|
||||
|
||||
### 3. Интеграция в `AddToCartButton`
|
||||
|
||||
В `onSuccess` мутации добавляется `cartAdded()`:
|
||||
|
||||
```ts
|
||||
const addMut = useMutation({
|
||||
mutationFn: () => addToCart({ productId, qty }),
|
||||
onSuccess: () => {
|
||||
void qc.invalidateQueries({ queryKey: ['me', 'cart'] })
|
||||
cartAdded()
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
`productTitle` не передаётся — текст уведомления универсальный.
|
||||
|
||||
### 4. Интеграция в `ToggleCartIcon`
|
||||
|
||||
Только add-мутация:
|
||||
|
||||
```ts
|
||||
const addMut = useMutation({
|
||||
mutationFn: () => addToCart({ productId, qty: 1 }),
|
||||
onSuccess: () => {
|
||||
void qc.invalidateQueries({ queryKey: ['me', 'cart'] })
|
||||
cartAdded()
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Remove-мутация без уведомления.
|
||||
|
||||
### 5. Mount point — `app/AppProviders.tsx`
|
||||
|
||||
`<CartSnackbar />` рендерится один раз в `AppProviders.tsx` (или `App.tsx`), чтобы быть доступным во всём приложении.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- effector / effector-react (уже используется)
|
||||
- @mui/material — Snackbar, Alert (уже используется)
|
||||
- react-router-dom — useNavigate (уже используется)
|
||||
|
||||
## Testing
|
||||
|
||||
- Unit-тест `CartSnackbar`: открывается по `cartAdded()`, закрывается по `cartDismissed()`
|
||||
- Unit-тест `AddToCartButton`: вызывает `cartAdded()` в `onSuccess`
|
||||
- Unit-тест `ToggleCartIcon`: вызывает `cartAdded()` только для add-мутации
|
||||
Reference in New Issue
Block a user