Контекст
После подтверждения удаления проекта (RemoveProjectDialog → useRemoveProject → ProjectHttp.removeProject) выполняется инвалидация кэша списка проектов и показывается toast, но навигация не меняется. Если удаление инициировано со страницы проекта (/team/projects/[projectId], /team/projects/[projectId]/settings), пользователь остаётся на URL несуществующего ресурса — возможны 404, ошибки загрузки деталей проекта и некорректное состояние UI (сайдбар, breadcrumbs). Требуется единообразное поведение: после успешного удаления переводить пользователя на страницу со всеми проектами команды.
Технические требования
- Локация логики:
src/features/projects/remove/ (useRemoveProject.ts, RemoveProjectDialog.tsx); точки входа: src/pages/project/ui/settings/ProjectDangerZone.tsx, src/pages/team/ui/projects/ProjectCard.tsx; маршрут назначения — routes.team.projects() (/team/projects) в src/shared/config/routes.ts
- Инструменты: Next.js App Router (
useRouter из next/navigation), TanStack Query (useMutation, invalidateQueries), существующий ProjectHttp.removeProject, sonner (toast уже в хуке)
- Логика работы:
- В колбэке
onSuccess мутации удаления (после успешного ответа API и до/после invalidateQueries по projectFabricKeys.list(teamSlug)) выполнить router.replace(routes.team.projects()) (предпочтительно replace, чтобы нельзя было вернуться «Назад» на удалённый проект).
- Редирект должен срабатывать для всех сценариев использования
RemoveProjectDialog (настройки проекта, карточка на ProjectsPage) без дублирования логики в каждом потребителе — централизовать в useRemoveProject или в RemoveProjectDialog после успешной мутации.
- При ошибке мутации (
onError) навигация не выполняется; диалог остаётся открытым или закрывается по текущему UX — без перехода.
- Если текущий
pathname уже равен routes.team.projects(), допускается пропуск редиректа (опционально) — достаточно инвалидации списка; главное — обязательный уход со страниц /team/projects/[projectId]/*.
- После редиректа список проектов должен отражать удаление (существующая инвалидация
projectFabricKeys.list); при необходимости сбросить projectFabricKeys.detail(teamSlug, id) для удалённого id.
Цель и критерии приемки (Definition of Done)
Важные указания
- Производительность: Дополнительных запросов не требуется; один
router.replace и существующая инвалидация списка.
- Ошибки: При
4xx/5xx от DELETE /teams/{teamSlug}/projects/{id} оставить текущую обработку мутации (toast error при наличии в проекте); редирект только на onSuccess.
- Безопасность: Не ослаблять проверки
canEdit / disabled на RemoveProjectDialog; редирект не должен маскировать отказ API (403/404). Удаление по-прежнему только через подтверждение имени проекта в диалоге.
Контекст
После подтверждения удаления проекта (
RemoveProjectDialog→useRemoveProject→ProjectHttp.removeProject) выполняется инвалидация кэша списка проектов и показывается toast, но навигация не меняется. Если удаление инициировано со страницы проекта (/team/projects/[projectId],/team/projects/[projectId]/settings), пользователь остаётся на URL несуществующего ресурса — возможны 404, ошибки загрузки деталей проекта и некорректное состояние UI (сайдбар, breadcrumbs). Требуется единообразное поведение: после успешного удаления переводить пользователя на страницу со всеми проектами команды.Технические требования
src/features/projects/remove/(useRemoveProject.ts,RemoveProjectDialog.tsx); точки входа:src/pages/project/ui/settings/ProjectDangerZone.tsx,src/pages/team/ui/projects/ProjectCard.tsx; маршрут назначения —routes.team.projects()(/team/projects) вsrc/shared/config/routes.tsuseRouterизnext/navigation), TanStack Query (useMutation,invalidateQueries), существующийProjectHttp.removeProject,sonner(toast уже в хуке)onSuccessмутации удаления (после успешного ответа API и до/послеinvalidateQueriesпоprojectFabricKeys.list(teamSlug)) выполнитьrouter.replace(routes.team.projects())(предпочтительноreplace, чтобы нельзя было вернуться «Назад» на удалённый проект).RemoveProjectDialog(настройки проекта, карточка наProjectsPage) без дублирования логики в каждом потребителе — централизовать вuseRemoveProjectили вRemoveProjectDialogпосле успешной мутации.onError) навигация не выполняется; диалог остаётся открытым или закрывается по текущему UX — без перехода.pathnameуже равенroutes.team.projects(), допускается пропуск редиректа (опционально) — достаточно инвалидации списка; главное — обязательный уход со страниц/team/projects/[projectId]/*.projectFabricKeys.list); при необходимости сброситьprojectFabricKeys.detail(teamSlug, id)для удалённого id.Цель и критерии приемки (Definition of Done)
routes.team.projects()(без хардкода строк URL в компонентах)./team/projects/[projectId]/settings(и любой вложенной страницы проекта) пользователь оказывается на/team/projects; удалённый проект отсутствует в списке; toast «Проект удалён» сохраняется.ProjectsPage(карточка проекта) не ломает UX: список обновляется, лишних полноэкранных переходов нет (допустим no-op редирект на ту же страницу).pushпри двойном клике (кнопка «Удалить» disabled на времяisPending)./team/projects; карточка на списке → удаление → карточка исчезает; кнопка «Назад» в браузере не возвращает на страницу удалённого проекта (при использованииreplace).Важные указания
router.replaceи существующая инвалидация списка.4xx/5xxотDELETE /teams/{teamSlug}/projects/{id}оставить текущую обработку мутации (toast error при наличии в проекте); редирект только наonSuccess.canEdit/disabledнаRemoveProjectDialog; редирект не должен маскировать отказ API (403/404). Удаление по-прежнему только через подтверждение имени проекта в диалоге.