diff --git a/.prettierrc.mjs b/.prettierrc.mjs
index c09cbcef..23666d92 100644
--- a/.prettierrc.mjs
+++ b/.prettierrc.mjs
@@ -9,6 +9,12 @@ const config = {
'',
'^@repo(.*)$',
'',
+ '^@/views(.*)$',
+ '^@/widgets(.*)$',
+ '^@/features(.*)$',
+ '^@/entities(.*)$',
+ '^@/shared(.*)$',
+ '',
'^[../]',
'^[./]',
],
diff --git a/apps/web/app/(auth)/layout.tsx b/apps/web/app/(auth)/layout.tsx
index f0832d70..8051227c 100644
--- a/apps/web/app/(auth)/layout.tsx
+++ b/apps/web/app/(auth)/layout.tsx
@@ -1,8 +1,8 @@
-import { FEATURES } from '@/hooks/use-feature-flag'
-import { ROUTES } from '@/shared/config/routes'
import Link from 'next/link'
import { notFound } from 'next/navigation'
+import { FEATURES, ROUTES } from '@/shared/config'
+
interface Props {
children: React.ReactNode
}
diff --git a/apps/web/app/(auth)/login/page.tsx b/apps/web/app/(auth)/login/page.tsx
index 1b8930d9..d75d88cd 100644
--- a/apps/web/app/(auth)/login/page.tsx
+++ b/apps/web/app/(auth)/login/page.tsx
@@ -1,66 +1,5 @@
-'use client'
-
-import { useLogin } from '@/hooks/api/use-auth'
-import { isApiError } from '@/lib/api/utils'
-import { ROUTES } from '@/shared/config/routes'
-import Link from 'next/link'
-import { useRouter } from 'next/navigation'
-
-import { Button, Form, toast } from '@repo/ui'
-
-import { AuthFormLayout } from '../auth-form-layout'
-import { LoginForm } from './login-form'
-import { LoginFormValues, useLoginForm } from './use-login-form'
+import { LoginPageView } from '@/views/auth'
export default function LoginPage() {
- const form = useLoginForm()
-
- const loginMutation = useLogin()
- const router = useRouter()
-
- const onSubmit = async (data: LoginFormValues) => {
- try {
- await loginMutation.mutateAsync(data)
- router.push(ROUTES.teams)
- } catch (error) {
- if (isApiError(error)) {
- toast.error(error.message)
- } else {
- throw error
- }
- }
- }
-
- return (
-
-
- )
+ return
}
diff --git a/apps/web/app/(auth)/register/page.tsx b/apps/web/app/(auth)/register/page.tsx
index c883890a..16b13f29 100644
--- a/apps/web/app/(auth)/register/page.tsx
+++ b/apps/web/app/(auth)/register/page.tsx
@@ -1,66 +1,5 @@
-'use client'
-
-import { useRegister } from '@/hooks/api/use-auth'
-import { isApiError } from '@/lib/api/utils'
-import { ROUTES } from '@/shared/config/routes'
-import Link from 'next/link'
-import { useRouter } from 'next/navigation'
-
-import { Button, Form, toast } from '@repo/ui'
-
-import { AuthFormLayout } from '../auth-form-layout'
-import { RegisterForm } from './register-form'
-import { RegisterFormValues, useRegisterForm } from './use-register-form'
+import { RegisterPageView } from '@/views/auth'
export default function RegisterPage() {
- const form = useRegisterForm()
-
- const registerMutation = useRegister()
- const router = useRouter()
-
- const onSubmit = async (data: RegisterFormValues) => {
- try {
- await registerMutation.mutateAsync(data)
- router.push(ROUTES.teams)
- } catch (error) {
- if (isApiError(error)) {
- toast.error(error.message)
- } else {
- throw error
- }
- }
- }
-
- return (
-
-
- )
+ return
}
diff --git a/apps/web/app/(dashboard)/layout.tsx b/apps/web/app/(dashboard)/layout.tsx
index 1f689d7b..80d614ff 100644
--- a/apps/web/app/(dashboard)/layout.tsx
+++ b/apps/web/app/(dashboard)/layout.tsx
@@ -1,15 +1,38 @@
+import { dehydrate, HydrationBoundary } from '@tanstack/react-query'
+import { redirect } from 'next/navigation'
+
import { MainLayout } from '@/widgets/main-layout'
+import { authKeys } from '@/shared/api/use-auth'
+import { ROUTES } from '@/shared/config'
+import { authService } from '@/shared/lib/api/auth-service'
+import { isApiError } from '@/shared/lib/api/utils'
+import { getQueryClient } from '@/shared/lib/query-client'
interface Props {
children: React.ReactNode
modal: React.ReactNode
}
-export default function DashboardLayout({ children, modal }: Props) {
+export default async function DashboardLayout({ children, modal }: Props) {
+ const queryClient = getQueryClient()
+
+ try {
+ await queryClient.prefetchQuery({
+ queryKey: authKeys.getMe,
+ queryFn: authService.getMe,
+ })
+ } catch (error) {
+ if (isApiError(error) && error.statusCode === 401) {
+ redirect(ROUTES.login)
+ }
+
+ throw error
+ }
+
return (
- <>
+
{children}
{modal}
- >
+
)
}
diff --git a/apps/web/app/(dashboard)/sprints/page.tsx b/apps/web/app/(dashboard)/sprints/page.tsx
index 5a35bf11..34b78e60 100644
--- a/apps/web/app/(dashboard)/sprints/page.tsx
+++ b/apps/web/app/(dashboard)/sprints/page.tsx
@@ -1,8 +1,9 @@
-import { FEATURES } from '@/hooks/use-feature-flag'
import { notFound } from 'next/navigation'
import { Card, CardContent } from '@repo/ui'
+import { FEATURES } from '@/shared/config'
+
type SprintTask = {
id: string
title: string
diff --git a/apps/web/app/(dashboard)/tasks/page.tsx b/apps/web/app/(dashboard)/tasks/page.tsx
index 6cf7dfa7..dd00b9ca 100644
--- a/apps/web/app/(dashboard)/tasks/page.tsx
+++ b/apps/web/app/(dashboard)/tasks/page.tsx
@@ -1,8 +1,9 @@
-import { FEATURES } from '@/hooks/use-feature-flag'
import { notFound } from 'next/navigation'
import { Button } from '@repo/ui'
+import { FEATURES } from '@/shared/config'
+
const mockTasks = [
{
id: 'TT-1',
diff --git a/apps/web/app/(dashboard)/teams/[id]/projects/[projectId]/page.tsx b/apps/web/app/(dashboard)/teams/[id]/projects/[projectId]/page.tsx
index b1f80ff8..9dfc63db 100644
--- a/apps/web/app/(dashboard)/teams/[id]/projects/[projectId]/page.tsx
+++ b/apps/web/app/(dashboard)/teams/[id]/projects/[projectId]/page.tsx
@@ -1,7 +1,8 @@
-import { FEATURES } from '@/hooks/use-feature-flag'
-import { ProjectDetailPageView } from '@/views/projects'
import { notFound } from 'next/navigation'
+import { ProjectDetailPageView } from '@/views/projects'
+import { FEATURES } from '@/shared/config'
+
export default function ProjectDetailPage() {
if (!FEATURES.PROJECTS) {
notFound()
diff --git a/apps/web/app/(dashboard)/teams/[id]/projects/page.tsx b/apps/web/app/(dashboard)/teams/[id]/projects/page.tsx
index 3df15731..ecb9f9d3 100644
--- a/apps/web/app/(dashboard)/teams/[id]/projects/page.tsx
+++ b/apps/web/app/(dashboard)/teams/[id]/projects/page.tsx
@@ -1,7 +1,8 @@
-import { FEATURES } from '@/hooks/use-feature-flag'
-import { ProjectsPageView } from '@/views/projects'
import { notFound } from 'next/navigation'
+import { ProjectsPageView } from '@/views/projects'
+import { FEATURES } from '@/shared/config'
+
export default function ProjectsPage() {
if (!FEATURES.PROJECTS) {
notFound()
diff --git a/apps/web/app/(dashboard)/teams/[id]/settings/page.tsx b/apps/web/app/(dashboard)/teams/[id]/settings/page.tsx
index 867d6fb2..37cc59ec 100644
--- a/apps/web/app/(dashboard)/teams/[id]/settings/page.tsx
+++ b/apps/web/app/(dashboard)/teams/[id]/settings/page.tsx
@@ -1,7 +1,8 @@
-import { FEATURES } from '@/hooks/use-feature-flag'
-import { TeamSettingsPageView } from '@/views/teams'
import { notFound } from 'next/navigation'
+import { TeamSettingsPageView } from '@/views/teams'
+import { FEATURES } from '@/shared/config'
+
export default function TeamSettingsPage() {
if (!FEATURES.TEAM_SETTINGS) {
notFound()
diff --git a/apps/web/app/error.tsx b/apps/web/app/error.tsx
index 09e16aa5..15821203 100644
--- a/apps/web/app/error.tsx
+++ b/apps/web/app/error.tsx
@@ -1,11 +1,12 @@
'use client'
-import { ROUTES } from '@/shared/config/routes'
import Link from 'next/link'
import { useEffect } from 'react'
import { Button, EmptyState } from '@repo/ui'
+import { ROUTES } from '@/shared/config'
+
type GlobalErrorProps = {
error: Error & { digest?: string }
reset: () => void
diff --git a/apps/web/app/not-found.tsx b/apps/web/app/not-found.tsx
index f0afa265..f937f9d9 100644
--- a/apps/web/app/not-found.tsx
+++ b/apps/web/app/not-found.tsx
@@ -1,8 +1,9 @@
-import { ROUTES } from '@/shared/config/routes'
import Link from 'next/link'
import { Button, EmptyState } from '@repo/ui'
+import { ROUTES } from '@/shared/config'
+
export default function GlobalNotFound() {
return (
diff --git a/apps/web/app/providers.tsx b/apps/web/app/providers.tsx
index 7e2dc72d..ab1d12b0 100644
--- a/apps/web/app/providers.tsx
+++ b/apps/web/app/providers.tsx
@@ -1,10 +1,10 @@
'use client'
-import { ThemeSync } from '@/features/theme'
import { QueryClientProvider } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
-import { getQueryClient } from '../lib/query-client'
+import { ThemeSync } from '@/features/theme'
+import { getQueryClient } from '@/shared/lib/query-client'
interface Props {
children: React.ReactNode
diff --git a/apps/web/features/theme/ui/theme-sync.tsx b/apps/web/features/theme/ui/theme-sync.tsx
index 7a81c2a8..44f30a89 100644
--- a/apps/web/features/theme/ui/theme-sync.tsx
+++ b/apps/web/features/theme/ui/theme-sync.tsx
@@ -2,7 +2,8 @@
import { useEffect } from 'react'
-import { useHydratedStore } from '../../../shared/hooks'
+import { useHydratedStore } from '@/shared/hooks'
+
import { useThemeStore } from '../model/store'
function ThemeSync() {
diff --git a/apps/web/features/theme/ui/theme-toggle.tsx b/apps/web/features/theme/ui/theme-toggle.tsx
index 5846eeca..fbba1207 100644
--- a/apps/web/features/theme/ui/theme-toggle.tsx
+++ b/apps/web/features/theme/ui/theme-toggle.tsx
@@ -2,7 +2,8 @@
import { Button, cn, MoonIcon, SunIcon } from '@repo/ui'
-import { useHydratedStore } from '../../../shared/hooks'
+import { useHydratedStore } from '@/shared/hooks'
+
import { useThemeStore } from '../model/store'
interface ThemeToggleProps {
diff --git a/apps/web/proxy.ts b/apps/web/proxy.ts
index f6ad920a..1dbb0042 100644
--- a/apps/web/proxy.ts
+++ b/apps/web/proxy.ts
@@ -1,17 +1,17 @@
+import { NextResponse, type NextRequest, type ProxyConfig } from 'next/server'
+
import {
isAuthRoute,
isProtectedRoute,
ROUTE_QUERY_PARAMS,
ROUTES,
-} from '@/shared/config/routes'
-import { NextResponse, type NextRequest, type ProxyConfig } from 'next/server'
-
-import { appendSetCookieHeaders, clearAuthCookies } from './lib/api/auth-cookies'
+} from '@/shared/config'
+import { appendSetCookieHeaders, clearAuthCookies } from '@/shared/lib/api/auth-cookies'
import {
refreshAuthSession,
type AuthRefreshResult,
-} from './lib/api/refresh-auth-session'
-import { isTokenExpiredSoon } from './lib/session'
+} from '@/shared/lib/api/refresh-auth-session'
+import { isTokenExpiredSoon } from '@/shared/lib/session'
const createLoginRedirect = (request: NextRequest) => {
const { pathname, search } = request.nextUrl
diff --git a/apps/web/hooks/api/use-auth.ts b/apps/web/shared/api/use-auth.ts
similarity index 91%
rename from apps/web/hooks/api/use-auth.ts
rename to apps/web/shared/api/use-auth.ts
index c9bbaac0..51dfc037 100644
--- a/apps/web/hooks/api/use-auth.ts
+++ b/apps/web/shared/api/use-auth.ts
@@ -1,6 +1,7 @@
-import { authService } from '@/lib/api/auth-service'
import { useMutation, useQuery } from '@tanstack/react-query'
+import { authService } from '@/shared/lib/api/auth-service'
+
export const authKeys = {
register: ['register'],
login: ['login'],
diff --git a/apps/web/hooks/api/use-example.ts b/apps/web/shared/api/use-example.ts
similarity index 95%
rename from apps/web/hooks/api/use-example.ts
rename to apps/web/shared/api/use-example.ts
index 712eec29..3486246c 100644
--- a/apps/web/hooks/api/use-example.ts
+++ b/apps/web/shared/api/use-example.ts
@@ -10,8 +10,8 @@ import {
Example,
exampleService,
UpdateExampleDto,
-} from '../../lib/api/example-service'
-import { ApiError, PaginationParams } from '../../lib/api/types'
+} from '@/shared/lib/api/example-service'
+import { ApiError, PaginationParams } from '@/shared/lib/api/types'
// Query keys для кэширования
export const exampleKeys = {
diff --git a/apps/web/hooks/api/use-teams.ts b/apps/web/shared/api/use-teams.ts
similarity index 95%
rename from apps/web/hooks/api/use-teams.ts
rename to apps/web/shared/api/use-teams.ts
index aedd8ad2..e81c052e 100644
--- a/apps/web/hooks/api/use-teams.ts
+++ b/apps/web/shared/api/use-teams.ts
@@ -1,12 +1,13 @@
+import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
+
import {
teamsService,
type CreateTeam,
type DeleteTeamResponse,
type Team,
type UpdateTeam,
-} from '@/lib/api/teams-service'
-import type { ApiError } from '@/lib/api/types'
-import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
+} from '@/shared/lib/api/teams-service'
+import type { ApiError } from '@/shared/lib/api/types'
export const teamsKeys = {
all: ['teams'] as const,
diff --git a/apps/web/hooks/use-feature-flag.ts b/apps/web/shared/config/feature-flags.ts
similarity index 100%
rename from apps/web/hooks/use-feature-flag.ts
rename to apps/web/shared/config/feature-flags.ts
diff --git a/apps/web/shared/config/index.ts b/apps/web/shared/config/index.ts
new file mode 100644
index 00000000..0bcf4b99
--- /dev/null
+++ b/apps/web/shared/config/index.ts
@@ -0,0 +1,2 @@
+export * from './feature-flags'
+export * from './routes'
diff --git a/apps/web/lib/api/auth-cookies.ts b/apps/web/shared/lib/api/auth-cookies.ts
similarity index 100%
rename from apps/web/lib/api/auth-cookies.ts
rename to apps/web/shared/lib/api/auth-cookies.ts
diff --git a/apps/web/lib/api/auth-service.ts b/apps/web/shared/lib/api/auth-service.ts
similarity index 100%
rename from apps/web/lib/api/auth-service.ts
rename to apps/web/shared/lib/api/auth-service.ts
diff --git a/apps/web/lib/api/axios-config.ts b/apps/web/shared/lib/api/axios-config.ts
similarity index 100%
rename from apps/web/lib/api/axios-config.ts
rename to apps/web/shared/lib/api/axios-config.ts
diff --git a/apps/web/lib/api/client.ts b/apps/web/shared/lib/api/client.ts
similarity index 92%
rename from apps/web/lib/api/client.ts
rename to apps/web/shared/lib/api/client.ts
index e90b32a9..ba1d4ddf 100644
--- a/apps/web/lib/api/client.ts
+++ b/apps/web/shared/lib/api/client.ts
@@ -1,10 +1,11 @@
-import { ROUTE_QUERY_PARAMS, ROUTES } from '@/shared/config/routes'
import axios, { AxiosError, InternalAxiosRequestConfig } from 'axios'
+import { ROUTE_QUERY_PARAMS, ROUTES } from '@/shared/config'
+
import { isClientSide, setCookieHeader } from './auth-cookies'
import { axiosConfig } from './axios-config'
import { refreshAuthSession } from './refresh-auth-session'
-import { ApiError } from './types'
+import type { ApiError } from './types'
import { toApiError } from './utils'
type RetryableConfig = InternalAxiosRequestConfig & {
diff --git a/apps/web/lib/api/example-service.ts b/apps/web/shared/lib/api/example-service.ts
similarity index 100%
rename from apps/web/lib/api/example-service.ts
rename to apps/web/shared/lib/api/example-service.ts
diff --git a/apps/web/lib/api/public-client.ts b/apps/web/shared/lib/api/public-client.ts
similarity index 100%
rename from apps/web/lib/api/public-client.ts
rename to apps/web/shared/lib/api/public-client.ts
diff --git a/apps/web/lib/api/refresh-auth-session.ts b/apps/web/shared/lib/api/refresh-auth-session.ts
similarity index 100%
rename from apps/web/lib/api/refresh-auth-session.ts
rename to apps/web/shared/lib/api/refresh-auth-session.ts
diff --git a/apps/web/lib/api/request-auth-refresh.ts b/apps/web/shared/lib/api/request-auth-refresh.ts
similarity index 100%
rename from apps/web/lib/api/request-auth-refresh.ts
rename to apps/web/shared/lib/api/request-auth-refresh.ts
diff --git a/apps/web/lib/api/teams-service.ts b/apps/web/shared/lib/api/teams-service.ts
similarity index 100%
rename from apps/web/lib/api/teams-service.ts
rename to apps/web/shared/lib/api/teams-service.ts
diff --git a/apps/web/lib/api/types.ts b/apps/web/shared/lib/api/types.ts
similarity index 100%
rename from apps/web/lib/api/types.ts
rename to apps/web/shared/lib/api/types.ts
diff --git a/apps/web/lib/api/utils.ts b/apps/web/shared/lib/api/utils.ts
similarity index 100%
rename from apps/web/lib/api/utils.ts
rename to apps/web/shared/lib/api/utils.ts
diff --git a/apps/web/lib/projects/catalog.ts b/apps/web/shared/lib/projects/catalog.ts
similarity index 98%
rename from apps/web/lib/projects/catalog.ts
rename to apps/web/shared/lib/projects/catalog.ts
index f08c59f1..29381501 100644
--- a/apps/web/lib/projects/catalog.ts
+++ b/apps/web/shared/lib/projects/catalog.ts
@@ -1,4 +1,4 @@
-import { teamRoutes } from '@/shared/config/routes'
+import { teamRoutes } from '@/shared/config'
export type ProjectCatalogItem = {
id: string
diff --git a/apps/web/shared/lib/projects/index.ts b/apps/web/shared/lib/projects/index.ts
new file mode 100644
index 00000000..6f5c1d90
--- /dev/null
+++ b/apps/web/shared/lib/projects/index.ts
@@ -0,0 +1,2 @@
+export * from './catalog'
+export * from './project-code'
diff --git a/apps/web/lib/projects/project-code.ts b/apps/web/shared/lib/projects/project-code.ts
similarity index 100%
rename from apps/web/lib/projects/project-code.ts
rename to apps/web/shared/lib/projects/project-code.ts
diff --git a/apps/web/lib/query-client.ts b/apps/web/shared/lib/query-client.ts
similarity index 100%
rename from apps/web/lib/query-client.ts
rename to apps/web/shared/lib/query-client.ts
diff --git a/apps/web/lib/session/index.ts b/apps/web/shared/lib/session/index.ts
similarity index 100%
rename from apps/web/lib/session/index.ts
rename to apps/web/shared/lib/session/index.ts
diff --git a/apps/web/lib/session/utils.ts b/apps/web/shared/lib/session/utils.ts
similarity index 100%
rename from apps/web/lib/session/utils.ts
rename to apps/web/shared/lib/session/utils.ts
diff --git a/apps/web/views/auth/index.ts b/apps/web/views/auth/index.ts
new file mode 100644
index 00000000..e1d9e759
--- /dev/null
+++ b/apps/web/views/auth/index.ts
@@ -0,0 +1,2 @@
+export { LoginPageView } from './ui/login-page-view'
+export { RegisterPageView } from './ui/register-page-view'
diff --git a/apps/web/app/(auth)/login/use-login-form.ts b/apps/web/views/auth/model/use-login-form.ts
similarity index 100%
rename from apps/web/app/(auth)/login/use-login-form.ts
rename to apps/web/views/auth/model/use-login-form.ts
diff --git a/apps/web/app/(auth)/register/use-register-form.ts b/apps/web/views/auth/model/use-register-form.ts
similarity index 100%
rename from apps/web/app/(auth)/register/use-register-form.ts
rename to apps/web/views/auth/model/use-register-form.ts
diff --git a/apps/web/app/(auth)/auth-form-layout.tsx b/apps/web/views/auth/ui/auth-form-layout.tsx
similarity index 100%
rename from apps/web/app/(auth)/auth-form-layout.tsx
rename to apps/web/views/auth/ui/auth-form-layout.tsx
diff --git a/apps/web/app/(auth)/login/login-form.tsx b/apps/web/views/auth/ui/login-form.tsx
similarity index 93%
rename from apps/web/app/(auth)/login/login-form.tsx
rename to apps/web/views/auth/ui/login-form.tsx
index ce59e409..8f5d093e 100644
--- a/apps/web/app/(auth)/login/login-form.tsx
+++ b/apps/web/views/auth/ui/login-form.tsx
@@ -12,7 +12,7 @@ import {
VStack,
} from '@repo/ui'
-import { LoginFormValues } from './use-login-form'
+import type { LoginFormValues } from '../model/use-login-form'
const LoginForm = () => {
const { control } = useFormContext()
diff --git a/apps/web/views/auth/ui/login-page-view.tsx b/apps/web/views/auth/ui/login-page-view.tsx
new file mode 100644
index 00000000..1d6af186
--- /dev/null
+++ b/apps/web/views/auth/ui/login-page-view.tsx
@@ -0,0 +1,69 @@
+'use client'
+
+import Link from 'next/link'
+import { useRouter } from 'next/navigation'
+
+import { Button, Form, toast } from '@repo/ui'
+
+import { useLogin } from '@/shared/api/use-auth'
+import { ROUTES } from '@/shared/config'
+import { isApiError } from '@/shared/lib/api/utils'
+
+import { useLoginForm, type LoginFormValues } from '../model/use-login-form'
+import { AuthFormLayout } from './auth-form-layout'
+import { LoginForm } from './login-form'
+
+function LoginPageView() {
+ const form = useLoginForm()
+
+ const loginMutation = useLogin()
+ const router = useRouter()
+
+ const onSubmit = async (data: LoginFormValues) => {
+ try {
+ await loginMutation.mutateAsync(data)
+ router.push(ROUTES.teams)
+ } catch (error) {
+ if (isApiError(error)) {
+ toast.error(error.message)
+ } else {
+ throw error
+ }
+ }
+ }
+
+ return (
+
+
+ )
+}
+
+export { LoginPageView }
diff --git a/apps/web/app/(auth)/register/register-form.tsx b/apps/web/views/auth/ui/register-form.tsx
similarity index 95%
rename from apps/web/app/(auth)/register/register-form.tsx
rename to apps/web/views/auth/ui/register-form.tsx
index 3716b06a..a620ca94 100644
--- a/apps/web/app/(auth)/register/register-form.tsx
+++ b/apps/web/views/auth/ui/register-form.tsx
@@ -12,7 +12,7 @@ import {
VStack,
} from '@repo/ui'
-import { RegisterFormValues } from './use-register-form'
+import type { RegisterFormValues } from '../model/use-register-form'
const RegisterForm = () => {
const { control } = useFormContext()
diff --git a/apps/web/views/auth/ui/register-page-view.tsx b/apps/web/views/auth/ui/register-page-view.tsx
new file mode 100644
index 00000000..180f21ac
--- /dev/null
+++ b/apps/web/views/auth/ui/register-page-view.tsx
@@ -0,0 +1,69 @@
+'use client'
+
+import Link from 'next/link'
+import { useRouter } from 'next/navigation'
+
+import { Button, Form, toast } from '@repo/ui'
+
+import { useRegister } from '@/shared/api/use-auth'
+import { ROUTES } from '@/shared/config'
+import { isApiError } from '@/shared/lib/api/utils'
+
+import { useRegisterForm, type RegisterFormValues } from '../model/use-register-form'
+import { AuthFormLayout } from './auth-form-layout'
+import { RegisterForm } from './register-form'
+
+function RegisterPageView() {
+ const form = useRegisterForm()
+
+ const registerMutation = useRegister()
+ const router = useRouter()
+
+ const onSubmit = async (data: RegisterFormValues) => {
+ try {
+ await registerMutation.mutateAsync(data)
+ router.push(ROUTES.teams)
+ } catch (error) {
+ if (isApiError(error)) {
+ toast.error(error.message)
+ } else {
+ throw error
+ }
+ }
+ }
+
+ return (
+
+
+ )
+}
+
+export { RegisterPageView }
diff --git a/apps/web/views/home/model/content.ts b/apps/web/views/home/model/content.ts
index cac770d5..feda8d2d 100644
--- a/apps/web/views/home/model/content.ts
+++ b/apps/web/views/home/model/content.ts
@@ -1,4 +1,3 @@
-import { ROUTES } from '@/shared/config/routes'
import type { ComponentType, SVGProps } from 'react'
import {
@@ -14,6 +13,8 @@ import {
Zap,
} from '@repo/ui/icons'
+import { ROUTES } from '@/shared/config'
+
type IconType = ComponentType>
export const navigationItems = [
diff --git a/apps/web/views/home/ui/home-header.tsx b/apps/web/views/home/ui/home-header.tsx
index 0dabc700..24164504 100644
--- a/apps/web/views/home/ui/home-header.tsx
+++ b/apps/web/views/home/ui/home-header.tsx
@@ -1,9 +1,10 @@
-import { ROUTES } from '@/shared/config/routes'
import Link from 'next/link'
import { Button } from '@repo/ui'
import { Github, KanbanSquare } from '@repo/ui/icons'
+import { ROUTES } from '@/shared/config'
+
import { navigationItems } from '../model/content'
function HomeHeader() {
diff --git a/apps/web/views/projects/ui/create-project-dialog.tsx b/apps/web/views/projects/ui/create-project-dialog.tsx
index aebf46e8..72748bf9 100644
--- a/apps/web/views/projects/ui/create-project-dialog.tsx
+++ b/apps/web/views/projects/ui/create-project-dialog.tsx
@@ -1,11 +1,5 @@
'use client'
-import {
- isValidProjectCode,
- normalizeProjectCodeInput,
- PROJECT_CODE_MAX_LENGTH,
- PROJECT_CODE_MIN_LENGTH,
-} from '@/lib/projects/project-code'
import { useEffect, useState } from 'react'
import {
@@ -19,6 +13,13 @@ import {
Label,
} from '@repo/ui'
+import {
+ isValidProjectCode,
+ normalizeProjectCodeInput,
+ PROJECT_CODE_MAX_LENGTH,
+ PROJECT_CODE_MIN_LENGTH,
+} from '@/shared/lib/projects'
+
import {
projectDialogContentClassName,
projectDialogFooterClassName,
diff --git a/apps/web/views/projects/ui/project-card.tsx b/apps/web/views/projects/ui/project-card.tsx
index 7523909e..09cc9895 100644
--- a/apps/web/views/projects/ui/project-card.tsx
+++ b/apps/web/views/projects/ui/project-card.tsx
@@ -1,10 +1,10 @@
'use client'
-import type { ProjectCatalogItem } from '@/lib/projects/catalog'
-
import { cn } from '@repo/ui'
import { ArrowRight, KanbanSquare, ListTodo } from '@repo/ui/icons'
+import type { ProjectCatalogItem } from '@/shared/lib/projects'
+
interface Props {
project: ProjectCatalogItem
onOpen: (project: ProjectCatalogItem) => void
diff --git a/apps/web/views/projects/ui/project-detail-page-view.tsx b/apps/web/views/projects/ui/project-detail-page-view.tsx
index 56fe3f77..26d5a81b 100644
--- a/apps/web/views/projects/ui/project-detail-page-view.tsx
+++ b/apps/web/views/projects/ui/project-detail-page-view.tsx
@@ -1,8 +1,5 @@
'use client'
-import { useTeamDetail } from '@/hooks/api/use-teams'
-import { formatProjectNameFromId, getProjectById } from '@/lib/projects/catalog'
-import { teamRoutes } from '@/shared/config/routes'
import Link from 'next/link'
import { useParams } from 'next/navigation'
@@ -16,6 +13,10 @@ import {
SquareKanban,
} from '@repo/ui/icons'
+import { useTeamDetail } from '@/shared/api/use-teams'
+import { teamRoutes } from '@/shared/config'
+import { formatProjectNameFromId, getProjectById } from '@/shared/lib/projects'
+
import { projectPageSubtitleClassName, projectPageTitleClassName } from '../lib/styles'
const actionCards = [
diff --git a/apps/web/views/projects/ui/projects-page-view.tsx b/apps/web/views/projects/ui/projects-page-view.tsx
index 3ac10e88..fbc14187 100644
--- a/apps/web/views/projects/ui/projects-page-view.tsx
+++ b/apps/web/views/projects/ui/projects-page-view.tsx
@@ -1,18 +1,19 @@
'use client'
-import { useTeamDetail } from '@/hooks/api/use-teams'
-import {
- buildTeamProjectHref,
- createProjectId,
- projectCatalog,
- type ProjectCatalogItem,
-} from '@/lib/projects/catalog'
import { useParams, useRouter } from 'next/navigation'
import { useState } from 'react'
import { Button, EmptyState, Input } from '@repo/ui'
import { FolderKanban, Plus, Search } from '@repo/ui/icons'
+import { useTeamDetail } from '@/shared/api/use-teams'
+import {
+ buildTeamProjectHref,
+ createProjectId,
+ projectCatalog,
+ type ProjectCatalogItem,
+} from '@/shared/lib/projects'
+
import {
projectPageHeaderClassName,
projectPagePrimaryButtonClassName,
diff --git a/apps/web/views/teams/ui/create-team-dialog.tsx b/apps/web/views/teams/ui/create-team-dialog.tsx
index 548128c8..fd901b9e 100644
--- a/apps/web/views/teams/ui/create-team-dialog.tsx
+++ b/apps/web/views/teams/ui/create-team-dialog.tsx
@@ -1,8 +1,5 @@
'use client'
-import { useCreateTeam } from '@/hooks/api/use-teams'
-import { isApiError } from '@/lib/api/utils'
-import { ROUTES } from '@/shared/config/routes'
import { useRouter } from 'next/navigation'
import { useState } from 'react'
@@ -19,6 +16,10 @@ import {
toast,
} from '@repo/ui'
+import { useCreateTeam } from '@/shared/api/use-teams'
+import { ROUTES } from '@/shared/config'
+import { isApiError } from '@/shared/lib/api/utils'
+
import {
teamDialogContentClassName,
teamDialogFooterClassName,
diff --git a/apps/web/views/teams/ui/team-settings-page-view.tsx b/apps/web/views/teams/ui/team-settings-page-view.tsx
index c60a23fd..eb1c7af6 100644
--- a/apps/web/views/teams/ui/team-settings-page-view.tsx
+++ b/apps/web/views/teams/ui/team-settings-page-view.tsx
@@ -1,6 +1,5 @@
'use client'
-import { useTeamDetail } from '@/hooks/api/use-teams'
import { useParams } from 'next/navigation'
import { useEffect, useState } from 'react'
@@ -28,6 +27,8 @@ import {
} from '@repo/ui'
import { Crown, Mail, Settings2, Shield, Trash2, UserPlus, Users } from '@repo/ui/icons'
+import { useTeamDetail } from '@/shared/api/use-teams'
+
import {
teamDialogContentClassName,
teamDialogFooterClassName,
diff --git a/apps/web/views/teams/ui/teams-page-view.tsx b/apps/web/views/teams/ui/teams-page-view.tsx
index 1a21c422..49b6881e 100644
--- a/apps/web/views/teams/ui/teams-page-view.tsx
+++ b/apps/web/views/teams/ui/teams-page-view.tsx
@@ -1,13 +1,14 @@
'use client'
-import { useTeamsList } from '@/hooks/api/use-teams'
-import { ROUTES, teamRoutes } from '@/shared/config/routes'
import { useRouter } from 'next/navigation'
import { useMemo } from 'react'
import { Button, EmptyState } from '@repo/ui'
import { Plus, Users } from '@repo/ui/icons'
+import { useTeamsList } from '@/shared/api/use-teams'
+import { ROUTES, teamRoutes } from '@/shared/config'
+
import { mapTeamListItemToTeamCardModel } from '../lib/mappers'
import {
teamPageHeaderClassName,
diff --git a/apps/web/widgets/main-layout/model/sidebar/navigation.ts b/apps/web/widgets/main-layout/model/sidebar/navigation.ts
index 9c314d1b..a279c17d 100644
--- a/apps/web/widgets/main-layout/model/sidebar/navigation.ts
+++ b/apps/web/widgets/main-layout/model/sidebar/navigation.ts
@@ -1,7 +1,3 @@
-import { FEATURES } from '@/hooks/use-feature-flag'
-import { projectCatalog } from '@/lib/projects/catalog'
-import { ROUTES, SIDEBAR_ROUTE_IDS, teamRoutes } from '@/shared/config/routes'
-
import {
Bell,
FolderKanban,
@@ -13,6 +9,9 @@ import {
Users,
} from '@repo/ui/icons'
+import { FEATURES, ROUTES, SIDEBAR_ROUTE_IDS, teamRoutes } from '@/shared/config'
+import { projectCatalog } from '@/shared/lib/projects'
+
import type {
SidebarNavSection,
SidebarProjectItem,
diff --git a/apps/web/widgets/main-layout/model/sidebar/types.ts b/apps/web/widgets/main-layout/model/sidebar/types.ts
index 9d842974..fd365861 100644
--- a/apps/web/widgets/main-layout/model/sidebar/types.ts
+++ b/apps/web/widgets/main-layout/model/sidebar/types.ts
@@ -1,7 +1,7 @@
-import type { SidebarRouteId } from '@/shared/config/routes'
-
import type { LucideIcon } from '@repo/ui/icons'
+import type { SidebarRouteId } from '@/shared/config'
+
type SideBarStoreState = {
isOpen: boolean
}
diff --git a/apps/web/widgets/main-layout/ui/header/profile-menu.tsx b/apps/web/widgets/main-layout/ui/header/profile-menu.tsx
index e2bc0f2c..d8faaa31 100644
--- a/apps/web/widgets/main-layout/ui/header/profile-menu.tsx
+++ b/apps/web/widgets/main-layout/ui/header/profile-menu.tsx
@@ -1,8 +1,5 @@
'use client'
-import { useLogout } from '@/hooks/api/use-auth'
-import { ROUTES } from '@/shared/config/routes'
-import { useRouter } from 'next/navigation'
import React from 'react'
import {
@@ -17,21 +14,26 @@ import {
DropdownMenuTrigger,
} from '@repo/ui'
+import { useLogout, useMe } from '@/shared/api/use-auth'
+import { ROUTE_QUERY_PARAMS, ROUTES } from '@/shared/config'
+
const currentUser = {
name: 'Имя',
avatar: 'AL',
}
const ProfileMenu = () => {
- const router = useRouter()
const logoutMutation = useLogout()
- const handleLogout = async () => {
- await logoutMutation.mutateAsync()
-
- router.push(ROUTES.login)
+ const handleLogout = () => {
+ logoutMutation.mutateAsync()
+ const url = new URL(ROUTES.login, window.location.origin)
+ url.searchParams.set(ROUTE_QUERY_PARAMS.clearAuth, '1')
+ window.location.assign(url)
}
+ const profileQuery = useMe()
+
return (
@@ -48,7 +50,7 @@ const ProfileMenu = () => {
{currentUser.avatar}
- {currentUser.name}
+ {profileQuery.data?.name}
diff --git a/apps/web/widgets/main-layout/ui/sidebar/sidebar.tsx b/apps/web/widgets/main-layout/ui/sidebar/sidebar.tsx
index d6da24aa..26a478c5 100644
--- a/apps/web/widgets/main-layout/ui/sidebar/sidebar.tsx
+++ b/apps/web/widgets/main-layout/ui/sidebar/sidebar.tsx
@@ -1,9 +1,5 @@
'use client'
-import { ThemeToggle } from '@/features/theme'
-import { useTeamsList } from '@/hooks/api/use-teams'
-import { buildTeamProjectHref } from '@/lib/projects/catalog'
-import { getSidebarRouteId, ROUTES, type SidebarRouteId } from '@/shared/config/routes'
import Link from 'next/link'
import { useParams, usePathname } from 'next/navigation'
import React from 'react'
@@ -11,6 +7,11 @@ import React from 'react'
import { Avatar, AvatarFallback, cn } from '@repo/ui'
import { KanbanSquare } from '@repo/ui/icons'
+import { ThemeToggle } from '@/features/theme'
+import { useTeamsList } from '@/shared/api/use-teams'
+import { getSidebarRouteId, ROUTES, type SidebarRouteId } from '@/shared/config'
+import { buildTeamProjectHref } from '@/shared/lib/projects'
+
import {
getSidebarSections,
sidebarCurrentUser,