Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .prettierrc.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ const config = {
'',
'^@repo(.*)$',
'',
'^@/views(.*)$',
'^@/widgets(.*)$',
'^@/features(.*)$',
'^@/entities(.*)$',
'^@/shared(.*)$',
'',
'^[../]',
'^[./]',
],
Expand Down
4 changes: 2 additions & 2 deletions apps/web/app/(auth)/layout.tsx
Original file line number Diff line number Diff line change
@@ -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
}
Expand Down
65 changes: 2 additions & 63 deletions apps/web/app/(auth)/login/page.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className='w-full max-w-sm'>
<AuthFormLayout
title='Войдите в свой аккаунт'
footer={
<Button
disabled={form.formState.isSubmitting}
type='submit'
variant='default'
className='w-full'
>
Войти
</Button>
}
>
<div className='space-y-4'>
<LoginForm />
<p className='text-center text-sm text-muted-foreground'>
Еще нет аккаунта?{' '}
<Link
href={ROUTES.register}
className='font-medium text-primary transition-opacity hover:opacity-80'
>
Зарегистрироваться
</Link>
</p>
</div>
</AuthFormLayout>
</form>
</Form>
)
return <LoginPageView />
}
65 changes: 2 additions & 63 deletions apps/web/app/(auth)/register/page.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className='w-full max-w-sm'>
<AuthFormLayout
title='Регистрация'
footer={
<Button
disabled={form.formState.isSubmitting}
type='submit'
variant='default'
className='w-full'
>
Зарегистрироваться
</Button>
}
>
<div className='space-y-4'>
<RegisterForm />
<p className='text-center text-sm text-muted-foreground'>
Уже есть аккаунт?{' '}
<Link
href={ROUTES.login}
className='font-medium text-primary transition-opacity hover:opacity-80'
>
Войти
</Link>
</p>
</div>
</AuthFormLayout>
</form>
</Form>
)
return <RegisterPageView />
}
29 changes: 26 additions & 3 deletions apps/web/app/(dashboard)/layout.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<>
<HydrationBoundary state={dehydrate(queryClient)}>
<MainLayout>{children}</MainLayout>
{modal}
</>
</HydrationBoundary>
)
}
3 changes: 2 additions & 1 deletion apps/web/app/(dashboard)/sprints/page.tsx
Original file line number Diff line number Diff line change
@@ -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
Expand Down
3 changes: 2 additions & 1 deletion apps/web/app/(dashboard)/tasks/page.tsx
Original file line number Diff line number Diff line change
@@ -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',
Expand Down
Original file line number Diff line number Diff line change
@@ -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()
Expand Down
5 changes: 3 additions & 2 deletions apps/web/app/(dashboard)/teams/[id]/projects/page.tsx
Original file line number Diff line number Diff line change
@@ -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()
Expand Down
5 changes: 3 additions & 2 deletions apps/web/app/(dashboard)/teams/[id]/settings/page.tsx
Original file line number Diff line number Diff line change
@@ -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()
Expand Down
3 changes: 2 additions & 1 deletion apps/web/app/error.tsx
Original file line number Diff line number Diff line change
@@ -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
Expand Down
3 changes: 2 additions & 1 deletion apps/web/app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<main className='flex min-h-screen items-center justify-center bg-slate-950 px-4 text-slate-100'>
Expand Down
4 changes: 2 additions & 2 deletions apps/web/app/providers.tsx
Original file line number Diff line number Diff line change
@@ -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
Expand Down
3 changes: 2 additions & 1 deletion apps/web/features/theme/ui/theme-sync.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
3 changes: 2 additions & 1 deletion apps/web/features/theme/ui/theme-toggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
12 changes: 6 additions & 6 deletions apps/web/proxy.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
2 changes: 2 additions & 0 deletions apps/web/shared/config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './feature-flags'
export * from './routes'
Original file line number Diff line number Diff line change
@@ -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 & {
Expand Down
File renamed without changes.
File renamed without changes.
Loading
Loading