From ac8cf918a473e4672604a24c86b2791d6e586933 Mon Sep 17 00:00:00 2001 From: Konstantin Zhigaylo Date: Sat, 13 Sep 2025 18:58:24 +0300 Subject: [PATCH 01/20] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D1=82=D0=B0=20ClubInfo=20=D0=B8=20=D1=80?= =?UTF-8?q?=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20?= =?UTF-8?q?ClubHeader=20=D0=B4=D0=BB=D1=8F=20=D1=83=D0=BB=D1=83=D1=87?= =?UTF-8?q?=D1=88=D0=B5=D0=BD=D0=B8=D1=8F=20=D1=81=D1=82=D1=80=D1=83=D0=BA?= =?UTF-8?q?=D1=82=D1=83=D1=80=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(withMenuNavigation)/(home)/Home.tsx | 40 +---- .../(withMenuNavigation)/(home)/HomeFeed.tsx | 42 +++++ .../(withMenuNavigation)/(home)/page.tsx | 24 +-- .../(withMenuNavigation)/clubs/[id]/Club.tsx | 10 +- .../(withMenuNavigation)/clubs/[id]/page.tsx | 15 +- .../profile/UserProfile.tsx | 85 ---------- .../profile/[id]/Profile.tsx | 63 +++---- .../profile/[id]/UserProfileClubs.tsx | 35 ++++ .../profile/[id]/clubs/ProfileClubs.tsx | 27 +-- .../profile/[id]/clubs/page.tsx | 23 ++- .../profile/[id]/page.tsx | 22 ++- .../profile/clubs/UserProfileClubs.tsx | 45 ----- .../profile/clubs/page.tsx | 12 -- .../(withMenuNavigation)/profile/page.tsx | 15 +- src/components/Badges.tsx | 10 +- src/components/ClubComponents/ClubHeader.tsx | 157 +++++------------- src/components/ClubComponents/ClubInfo.tsx | 72 ++++++++ .../SubscribeButtons/SubscribeButton.tsx | 2 + src/components/ImageLoader/ImageLoader.tsx | 2 + .../MenuNavigation/MenuNavigation.tsx | 2 +- src/components/PostCard/PostCard.tsx | 7 +- .../PostCard/PostHeader/MoreDropList.tsx | 11 +- .../PostCard/PostHeader/PostHeader.tsx | 5 +- .../PostCard/PostHeader/PostReportDialog.tsx | 10 +- .../PostCard/store/useReportPostStore.ts | 17 -- src/lib/config/routes.config.ts | 4 +- .../actions/getPersonIdFromToken.action.ts | 28 ++++ 27 files changed, 350 insertions(+), 435 deletions(-) create mode 100644 src/app/(auth)/(withMenuNavigation)/(home)/HomeFeed.tsx delete mode 100644 src/app/(auth)/(withMenuNavigation)/profile/UserProfile.tsx create mode 100644 src/app/(auth)/(withMenuNavigation)/profile/[id]/UserProfileClubs.tsx delete mode 100644 src/app/(auth)/(withMenuNavigation)/profile/clubs/UserProfileClubs.tsx delete mode 100644 src/app/(auth)/(withMenuNavigation)/profile/clubs/page.tsx create mode 100644 src/components/ClubComponents/ClubInfo.tsx delete mode 100644 src/components/PostCard/store/useReportPostStore.ts create mode 100644 src/server-actions/actions/getPersonIdFromToken.action.ts diff --git a/src/app/(auth)/(withMenuNavigation)/(home)/Home.tsx b/src/app/(auth)/(withMenuNavigation)/(home)/Home.tsx index d49a58b..3fe85e1 100644 --- a/src/app/(auth)/(withMenuNavigation)/(home)/Home.tsx +++ b/src/app/(auth)/(withMenuNavigation)/(home)/Home.tsx @@ -1,33 +1,11 @@ -'use client'; - -import dynamic from 'next/dynamic'; - -import Loader from '@/components/Loader'; import { Page } from '@/components/Page'; -import { Skeleton } from '@/components/ui/skeleton'; - -import { useInfinityScroll } from '@/hooks/useInfinityScroll'; - -import { feedApi } from '@/api/api'; +import HomeFeed from '@/app/(auth)/(withMenuNavigation)/(home)/HomeFeed'; import { Header, HeaderTitle } from '@/hoc/Header/Header'; import { MainContent } from '@/hoc/MainContent/MainContent'; import { parseLocalTime } from '@/lib/utils/time.util'; -const PostCardDynamic = dynamic(() => import('@/components/PostCard/PostCard').then((mod) => mod.PostCard), { - loading: () => , -}); - export default function Home({ timeBasedGreeting }: { timeBasedGreeting: string }) { - const { - ref, - infiniteQuery: { data, isLoading, isFetchingNextPage, hasNextPage, error }, - } = useInfinityScroll({ - queryKey: ['fetch-feed-posts'], - queryFn: async ({ pageParam = 0 }) => (await feedApi.feedGetFeedPosts(pageParam, 10)).data, - pageSize: 10, - }); - return (
@@ -36,22 +14,8 @@ export default function Home({ timeBasedGreeting }: { timeBasedGreeting: string {parseLocalTime(Date(), { day: 'numeric', month: 'short' })}

- -
- {data && - data.pages.flatMap((page) => page).map((post) => )} - {(isFetchingNextPage || isLoading) && } - {!hasNextPage && !isLoading && ( -

Ваша лента закончилась

- )} - {error && ( -

- Не удалось выполнить загрузку постов. Пожалуйста, повторите позже. -

- )} -
-
+
); diff --git a/src/app/(auth)/(withMenuNavigation)/(home)/HomeFeed.tsx b/src/app/(auth)/(withMenuNavigation)/(home)/HomeFeed.tsx new file mode 100644 index 0000000..39d1dfd --- /dev/null +++ b/src/app/(auth)/(withMenuNavigation)/(home)/HomeFeed.tsx @@ -0,0 +1,42 @@ +'use client'; + +import dynamic from 'next/dynamic'; + +import Loader from '@/components/Loader'; +import { Skeleton } from '@/components/ui/skeleton'; + +import { useInfinityScroll } from '@/hooks/useInfinityScroll'; + +import { feedApi } from '@/api/api'; + +const PostCardDynamic = dynamic(() => import('@/components/PostCard/PostCard').then((mod) => mod.PostCard), { + loading: () => , + ssr: false, +}); + +export default function HomeFeed() { + const { + ref, + infiniteQuery: { data, isLoading, isFetchingNextPage, hasNextPage, error }, + } = useInfinityScroll({ + queryKey: ['fetch-feed-posts'], + queryFn: async ({ pageParam = 0 }) => (await feedApi.feedGetFeedPosts(pageParam, 10)).data, + pageSize: 10, + }); + + return ( +
+ {data && data.pages.flatMap((page) => page).map((post) => )} + {(isFetchingNextPage || isLoading) && } + {!hasNextPage && !isLoading && ( +

Ваша лента закончилась

+ )} + {error && ( +

+ Не удалось выполнить загрузку постов. Пожалуйста, повторите позже. +

+ )} +
+
+ ); +} diff --git a/src/app/(auth)/(withMenuNavigation)/(home)/page.tsx b/src/app/(auth)/(withMenuNavigation)/(home)/page.tsx index 221d094..9879456 100644 --- a/src/app/(auth)/(withMenuNavigation)/(home)/page.tsx +++ b/src/app/(auth)/(withMenuNavigation)/(home)/page.tsx @@ -7,20 +7,20 @@ export const metadata: Metadata = { description: 'Главная страница', }; -export default async function HomePage() { - const getTimeBasedGreeting = (): string => { - const currentHour = new Date().getHours(); +const getTimeBasedGreeting = (): string => { + const currentHour = new Date().getHours(); - if (currentHour >= 5 && currentHour < 12) { - return 'Доброе утро'; - } else if (currentHour >= 12 && currentHour < 18) { - return 'Добрый день'; - } else if (currentHour >= 18 && currentHour < 23) { - return 'Добрый вечер'; - } + if (currentHour >= 5 && currentHour < 12) { + return 'Доброе утро'; + } else if (currentHour >= 12 && currentHour < 18) { + return 'Добрый день'; + } else if (currentHour >= 18 && currentHour < 23) { + return 'Добрый вечер'; + } - return 'Доброй ночи'; - }; + return 'Доброй ночи'; +}; +export default function HomePage() { return ; } diff --git a/src/app/(auth)/(withMenuNavigation)/clubs/[id]/Club.tsx b/src/app/(auth)/(withMenuNavigation)/clubs/[id]/Club.tsx index 887a9ae..de3fcda 100644 --- a/src/app/(auth)/(withMenuNavigation)/clubs/[id]/Club.tsx +++ b/src/app/(auth)/(withMenuNavigation)/clubs/[id]/Club.tsx @@ -1,21 +1,19 @@ -'use client'; - import dynamic from 'next/dynamic'; import { ClubFeed } from '@/components/ClubComponents/ClubFeed'; import { ClubHeader } from '@/components/ClubComponents/ClubHeader'; +import ClubInfo from '@/components/ClubComponents/ClubInfo'; import { Page } from '@/components/Page'; import type { ClubDetailDTO } from '@/api/axios-client/models/club-detail-dto'; -const ClubReportDialogDynamic = dynamic(() => import('@/components/ClubComponents/ClubReportDialog'), { - ssr: false, -}); +const ClubReportDialogDynamic = dynamic(() => import('@/components/ClubComponents/ClubReportDialog')); export function Club({ club }: { club: ClubDetailDTO }) { return (
- + + diff --git a/src/app/(auth)/(withMenuNavigation)/clubs/[id]/page.tsx b/src/app/(auth)/(withMenuNavigation)/clubs/[id]/page.tsx index af6e132..ed8b0b5 100644 --- a/src/app/(auth)/(withMenuNavigation)/clubs/[id]/page.tsx +++ b/src/app/(auth)/(withMenuNavigation)/clubs/[id]/page.tsx @@ -1,7 +1,14 @@ +import { notFound } from 'next/navigation'; + +import { clubsApi } from '@/api/api'; +import type { ClubDetailDTO } from '@/api/axios-client'; + import { Club } from './Club'; -import ClubNotFound from './ClubNotFound'; import { getClubGetByIdAction } from '@/server-actions/actions/clubs.action'; +export const dynamic = 'force-dynamic'; +export const revalidate = 60; + export async function generateMetadata({ params }: { params: Promise<{ id: string }> }) { try { const id = (await params).id; @@ -20,11 +27,11 @@ export async function generateMetadata({ params }: { params: Promise<{ id: strin export default async function Page(props: { params: Promise<{ id: string }> }) { const id = (await props.params).id; - try { - const club = await getClubGetByIdAction(id); + const club: ClubDetailDTO = (await clubsApi.clubsGetById(Number(id))).data; + console.log(club); return ; } catch { - return ; + return notFound(); } } diff --git a/src/app/(auth)/(withMenuNavigation)/profile/UserProfile.tsx b/src/app/(auth)/(withMenuNavigation)/profile/UserProfile.tsx deleted file mode 100644 index 98fba34..0000000 --- a/src/app/(auth)/(withMenuNavigation)/profile/UserProfile.tsx +++ /dev/null @@ -1,85 +0,0 @@ -'use client'; - -import { useQuery } from '@tanstack/react-query'; -import Link from 'next/link'; -import { BiMessageSquare, BiSolidIdCard } from 'react-icons/bi'; - -import { SettingBadge } from '@/components/Badges'; -import { ClubCard } from '@/components/ClubComponents/ClubCard'; -import { Page } from '@/components/Page'; -import { Avatar } from '@/components/ui/Avatar'; -import { SkeletonList } from '@/components/ui/skeleton'; - -import { AUTH_PAGE } from '@/lib/config/routes.config'; - -import { useProfile } from '@/hooks/useProfile'; - -import { userApi } from '@/api/api'; - -import { Header, HeaderTitle } from '@/hoc/Header/Header'; -import { MainContent } from '@/hoc/MainContent/MainContent'; - -export default function UserProfile() { - const { - data: userClubs, - isLoading, - error, - } = useQuery({ - queryKey: ['fetch-profile-user-clubs'], - queryFn: async () => (await userApi.userGetSubscribedClubs(0, 3)).data, - }); - const { data: user } = useProfile(); - - return ( - -
- Профиль - - - -
- - -
-
- -
-

- {user?.firstName} {user?.lastName} -

-

был недавно

-
-
-
-
- -

- {user?.about || '...'} -

-
-
- -

- {user?.institute?.name || 'Нет института'} -

-
-
-
-
-
-

Подписки

- - Показать все - -
- {isLoading && } - {!isLoading && - userClubs && - userClubs.length > 0 && - userClubs.map((club) => )} - {error &&

Не удалось загрузить ваши подписки

} -
-
-
- ); -} diff --git a/src/app/(auth)/(withMenuNavigation)/profile/[id]/Profile.tsx b/src/app/(auth)/(withMenuNavigation)/profile/[id]/Profile.tsx index ffcf35a..868ad64 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/[id]/Profile.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/[id]/Profile.tsx @@ -1,13 +1,8 @@ -'use client'; - -import { useQuery } from '@tanstack/react-query'; import { Copy, EllipsisVertical, OctagonAlert } from 'lucide-react'; import Link from 'next/link'; -import { usePathname } from 'next/navigation'; -import toast from 'react-hot-toast'; import { BiMessageSquare, BiSolidIdCard } from 'react-icons/bi'; +import { IoSettingsSharp } from 'react-icons/io5'; -import { ClubCard } from '@/components/ClubComponents/ClubCard'; import { Page } from '@/components/Page'; import { Avatar } from '@/components/ui/Avatar'; import { BackButton } from '@/components/ui/BackButton'; @@ -18,36 +13,34 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; -import { SkeletonList } from '@/components/ui/skeleton'; import { AUTH_PAGE } from '@/lib/config/routes.config'; -import { clubsApi } from '@/api/api'; import type { PersonDetailDTO } from '@/api/axios-client/models'; +import UserProfileClubs from '@/app/(auth)/(withMenuNavigation)/profile/[id]/UserProfileClubs'; import { Header } from '@/hoc/Header/Header'; import { MainContent } from '@/hoc/MainContent/MainContent'; type Props = { user: PersonDetailDTO; + isCurrent: boolean; }; -export default function Profile({ user }: Props) { - const { - data: userClubs, - isLoading, - error, - } = useQuery({ - queryKey: [user.id, 'fetch-profile-user-clubs'], - queryFn: async () => (await clubsApi.clubsGetByPersonId(user.id, 0, 3)).data, - }); - - const pathname = usePathname(); - +export default function Profile({ user, isCurrent }: Props) { return ( -
+
+

Профиль

+ + {isCurrent && ( + + + + )} - { - navigator.clipboard.writeText(`https://setka-rtu.ru${pathname}`); - toast.success('Ссылка скопирована'); - }} - > + Скопировать ссылку - - - Пожаловаться - + {!isCurrent && ( + + + Пожаловаться + + )}
@@ -101,18 +91,11 @@ export default function Profile({ user }: Props) {

Подписки

- + Показать все
- {isLoading && } - {!isLoading && - userClubs && - userClubs.length > 0 && - userClubs.map((club) => )} - {error && ( -

Не удалось загрузить подписки пользователя

- )} +
diff --git a/src/app/(auth)/(withMenuNavigation)/profile/[id]/UserProfileClubs.tsx b/src/app/(auth)/(withMenuNavigation)/profile/[id]/UserProfileClubs.tsx new file mode 100644 index 0000000..ab959c3 --- /dev/null +++ b/src/app/(auth)/(withMenuNavigation)/profile/[id]/UserProfileClubs.tsx @@ -0,0 +1,35 @@ +'use client'; + +import { useQuery } from '@tanstack/react-query'; +import dynamic from 'next/dynamic'; +import { Fragment } from 'react'; + +import { SkeletonList } from '@/components/ui/skeleton'; + +import { clubsApi } from '@/api/api'; + +const ClubCardDynamic = dynamic(() => import('@/components/ClubComponents/ClubCard').then((mod) => mod.ClubCard), { + ssr: false, +}); + +export default function UserProfileClubs({ id }: { id: number }) { + const { + data: userClubs, + isLoading, + error, + } = useQuery({ + queryKey: [id, 'fetch-profile-user-clubs'], + queryFn: async () => (await clubsApi.clubsGetByPersonId(id, 0, 3)).data, + }); + + return ( + + {isLoading && } + {!isLoading && + userClubs && + userClubs.length > 0 && + userClubs.map((club) => )} + {error &&

Не удалось загрузить подписки пользователя

} +
+ ); +} diff --git a/src/app/(auth)/(withMenuNavigation)/profile/[id]/clubs/ProfileClubs.tsx b/src/app/(auth)/(withMenuNavigation)/profile/[id]/clubs/ProfileClubs.tsx index 3257f6b..a46cbd0 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/[id]/clubs/ProfileClubs.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/[id]/clubs/ProfileClubs.tsx @@ -3,7 +3,6 @@ import { useMemo } from 'react'; import { ClubCard } from '@/components/ClubComponents/ClubCard'; -import { BackButton } from '@/components/ui/BackButton'; import { SkeletonList } from '@/components/ui/skeleton'; import { useInfinityScroll } from '@/hooks/useInfinityScroll'; @@ -11,8 +10,6 @@ import { useInfinityScroll } from '@/hooks/useInfinityScroll'; import { clubsApi } from '@/api/api'; import type { PersonDetailDTO } from '@/api/axios-client'; -import { Header, HeaderTitle } from '@/hoc/Header/Header'; - export default function ProfileClubs({ user }: { user: PersonDetailDTO }) { const { ref, @@ -26,22 +23,14 @@ export default function ProfileClubs({ user }: { user: PersonDetailDTO }) { const userClubs = useMemo(() => (clubs ? clubs.pages.flatMap((page) => page) : []), [clubs]); return ( -
-
- - - {user.firstName} {user.lastName}: подписки - -
-
- {(isFetchingNextPage || isLoading) && } - {!isLoading && userClubs.length === 0 ? ( -

Нет ни одной подписки

- ) : ( - userClubs.map((club) => ) - )} -
-
+
+ {(isFetchingNextPage || isLoading) && } + {!isLoading && userClubs.length === 0 ? ( +

Нет ни одной подписки

+ ) : ( + userClubs.map((club) => ) + )} +
); } diff --git a/src/app/(auth)/(withMenuNavigation)/profile/[id]/clubs/page.tsx b/src/app/(auth)/(withMenuNavigation)/profile/[id]/clubs/page.tsx index cd395f9..58c8e90 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/[id]/clubs/page.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/[id]/clubs/page.tsx @@ -1,18 +1,35 @@ import type { Metadata } from 'next'; +import { Page } from '@/components/Page'; +import { BackButton } from '@/components/ui/BackButton'; + import { usersApi } from '@/api/api'; import type { PersonDetailDTO } from '@/api/axios-client'; import ProfileClubs from './ProfileClubs'; +import { Header, HeaderTitle } from '@/hoc/Header/Header'; +import { MainContent } from '@/hoc/MainContent/MainContent'; export const metadata: Metadata = { title: 'Подписки', - description: '', + description: 'Подписки пользователя', }; -export default async function Page({ params }: { params: Promise<{ id: number }> }) { +export default async function ClubsPage({ params }: { params: Promise<{ id: number }> }) { const { id } = await params; const user: PersonDetailDTO = (await usersApi.usersGetById(id)).data; - return ; + return ( + +
+ + + {user.firstName} {user.lastName}: подписки + +
+ + + +
+ ); } diff --git a/src/app/(auth)/(withMenuNavigation)/profile/[id]/page.tsx b/src/app/(auth)/(withMenuNavigation)/profile/[id]/page.tsx index 302fda4..df26689 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/[id]/page.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/[id]/page.tsx @@ -1,24 +1,30 @@ import type { Metadata } from 'next'; -import { redirect } from 'next/navigation'; -import { AUTH_PAGE } from '@/lib/config/routes.config'; - -import { userApi, usersApi } from '@/api/api'; +import { usersApi } from '@/api/api'; import type { PersonDetailDTO } from '@/api/axios-client'; import Profile from './Profile'; +import { getPersonIdFromToken } from '@/server-actions/actions/getPersonIdFromToken.action'; + +export const dynamic = 'force-dynamic'; +export const revalidate = 60; export const metadata: Metadata = { title: 'Профиль', - description: '', + description: 'Профиль пользователя', }; export default async function Page({ params }: { params: Promise<{ id: number }> }) { const { id } = await params; const user: PersonDetailDTO = (await usersApi.usersGetById(id)).data; - const currentUser: PersonDetailDTO = (await userApi.userGetPersonalDetails()).data; - if (currentUser.id === user.id) redirect(AUTH_PAGE.PROFILE); + let currentPerson = await getPersonIdFromToken(); + if (!currentPerson) { + console.log('Не удалось получить текущего пользователя из токена'); + currentPerson = '0'; + } + + const isCurrent = user.id === Number(currentPerson); - return ; + return ; } diff --git a/src/app/(auth)/(withMenuNavigation)/profile/clubs/UserProfileClubs.tsx b/src/app/(auth)/(withMenuNavigation)/profile/clubs/UserProfileClubs.tsx deleted file mode 100644 index 4791aa4..0000000 --- a/src/app/(auth)/(withMenuNavigation)/profile/clubs/UserProfileClubs.tsx +++ /dev/null @@ -1,45 +0,0 @@ -'use client'; - -import { useMemo } from 'react'; - -import { ClubCard } from '@/components/ClubComponents/ClubCard'; -import { Page } from '@/components/Page'; -import { BackButton } from '@/components/ui/BackButton'; -import { SkeletonList } from '@/components/ui/skeleton'; - -import { useInfinityScroll } from '@/hooks/useInfinityScroll'; - -import { userApi } from '@/api/api'; - -import { Header, HeaderTitle } from '@/hoc/Header/Header'; - -export default function UserProfileClubs() { - const { - ref, - infiniteQuery: { data: clubs, isLoading, isFetchingNextPage }, - } = useInfinityScroll({ - queryKey: ['fetch-user-clubs'], - queryFn: async (page) => (await userApi.userGetSubscribedClubs(page.pageParam, 12)).data, - pageSize: 12, - }); - - const userClubs = useMemo(() => (clubs ? clubs.pages.flatMap((page) => page) : []), [clubs]); - - return ( - -
- - Подписки -
-
- {(isFetchingNextPage || isLoading) && } - {!isLoading && userClubs.length === 0 ? ( -

Вы пока не подписаны ни на один клуб

- ) : ( - userClubs.map((club) => ) - )} -
-
-
- ); -} diff --git a/src/app/(auth)/(withMenuNavigation)/profile/clubs/page.tsx b/src/app/(auth)/(withMenuNavigation)/profile/clubs/page.tsx deleted file mode 100644 index a84a105..0000000 --- a/src/app/(auth)/(withMenuNavigation)/profile/clubs/page.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import type { Metadata } from 'next'; - -import ProfileClubs from './UserProfileClubs'; - -export const metadata: Metadata = { - title: 'Ваши подписки', - description: '', -}; - -export default function Page() { - return ; -} diff --git a/src/app/(auth)/(withMenuNavigation)/profile/page.tsx b/src/app/(auth)/(withMenuNavigation)/profile/page.tsx index 46ccecb..d325c69 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/page.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/page.tsx @@ -1,12 +1,13 @@ -import type { Metadata } from 'next'; +import { redirect } from 'next/navigation'; -import Profile from './UserProfile'; +import { AUTH_PAGE } from '@/lib/config/routes.config'; -export const metadata: Metadata = { - title: 'Ваш профиль', - description: '', -}; +import { getPersonIdFromToken } from '@/server-actions/actions/getPersonIdFromToken.action'; export default async function Page() { - return ; + const currentPerson = await getPersonIdFromToken(); + if (!currentPerson) { + redirect('/'); + } + redirect(AUTH_PAGE.USER_PROFILE(Number(currentPerson))); } diff --git a/src/components/Badges.tsx b/src/components/Badges.tsx index dc48d5a..9c0317b 100644 --- a/src/components/Badges.tsx +++ b/src/components/Badges.tsx @@ -11,14 +11,6 @@ function BadgeWrapper({ children, ...props }: TBadgeWrapper) { ); } -function SettingBadge({ ...props }: HTMLAttributes) { - return ( - - - - ); -} - function CalendarBadge({ ...props }: HTMLAttributes) { return ( @@ -27,4 +19,4 @@ function CalendarBadge({ ...props }: HTMLAttributes) { ); } -export { SettingBadge, BadgeWrapper, CalendarBadge }; +export { BadgeWrapper, CalendarBadge }; diff --git a/src/components/ClubComponents/ClubHeader.tsx b/src/components/ClubComponents/ClubHeader.tsx index fab8fe9..8a04cd2 100644 --- a/src/components/ClubComponents/ClubHeader.tsx +++ b/src/components/ClubComponents/ClubHeader.tsx @@ -1,6 +1,5 @@ 'use client'; -import { useQuery } from '@tanstack/react-query'; import { EllipsisVertical, Settings } from 'lucide-react'; import dynamic from 'next/dynamic'; import Link from 'next/link'; @@ -21,7 +20,6 @@ import { AUTH_PAGE } from '@/lib/config/routes.config'; import { useClubsRole } from '@/hooks/useClubsRole'; -import { clubsApi } from '@/api/api'; import type { ClubDetailDTO } from '@/api/axios-client'; import LoaderImage from '../ImageLoader/ImageLoader'; @@ -32,46 +30,17 @@ import { Skeleton } from '../ui/skeleton'; import { CLUB_ROLES } from '@/lib/enums/club-roles.enum'; import { getStaticImg } from '@/lib/helpers/getStaticImg.helper'; -const SubscribeButtonDynamic = dynamic( - () => import('./SubscribeButtons/SubscribeButton').then((mod) => mod.SubscribeButton), - { - ssr: false, - loading: () => , - } -); - -export function ClubHeader({ initClubData }: { initClubData: ClubDetailDTO }) { +export function ClubHeader({ club }: { club: ClubDetailDTO }) { const pathname = usePathname(); const { checkRole } = useClubsRole(); const reportOpen = useClubReportDialogStore((store) => store.openDialog); - const { data: club } = useQuery({ - queryKey: ['fetch-club', initClubData?.id], - queryFn: async () => (await clubsApi.clubsGetById(Number(initClubData?.id))).data, - initialData: initClubData, - enabled: !!initClubData, - }); - - const displaySubscribers = useMemo(() => { - const subscribers = club?.subscriberCount || 0; - - const lastDigit = subscribers % 10; - const lastTwoDigits = subscribers % 100; - - if (lastTwoDigits >= 11 && lastTwoDigits <= 19) return `подписчиков`; - if (lastDigit === 1) return `подписчик`; - if (lastDigit >= 2 && lastDigit <= 4) return ` подписчика`; - - return `подписчиков`; - }, [club?.subscriberCount]); - return ( - <> -
-
-
- - {/*

+

+
+ + {/*

{ navigator.clipboard.writeText('@IKB_MIREA'); @@ -80,84 +49,44 @@ export function ClubHeader({ initClubData }: { initClubData: ClubDetailDTO }) { > @IKB_MIREA

*/} -
-
- {checkRole(initClubData.id) === CLUB_ROLES.OWNER && ( - - - - )} - - - - - - - { - navigator.clipboard.writeText(`https://setka-rtu.ru${pathname}`); - toast.success('Ссылка скопирована'); - }} - className="text-text" - > - - Скопировать ссылку - - reportOpen(club.id)}> - - Пожаловаться - - - -
-
-
- -
- -
- -
-
-

{club?.name}

-

{club?.about}

- - - {club?.subscriberCount} - {' '} - {displaySubscribers} - +
+ {checkRole(club?.id) === CLUB_ROLES.OWNER && ( + + + + )} + + + + + + + { + navigator.clipboard.writeText(`https://setka-rtu.ru${pathname}`); + toast.success('Ссылка скопирована'); + }} + className="text-text" + > + + Скопировать ссылку + + reportOpen(club.id)}> + + Пожаловаться + + +
- - {/*
*/} - {/* */} - {/*
*/} -
- +
+
); } diff --git a/src/components/ClubComponents/ClubInfo.tsx b/src/components/ClubComponents/ClubInfo.tsx new file mode 100644 index 0000000..37f5580 --- /dev/null +++ b/src/components/ClubComponents/ClubInfo.tsx @@ -0,0 +1,72 @@ +import dynamic from 'next/dynamic'; +import Link from 'next/link'; + +import LoaderImage from '@/components/ImageLoader/ImageLoader'; +import { Skeleton } from '@/components/ui/skeleton'; + +import type { ClubDetailDTO } from '@/api/axios-client'; + +import { getStaticImg } from '@/lib/helpers/getStaticImg.helper'; + +const SubscribeButtonDynamic = dynamic( + () => import('./SubscribeButtons/SubscribeButton').then((mod) => mod.SubscribeButton), + { + loading: () => , + } +); + +export default function ClubInfo({ club }: { club: ClubDetailDTO }) { + const displaySubscribers = () => { + const subscribers = club?.subscriberCount || 0; + + const lastDigit = subscribers % 10; + const lastTwoDigits = subscribers % 100; + + if (lastTwoDigits >= 11 && lastTwoDigits <= 19) return `подписчиков`; + if (lastDigit === 1) return `подписчик`; + if (lastDigit >= 2 && lastDigit <= 4) return ` подписчика`; + + return `подписчиков`; + }; + return ( +
+
+ +
+ +
+ + +
+
+

{club?.name}

+

{club?.about}

+ + {club?.subscriberCount}{' '} + {displaySubscribers()} + +
+ + {/*
*/} + {/* */} + {/*
*/} +
+ ); +} diff --git a/src/components/ClubComponents/SubscribeButtons/SubscribeButton.tsx b/src/components/ClubComponents/SubscribeButtons/SubscribeButton.tsx index b9852a4..ad5a9af 100644 --- a/src/components/ClubComponents/SubscribeButtons/SubscribeButton.tsx +++ b/src/components/ClubComponents/SubscribeButtons/SubscribeButton.tsx @@ -1,3 +1,5 @@ +'use client'; + import { useMutation, useQueryClient } from '@tanstack/react-query'; import type { VariantProps } from 'class-variance-authority'; import { SquareCheck, SquarePlus } from 'lucide-react'; diff --git a/src/components/ImageLoader/ImageLoader.tsx b/src/components/ImageLoader/ImageLoader.tsx index 57c8904..934ef3e 100644 --- a/src/components/ImageLoader/ImageLoader.tsx +++ b/src/components/ImageLoader/ImageLoader.tsx @@ -1,3 +1,5 @@ +'use client'; + import Image, { type ImageProps } from 'next/image'; import { useState } from 'react'; diff --git a/src/components/MenuNavigation/MenuNavigation.tsx b/src/components/MenuNavigation/MenuNavigation.tsx index e3009a9..865aca2 100644 --- a/src/components/MenuNavigation/MenuNavigation.tsx +++ b/src/components/MenuNavigation/MenuNavigation.tsx @@ -12,7 +12,7 @@ export function MenuNavigation() { const pathname = usePathname(); return ( -
+
{MENU_LINKS.map(({ Icon, link }, index) => ( diff --git a/src/components/PostCard/PostCard.tsx b/src/components/PostCard/PostCard.tsx index 35bb61d..3277a21 100644 --- a/src/components/PostCard/PostCard.tsx +++ b/src/components/PostCard/PostCard.tsx @@ -23,6 +23,7 @@ const PostReportDialog = dynamic(() => import('./PostHeader/PostReportDialog').t export function PostCard({ className, post }: PostCardProps) { const [showFull, setShowFull] = useState(false); + const [showReport, setShowReport] = useState(false); const isLong = post.content.length > 356; const displayText = !showFull && isLong ? truncateText(post.content, 200) : post.content; @@ -30,7 +31,7 @@ export function PostCard({ className, post }: PostCardProps) { return ( <>
- +

{post.title}

@@ -39,7 +40,7 @@ export function PostCard({ className, post }: PostCardProps) {

{((!showFull && isLong) || (showFull && isLong)) && (
{/* FIXME Мне кажется что это сильно будет бить по загрузке страницы */} - + ); } diff --git a/src/components/PostCard/PostHeader/MoreDropList.tsx b/src/components/PostCard/PostHeader/MoreDropList.tsx index 3f6f918..04df5d5 100644 --- a/src/components/PostCard/PostHeader/MoreDropList.tsx +++ b/src/components/PostCard/PostHeader/MoreDropList.tsx @@ -16,10 +16,13 @@ import type { PostDetailDTO } from '@/api/axios-client/models'; import { usePostReportDialogStore } from '../store/useReportPostStore'; -export function MoreDropList({ post }: { post: PostDetailDTO }) { - const router = useRouter(); - const postReportOpen = usePostReportDialogStore((store) => store.openDialog); +type Props = { + post: PostDetailDTO; + onOpenChange: (state: boolean) => void; +}; +export function MoreDropList({ post, onOpenChange }: Props) { + const router = useRouter(); return ( @@ -52,7 +55,7 @@ export function MoreDropList({ post }: { post: PostDetailDTO }) { Поделиться {/* TODO свзяать с постом */} - postReportOpen(0)}> + onOpenChange(true)}> Пожаловаться diff --git a/src/components/PostCard/PostHeader/PostHeader.tsx b/src/components/PostCard/PostHeader/PostHeader.tsx index 43810bd..7ba1989 100644 --- a/src/components/PostCard/PostHeader/PostHeader.tsx +++ b/src/components/PostCard/PostHeader/PostHeader.tsx @@ -13,9 +13,10 @@ import { parseLocalDate } from '@/lib/utils/time.util'; type Props = { post: PostDetailDTO; + onOpenChange: (state: boolean) => void; }; -export function PostHeader({ post }: Props) { +export function PostHeader({ post, onOpenChange }: Props) { return (
@@ -30,7 +31,7 @@ export function PostHeader({ post }: Props) {
- +
); } diff --git a/src/components/PostCard/PostHeader/PostReportDialog.tsx b/src/components/PostCard/PostHeader/PostReportDialog.tsx index c9b1dbc..2c359fe 100644 --- a/src/components/PostCard/PostHeader/PostReportDialog.tsx +++ b/src/components/PostCard/PostHeader/PostReportDialog.tsx @@ -6,12 +6,14 @@ import { ResponsiveDialog } from '@/components/ui/responsive-dialog'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Textarea } from '@/components/ui/textarea'; -export default function PostReportDialog() { - const postDialogOpen = usePostReportDialogStore((store) => store.open); - const postDialogOnOpenChange = usePostReportDialogStore((store) => store.onOpenChange); +type Props = { + open: boolean; + onOpenChange: (state: boolean) => void; +}; +export default function PostReportDialog({ open, onOpenChange }: Props) { return ( - + Пожаловаться на пост diff --git a/src/components/PostCard/store/useReportPostStore.ts b/src/components/PostCard/store/useReportPostStore.ts deleted file mode 100644 index 5b77dfa..0000000 --- a/src/components/PostCard/store/useReportPostStore.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { create } from 'zustand'; - -interface IPostReportDialogStore { - open: boolean; - postId: number | null; - onOpenChange: (open: boolean) => void; - openDialog: (postId: number) => void; - closeDialog: () => void; -} - -export const usePostReportDialogStore = create((set) => ({ - open: false, - onOpenChange: (open) => set({ open }), - postId: null, - openDialog: (postId) => set({ open: true, postId }), - closeDialog: () => set({ open: false, postId: null }), -})); diff --git a/src/lib/config/routes.config.ts b/src/lib/config/routes.config.ts index 1a13e40..0c98bdc 100644 --- a/src/lib/config/routes.config.ts +++ b/src/lib/config/routes.config.ts @@ -9,7 +9,7 @@ class AuthPage { PROFILE_CREATE_CLUB = `${this.PROFILE_SETTINGS}/create-club`; CLUBS = '/clubs'; - SETTING_CLUB =(clubId: number) => this.PROFILE_SETTINGS + `/club/${clubId}`; + SETTING_CLUB = (clubId: number) => this.PROFILE_SETTINGS + `/club/${clubId}`; POST_COMMENTS = (id: number | string) => `/post/comments/${id}`; CLUB = (clubId: number | string) => `${this.CLUBS}/${clubId}`; @@ -17,7 +17,7 @@ class AuthPage { EVENT = (id: number | string) => `${this.EVENTS}/${id}`; EVENTS_CALENDAR = () => `${this.EVENTS}/calendar`; USER_PROFILE = (user_id: number | string) => `${this.PROFILE}/${user_id}`; - + USER_PROFILE_CLUBS = (user_id: number | string) => `${this.PROFILE}/${user_id}/clubs`; } class PublicPage { diff --git a/src/server-actions/actions/getPersonIdFromToken.action.ts b/src/server-actions/actions/getPersonIdFromToken.action.ts new file mode 100644 index 0000000..ea85d41 --- /dev/null +++ b/src/server-actions/actions/getPersonIdFromToken.action.ts @@ -0,0 +1,28 @@ +'use server'; + +import { jwtVerify } from 'jose'; +import { cookies } from 'next/headers'; + +export async function getPersonIdFromToken(): Promise { + try { + const cookieStore = await cookies(); + const token = cookieStore.get('AccessToken')?.value; + if (!token) { + console.warn('Токен не найден в куках'); + return null; + } + + const secretKey = new TextEncoder().encode(process.env.JWT_SECRET); + const { payload } = await jwtVerify(token, secretKey); + const personId = payload.personId as string; + if (!personId) { + console.warn('personId не найден в токене'); + return null; + } + + return personId; + } catch (error) { + console.error('Ошибка при получении ID пользователя из токена:', error); + return null; + } +} From f8855bebd2c082a144cdd5f8d70f8377a769be5e Mon Sep 17 00:00:00 2001 From: Konstantin Zhigaylo Date: Sat, 13 Sep 2025 19:32:02 +0300 Subject: [PATCH 02/20] =?UTF-8?q?feat:=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA?= =?UTF-8?q?=D0=B8=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B5=D0=BD=D0=B8=D1=8F?= =?UTF-8?q?=20ID=20=D0=B8=D0=B7=20=D1=82=D0=BE=D0=BA=D0=B5=D0=BD=D0=B0=20?= =?UTF-8?q?=D0=B8=20=D1=80=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE=D1=80=D0=B8?= =?UTF-8?q?=D0=BD=D0=B3=20=D0=B2=D1=8B=D0=B7=D0=BE=D0=B2=D0=B0=20API=20?= =?UTF-8?q?=D0=BA=D0=BB=D1=83=D0=B1=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(auth)/(withMenuNavigation)/clubs/[id]/page.tsx | 3 +-- .../actions/getPersonIdFromToken.action.ts | 9 ++++++--- src/server-actions/middleware/utils/jwt-verify.ts | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/app/(auth)/(withMenuNavigation)/clubs/[id]/page.tsx b/src/app/(auth)/(withMenuNavigation)/clubs/[id]/page.tsx index ed8b0b5..1829d3f 100644 --- a/src/app/(auth)/(withMenuNavigation)/clubs/[id]/page.tsx +++ b/src/app/(auth)/(withMenuNavigation)/clubs/[id]/page.tsx @@ -1,6 +1,5 @@ import { notFound } from 'next/navigation'; -import { clubsApi } from '@/api/api'; import type { ClubDetailDTO } from '@/api/axios-client'; import { Club } from './Club'; @@ -28,7 +27,7 @@ export async function generateMetadata({ params }: { params: Promise<{ id: strin export default async function Page(props: { params: Promise<{ id: string }> }) { const id = (await props.params).id; try { - const club: ClubDetailDTO = (await clubsApi.clubsGetById(Number(id))).data; + const club: ClubDetailDTO = await getClubGetByIdAction(id); console.log(club); return ; } catch { diff --git a/src/server-actions/actions/getPersonIdFromToken.action.ts b/src/server-actions/actions/getPersonIdFromToken.action.ts index ea85d41..dfbb987 100644 --- a/src/server-actions/actions/getPersonIdFromToken.action.ts +++ b/src/server-actions/actions/getPersonIdFromToken.action.ts @@ -1,8 +1,9 @@ 'use server'; -import { jwtVerify } from 'jose'; import { cookies } from 'next/headers'; +import { jwtVerifyServer } from '@/server-actions/middleware/utils/jwt-verify'; + export async function getPersonIdFromToken(): Promise { try { const cookieStore = await cookies(); @@ -12,8 +13,10 @@ export async function getPersonIdFromToken(): Promise { return null; } - const secretKey = new TextEncoder().encode(process.env.JWT_SECRET); - const { payload } = await jwtVerify(token, secretKey); + const payload = await jwtVerifyServer(token); + if (!payload) { + return null; + } const personId = payload.personId as string; if (!personId) { console.warn('personId не найден в токене'); diff --git a/src/server-actions/middleware/utils/jwt-verify.ts b/src/server-actions/middleware/utils/jwt-verify.ts index 8a61fa5..75bd20f 100644 --- a/src/server-actions/middleware/utils/jwt-verify.ts +++ b/src/server-actions/middleware/utils/jwt-verify.ts @@ -6,7 +6,7 @@ export async function jwtVerifyServer(accessToken: string) { try { const secretKey = new TextEncoder().encode(process.env.JWT_SECRET); - const { payload }: { payload: unknown } = await jwtVerify(accessToken, secretKey, { + const { payload } = await jwtVerify(accessToken, secretKey, { algorithms: ['HS256'], typ: 'JWT', issuer: process.env.JWT_ISSUER, From 372b69b31a24cfd847062a20747ad6377107b46b Mon Sep 17 00:00:00 2001 From: Konstantin Zhigaylo Date: Sun, 28 Sep 2025 18:33:15 +0300 Subject: [PATCH 03/20] =?UTF-8?q?feat(ClubCard):=20=D0=BD=D0=B5=20=D0=B8?= =?UTF-8?q?=D1=81=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D1=82?= =?UTF-8?q?=D1=8C=20useRouter=20=D0=B8=20=D1=81=D0=B4=D0=B5=D0=BB=D0=B0?= =?UTF-8?q?=D1=82=D1=8C=20=D1=81=D0=B5=D1=80=D0=B2=D0=B5=D1=80=D0=BD=D1=8B?= =?UTF-8?q?=D0=BC=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=BE=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(withMenuNavigation)/(home)/Home.tsx | 8 +- .../(withMenuNavigation)/clubs/Clubs.tsx | 2 +- src/components/ClubComponents/ClubCard.tsx | 79 ++++++++++--------- 3 files changed, 49 insertions(+), 40 deletions(-) diff --git a/src/app/(auth)/(withMenuNavigation)/(home)/Home.tsx b/src/app/(auth)/(withMenuNavigation)/(home)/Home.tsx index 3fe85e1..c3a81c6 100644 --- a/src/app/(auth)/(withMenuNavigation)/(home)/Home.tsx +++ b/src/app/(auth)/(withMenuNavigation)/(home)/Home.tsx @@ -1,3 +1,5 @@ +import { Suspense } from 'react'; + import { Page } from '@/components/Page'; import HomeFeed from '@/app/(auth)/(withMenuNavigation)/(home)/HomeFeed'; @@ -5,6 +7,8 @@ import { Header, HeaderTitle } from '@/hoc/Header/Header'; import { MainContent } from '@/hoc/MainContent/MainContent'; import { parseLocalTime } from '@/lib/utils/time.util'; +export const dynamic = 'force-static'; + export default function Home({ timeBasedGreeting }: { timeBasedGreeting: string }) { return ( @@ -15,7 +19,9 @@ export default function Home({ timeBasedGreeting }: { timeBasedGreeting: string

- + + +
); diff --git a/src/app/(auth)/(withMenuNavigation)/clubs/Clubs.tsx b/src/app/(auth)/(withMenuNavigation)/clubs/Clubs.tsx index 8dd1bd4..5891e7d 100644 --- a/src/app/(auth)/(withMenuNavigation)/clubs/Clubs.tsx +++ b/src/app/(auth)/(withMenuNavigation)/clubs/Clubs.tsx @@ -48,7 +48,7 @@ export function Clubs() { -
+
{isLoading && } {clubs?.pages .flatMap((page) => page) diff --git a/src/components/ClubComponents/ClubCard.tsx b/src/components/ClubComponents/ClubCard.tsx index 03d0b09..d5b7ad5 100644 --- a/src/components/ClubComponents/ClubCard.tsx +++ b/src/components/ClubComponents/ClubCard.tsx @@ -1,7 +1,5 @@ -'use client'; - import Image from 'next/image'; -import { useRouter } from 'next/navigation'; +import Link from 'next/link'; import { AUTH_PAGE } from '@/lib/config/routes.config'; @@ -16,44 +14,49 @@ interface Props { } export function ClubCard({ club, showSubscribe = false }: Props) { - const router = useRouter(); - return ( -
- {`${club.name}'s router.push(AUTH_PAGE.CLUB(club.id))} - /> - -
router.push(AUTH_PAGE.CLUB(club.id))} +
+ -

- {club.name} -

-

- {club.about} -

-
+
+ {`${club.name}'s { + e.currentTarget.src = '/img/default-club-avatar.png'; + }} + /> +
+ +
+

+ {club.name} +

+

+ {club.about || 'Описание отсутствует'} +

+
+ + {showSubscribe && ( - +
+ +
)} -
+ ); } From 6d4c813c391715aafc411281ed92854df2c61158 Mon Sep 17 00:00:00 2001 From: Konstantin Zhigaylo Date: Sun, 28 Sep 2025 18:37:39 +0300 Subject: [PATCH 04/20] =?UTF-8?q?feat(About):=20=D0=BE=D0=BF=D1=82=D0=B8?= =?UTF-8?q?=D0=BC=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D1=81=D1=82=D1=80?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D1=86=D1=8B=20=D0=BE=20=D0=BF=D1=80=D0=B8?= =?UTF-8?q?=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(withMenuNavigation)/profile/settings/about/About.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/(auth)/(withMenuNavigation)/profile/settings/about/About.tsx b/src/app/(auth)/(withMenuNavigation)/profile/settings/about/About.tsx index 6d3de4f..d436cf9 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/settings/about/About.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/settings/about/About.tsx @@ -1,5 +1,3 @@ -'use client'; - import { FaGithub, FaGraduationCap } from 'react-icons/fa'; import LoaderImage from '@/components/ImageLoader/ImageLoader'; @@ -16,6 +14,8 @@ type Developer = { avatar: string; }; +export const dynamic = 'force-static'; + function DeveloperItem({ name, role, avatar }: Developer) { return (
@@ -23,7 +23,7 @@ function DeveloperItem({ name, role, avatar }: Developer) { loaderClassName="w-fit" src={avatar} alt="Avatar" - className="size-12 shrink-0 grow-0 rounded-full" + className="size-12 min-w-12 grow rounded-full" width={128} height={128} /> From 11fcb43060a07ff72b53acaa5fac8d5f8d3165f8 Mon Sep 17 00:00:00 2001 From: Konstantin Zhigaylo Date: Sun, 28 Sep 2025 18:40:29 +0300 Subject: [PATCH 05/20] =?UTF-8?q?feat(Appearance):=20=D0=BE=D0=BF=D1=82?= =?UTF-8?q?=D0=B8=D0=BC=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(withMenuNavigation)/profile/[id]/Profile.tsx | 3 +++ .../profile/settings/appearance/appearance.tsx | 10 ---------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/app/(auth)/(withMenuNavigation)/profile/[id]/Profile.tsx b/src/app/(auth)/(withMenuNavigation)/profile/[id]/Profile.tsx index 868ad64..7dbc1aa 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/[id]/Profile.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/[id]/Profile.tsx @@ -27,6 +27,9 @@ type Props = { isCurrent: boolean; }; +export const dynamic = 'force-dynamic'; +export const revalidate = 60; + export default function Profile({ user, isCurrent }: Props) { return ( diff --git a/src/app/(auth)/(withMenuNavigation)/profile/settings/appearance/appearance.tsx b/src/app/(auth)/(withMenuNavigation)/profile/settings/appearance/appearance.tsx index 8d87633..0a8b3c2 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/settings/appearance/appearance.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/settings/appearance/appearance.tsx @@ -1,7 +1,6 @@ 'use client'; import { useTheme } from 'next-themes'; -import { useEffect, useState } from 'react'; import { IoMoon, IoSettingsSharp, IoSunny } from 'react-icons/io5'; import { Page } from '@/components/Page'; @@ -13,15 +12,6 @@ import { MainContent } from '@/hoc/MainContent/MainContent'; export default function Appearance() { const { theme, setTheme } = useTheme(); - const [mounted, setMounted] = useState(false); - - useEffect(() => { - setMounted(true); - }, []); - - if (!mounted) { - return null; - } const handleThemeChange = (newTheme: string) => { setTheme(newTheme); From 09c56b0863193a5100fcdb9fc0a238a96857f343 Mon Sep 17 00:00:00 2001 From: Konstantin Zhigaylo Date: Sun, 28 Sep 2025 18:46:12 +0300 Subject: [PATCH 06/20] =?UTF-8?q?chore(routes):=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=B8=D0=BB=20=D0=BD=D0=B5=D0=BE=D0=B1=D1=85=D0=BE=D0=B4?= =?UTF-8?q?=D0=B8=D0=BC=D1=8B=D0=B9=20=D0=BF=D1=83=D1=82=D1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/config/routes.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/config/routes.config.ts b/src/lib/config/routes.config.ts index ce4d161..470dd7f 100644 --- a/src/lib/config/routes.config.ts +++ b/src/lib/config/routes.config.ts @@ -4,7 +4,6 @@ class AuthPage { PROFILE = '/profile'; PROFILE_SETTINGS = `${this.PROFILE}/settings`; FINDER = '/finder'; - PROFILE_CLUBS = '/profile/clubs'; POST_DRAFT = '/post/draft'; PROFILE_CREATE_CLUB = `${this.PROFILE_SETTINGS}/create-club`; @@ -17,6 +16,7 @@ class AuthPage { EVENT = (id: number | string) => `${this.EVENTS}/${id}`; EVENTS_CALENDAR = () => `${this.EVENTS}/calendar`; USER_PROFILE = (user_id: number | string) => `${this.PROFILE}/${user_id}`; + USER_PROFILE_CLUBS = (user_id: number | string) => `${this.PROFILE}/${user_id}/clubs`; } class PublicPage { From 3d954f70670f851999563617187a944e139cdb0c Mon Sep 17 00:00:00 2001 From: Konstantin Zhigaylo Date: Sun, 28 Sep 2025 18:51:08 +0300 Subject: [PATCH 07/20] =?UTF-8?q?refactor(PostReportDialog):=20=D1=83?= =?UTF-8?q?=D0=B1=D1=80=D0=B0=D0=BB=20=D0=BD=D0=B5=D0=BD=D1=83=D0=B6=D0=BD?= =?UTF-8?q?=D1=8B=D0=B5=20=D0=B8=D0=BC=D0=BF=D0=BE=D1=80=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/PostCard/PostHeader/MoreDropList.tsx | 2 -- src/components/PostCard/PostHeader/PostReportDialog.tsx | 1 - 2 files changed, 3 deletions(-) diff --git a/src/components/PostCard/PostHeader/MoreDropList.tsx b/src/components/PostCard/PostHeader/MoreDropList.tsx index 04df5d5..21f27e8 100644 --- a/src/components/PostCard/PostHeader/MoreDropList.tsx +++ b/src/components/PostCard/PostHeader/MoreDropList.tsx @@ -14,8 +14,6 @@ import { AUTH_PAGE } from '@/lib/config/routes.config'; import type { PostDetailDTO } from '@/api/axios-client/models'; -import { usePostReportDialogStore } from '../store/useReportPostStore'; - type Props = { post: PostDetailDTO; onOpenChange: (state: boolean) => void; diff --git a/src/components/PostCard/PostHeader/PostReportDialog.tsx b/src/components/PostCard/PostHeader/PostReportDialog.tsx index 2c359fe..f0cd9ea 100644 --- a/src/components/PostCard/PostHeader/PostReportDialog.tsx +++ b/src/components/PostCard/PostHeader/PostReportDialog.tsx @@ -1,6 +1,5 @@ 'use client'; -import { usePostReportDialogStore } from '@/components/PostCard/store/useReportPostStore'; import { Button } from '@/components/ui/button'; import { ResponsiveDialog } from '@/components/ui/responsive-dialog'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; From 3f87b5e2c10010552ad21f47c11bf3ebbc029b70 Mon Sep 17 00:00:00 2001 From: Konstantin Zhigaylo Date: Sun, 28 Sep 2025 19:07:52 +0300 Subject: [PATCH 08/20] =?UTF-8?q?refactor(Comments):=20=D0=BE=D0=BF=D1=82?= =?UTF-8?q?=D0=B8=D0=BC=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D1=81=D1=82?= =?UTF-8?q?=D0=B8=D0=BB=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 164 +++++++++--------- .../(auth)/post/comments/[id]/CommentList.tsx | 2 +- .../post/comments/[id]/CommentsPage.tsx | 2 +- .../[id]/useInfinityComments(disabled).tsx | 35 ---- .../CommentComponents/CommentItem.tsx | 4 +- 5 files changed, 86 insertions(+), 121 deletions(-) delete mode 100644 src/app/(auth)/post/comments/[id]/useInfinityComments(disabled).tsx diff --git a/package.json b/package.json index d22ea42..f1caf5c 100644 --- a/package.json +++ b/package.json @@ -1,84 +1,84 @@ { - "name": "setka-frontend", - "version": "0.1.0", - "private": true, - "scripts": { - "dev": "next dev --turbopack --experimental-https", - "build": "next build", - "start": "next start", - "api:code-gen": "openapi-generator-cli generate --generator-key api && openapi-generator-cli generate --generator-key admin-api", - "api-dev:code-gen": "openapi-generator-cli generate --generator-key api-dev", - "lint": "next lint" - }, - "dependencies": { - "@editorjs/editorjs": "^2.30.8", - "@editorjs/paragraph": "^2.11.7", - "@editorjs/underline": "^1.2.1", - "@hookform/resolvers": "^5.2.1", - "@radix-ui/react-checkbox": "^1.3.3", - "@radix-ui/react-dialog": "^1.1.15", - "@radix-ui/react-dropdown-menu": "^2.1.16", - "@radix-ui/react-popover": "^1.1.15", - "@radix-ui/react-select": "^2.2.6", - "@radix-ui/react-slot": "^1.2.3", - "@tailwindcss/postcss": "^4.1.12", - "@tanstack/react-query": "^5.85.5", - "@uidotdev/usehooks": "^2.4.1", - "axios": "^1.11.0", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "date-fns": "^4.1.0", - "framer-motion": "^12.23.12", - "jose": "^6.0.13", - "js-cookie": "^3.0.5", - "lucide-react": "^0.542.0", - "next": "15.5.2", - "next-themes": "^0.4.6", - "path-to-regexp": "^8.2.0", - "prettier-plugin-tailwindcss": "^0.6.14", - "react": "^19.1.1", - "react-click-away-listener": "^2.4.0", - "react-day-picker": "9.9.0", - "react-dom": "^19.1.1", - "react-hook-form": "^7.62.0", - "react-hot-toast": "^2.6.0", - "react-icons": "^5.5.0", - "react-intersection-observer": "^9.16.0", - "react-mobile-cropper": "^0.10.0", - "swiper": "^11.2.10", - "tailwind-merge": "^3.3.1", - "tw-animate-css": "^1.3.7", - "use-debounce": "^10.0.5", - "vaul": "^1.1.2", - "zod": "^4.1.3", - "zustand": "^5.0.8" - }, - "devDependencies": { - "@eslint/eslintrc": "^3.3.1", - "@next/eslint-plugin-next": "^15.5.2", - "@openapitools/openapi-generator-cli": "^2.23.1", - "@tanstack/eslint-plugin-query": "^5.83.1", - "@trivago/prettier-plugin-sort-imports": "^5.2.2", - "@types/js-cookie": "^3.0.6", - "@types/node": "^24.3.0", - "@types/react": "^19.1.11", - "@types/react-dom": "^19.1.8", - "eslint": "^9.34.0", - "eslint-config-next": "15.5.2", - "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^5.2.0", - "postcss": "^8.5.6", - "prettier": "^3.6.2", - "sass": "^1.91.0", - "tailwindcss": "^4.1.12", - "typescript": "5.9.2" - }, - "packageManager": "pnpm@10.13.1+sha512.37ebf1a5c7a30d5fabe0c5df44ee8da4c965ca0c5af3dbab28c3a1681b70a256218d05c81c9c0dcf767ef6b8551eb5b960042b9ed4300c59242336377e01cfad", - "trustedDependencies": [ - "@nestjs/core", - "@openapitools/openapi-generator-cli", - "@parcel/watcher", - "@tailwindcss/oxide", - "unrs-resolver" - ] + "name": "setka-frontend", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev --turbopack --experimental-https", + "build": "next build", + "start": "next start", + "api:code-gen": "openapi-generator-cli generate --generator-key api && openapi-generator-cli generate --generator-key admin-api", + "api-dev:code-gen": "openapi-generator-cli generate --generator-key api-dev", + "lint": "next lint" + }, + "dependencies": { + "@editorjs/editorjs": "^2.30.8", + "@editorjs/paragraph": "^2.11.7", + "@editorjs/underline": "^1.2.1", + "@hookform/resolvers": "^5.2.1", + "@radix-ui/react-checkbox": "^1.3.3", + "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-dropdown-menu": "^2.1.16", + "@radix-ui/react-popover": "^1.1.15", + "@radix-ui/react-select": "^2.2.6", + "@radix-ui/react-slot": "^1.2.3", + "@tailwindcss/postcss": "^4.1.12", + "@tanstack/react-query": "^5.85.5", + "@uidotdev/usehooks": "^2.4.1", + "axios": "^1.11.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "date-fns": "^4.1.0", + "framer-motion": "^12.23.12", + "jose": "^6.0.13", + "js-cookie": "^3.0.5", + "lucide-react": "^0.542.0", + "next": "15.5.2", + "next-themes": "^0.4.6", + "path-to-regexp": "^8.2.0", + "react": "^19.1.1", + "react-click-away-listener": "^2.4.0", + "react-day-picker": "9.9.0", + "react-dom": "^19.1.1", + "react-hook-form": "^7.62.0", + "react-hot-toast": "^2.6.0", + "react-icons": "^5.5.0", + "react-intersection-observer": "^9.16.0", + "react-mobile-cropper": "^0.10.0", + "swiper": "^11.2.10", + "tailwind-merge": "^3.3.1", + "tw-animate-css": "^1.3.7", + "use-debounce": "^10.0.5", + "vaul": "^1.1.2", + "zod": "^4.1.3", + "zustand": "^5.0.8" + }, + "devDependencies": { + "@eslint/eslintrc": "^3.3.1", + "@next/eslint-plugin-next": "^15.5.2", + "@openapitools/openapi-generator-cli": "^2.23.1", + "@tanstack/eslint-plugin-query": "^5.83.1", + "@trivago/prettier-plugin-sort-imports": "^5.2.2", + "prettier-plugin-tailwindcss": "^0.6.14", + "@types/js-cookie": "^3.0.6", + "@types/node": "^24.3.0", + "@types/react": "^19.1.11", + "@types/react-dom": "^19.1.8", + "eslint": "^9.34.0", + "eslint-config-next": "15.5.2", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^5.2.0", + "postcss": "^8.5.6", + "prettier": "^3.6.2", + "sass": "^1.91.0", + "tailwindcss": "^4.1.12", + "typescript": "5.9.2" + }, + "packageManager": "pnpm@10.13.1+sha512.37ebf1a5c7a30d5fabe0c5df44ee8da4c965ca0c5af3dbab28c3a1681b70a256218d05c81c9c0dcf767ef6b8551eb5b960042b9ed4300c59242336377e01cfad", + "trustedDependencies": [ + "@nestjs/core", + "@openapitools/openapi-generator-cli", + "@parcel/watcher", + "@tailwindcss/oxide", + "unrs-resolver" + ] } diff --git a/src/app/(auth)/post/comments/[id]/CommentList.tsx b/src/app/(auth)/post/comments/[id]/CommentList.tsx index abb45e9..278c409 100644 --- a/src/app/(auth)/post/comments/[id]/CommentList.tsx +++ b/src/app/(auth)/post/comments/[id]/CommentList.tsx @@ -46,7 +46,7 @@ export function CommentList({ post }: { post: PostDetailDTO }) { return ( <> -
+
{comments && comments.length === 0 &&

Комментариев нет

} {!isLoading && diff --git a/src/app/(auth)/post/comments/[id]/CommentsPage.tsx b/src/app/(auth)/post/comments/[id]/CommentsPage.tsx index 99381d3..29781b5 100644 --- a/src/app/(auth)/post/comments/[id]/CommentsPage.tsx +++ b/src/app/(auth)/post/comments/[id]/CommentsPage.tsx @@ -17,7 +17,7 @@ export function CommentsPage({ post }: { post: PostDetailDTO }) { Комментарии - + diff --git a/src/app/(auth)/post/comments/[id]/useInfinityComments(disabled).tsx b/src/app/(auth)/post/comments/[id]/useInfinityComments(disabled).tsx deleted file mode 100644 index d1a3417..0000000 --- a/src/app/(auth)/post/comments/[id]/useInfinityComments(disabled).tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { useInfiniteQuery } from '@tanstack/react-query'; -import { useEffect } from 'react'; -import { useInView } from 'react-intersection-observer'; - -import { commentApi } from '@/api/api'; - -const PAGE_SIZE = 100; - -// TODO: сделать универсальный хук из этого и использовать везде где нужна пагинация по скроллу -export const useInfinityComments = (post_id: number) => { - const { ref, inView } = useInView(); - - const infiniteQuery = useInfiniteQuery({ - queryKey: ['fetch-post-comments', post_id], - queryFn: async ({ pageParam }) => (await commentApi.commentsGetByPostId(post_id, 0, pageParam, PAGE_SIZE)).data, - initialPageParam: 0, - getNextPageParam: (lastPage, __, lastPageParam) => { - if (!lastPage.length || lastPage.length < PAGE_SIZE) return null; - return lastPageParam + 1; - }, - getPreviousPageParam: (_, __, firstPageParam) => firstPageParam, - }); - - useEffect(() => { - if (inView && infiniteQuery.hasNextPage) { - infiniteQuery.fetchNextPage(); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [inView]); - - return { - ref, - infiniteQuery, - }; -}; diff --git a/src/components/CommentComponents/CommentItem.tsx b/src/components/CommentComponents/CommentItem.tsx index cc59a3a..0617375 100644 --- a/src/components/CommentComponents/CommentItem.tsx +++ b/src/components/CommentComponents/CommentItem.tsx @@ -91,11 +91,11 @@ export function CommentItem({ }, [comment.deletedAt]); return ( -
+
From 333d3c601b215e84d3c87ba01aa3429315de7490 Mon Sep 17 00:00:00 2001 From: Konstantin Zhigaylo Date: Sun, 28 Sep 2025 19:19:58 +0300 Subject: [PATCH 09/20] =?UTF-8?q?feat:=20SSR=20=D0=B4=D0=BB=D1=8F=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D1=84=D0=B8=D0=BB=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../profile/settings/MenuLogoutItem.tsx | 45 ++++++++++ .../profile/settings/Settings.tsx | 89 ++----------------- .../profile/settings/UserCard.tsx | 28 ++++++ .../profile/settings/UserClubs.tsx | 33 +++++++ .../profile/settings/page.tsx | 10 +-- src/hooks/useProfile.tsx | 10 +-- 6 files changed, 121 insertions(+), 94 deletions(-) create mode 100644 src/app/(auth)/(withMenuNavigation)/profile/settings/MenuLogoutItem.tsx create mode 100644 src/app/(auth)/(withMenuNavigation)/profile/settings/UserCard.tsx create mode 100644 src/app/(auth)/(withMenuNavigation)/profile/settings/UserClubs.tsx diff --git a/src/app/(auth)/(withMenuNavigation)/profile/settings/MenuLogoutItem.tsx b/src/app/(auth)/(withMenuNavigation)/profile/settings/MenuLogoutItem.tsx new file mode 100644 index 0000000..69dd71f --- /dev/null +++ b/src/app/(auth)/(withMenuNavigation)/profile/settings/MenuLogoutItem.tsx @@ -0,0 +1,45 @@ +'use client'; + +import { useMutation } from '@tanstack/react-query'; +import { useRouter } from 'next/navigation'; +import { startTransition } from 'react'; +import { BiExit } from 'react-icons/bi'; + +import { MenuItem } from '@/components/ui/menu'; + +import { PUBLIC_PAGE } from '@/lib/config/routes.config'; + +import { authApi } from '@/api/api'; + +export default function MenuLogoutItem() { + const router = useRouter(); + + const { mutateAsync } = useMutation({ + mutationKey: ['logout-profile'], + mutationFn: async () => await authApi.authLogout(), + }); + + const logoutHandler = async () => { + const { toast } = await import('react-hot-toast'); + toast.promise( + mutateAsync(), + { + loading: 'Выход из системы', + success: () => { + startTransition(() => { + router.push(PUBLIC_PAGE.AUTH('login')); + }); + + return 'Успешно!'; + }, + error: () => { + return 'Ошибка при попытке выхода из профиля'; + }, + }, + { + id: 'error', + } + ); + }; + return ; +} diff --git a/src/app/(auth)/(withMenuNavigation)/profile/settings/Settings.tsx b/src/app/(auth)/(withMenuNavigation)/profile/settings/Settings.tsx index 0f37115..972953b 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/settings/Settings.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/settings/Settings.tsx @@ -1,63 +1,15 @@ -'use client'; - -import { useMutation } from '@tanstack/react-query'; -import { useRouter } from 'next/navigation'; -import { startTransition } from 'react'; -import { BiExit } from 'react-icons/bi'; -import { LuPlus } from 'react-icons/lu'; - import { Page } from '@/components/Page'; -import { ProfileAvatarUploader } from '@/components/ProfileComponents/ProfileAvatar/ProfileAvatarUploader'; import { PROFILE_SETTING_SECTIONS } from '@/components/ProfileComponents/profile-section.config'; import { BackButton } from '@/components/ui/BackButton'; -import { Menu, MenuItem, MenuLink } from '@/components/ui/menu'; -import { SkeletonList } from '@/components/ui/skeleton'; - -import { AUTH_PAGE, PUBLIC_PAGE } from '@/lib/config/routes.config'; +import { Menu, MenuLink } from '@/components/ui/menu'; -import { useProfile } from '@/hooks/useProfile'; - -import { authApi } from '@/api/api'; -import { type PersonDetailDTO } from '@/api/axios-client'; - -import { useOwnedClubs } from './useOwnedClubs'; +import MenuLogoutItem from '@/app/(auth)/(withMenuNavigation)/profile/settings/MenuLogoutItem'; +import UserCard from '@/app/(auth)/(withMenuNavigation)/profile/settings/UserCard'; +import UserClubs from '@/app/(auth)/(withMenuNavigation)/profile/settings/UserClubs'; import { Header, HeaderTitle } from '@/hoc/Header/Header'; import { MainContent } from '@/hoc/MainContent/MainContent'; -import { getStaticImg } from '@/lib/helpers/getStaticImg.helper'; - -export default function Settings({ initUser }: { initUser: PersonDetailDTO }) { - const router = useRouter(); - const { data: user } = useProfile(initUser); - const { data: ownedClubs, isLoading } = useOwnedClubs(); - - const { mutateAsync } = useMutation({ - mutationKey: ['logout-profile'], - mutationFn: async () => await authApi.authLogout(), - }); - - const logoutHandler = async () => { - const { toast } = await import('react-hot-toast'); - toast.promise( - mutateAsync(), - { - loading: 'Выход из системы', - success: () => { - startTransition(() => { - router.push(PUBLIC_PAGE.AUTH('login')); - }); - - return 'Успешно!'; - }, - error: () => { - return 'Ошибка при попытке выхода из профиля'; - }, - }, - { - id: 'error', - } - ); - }; +export default function Settings() { return (
@@ -66,13 +18,7 @@ export default function Settings({ initUser }: { initUser: PersonDetailDTO }) {
- -
-

- {user?.firstName} {user?.lastName} -

-

{user?.about}

-
+

Основные

@@ -82,29 +28,10 @@ export default function Settings({ initUser }: { initUser: PersonDetailDTO }) { ))}

Ваши клубы

- - {isLoading && ( - - )} - {!isLoading && - ownedClubs?.map((club) => ( - - ))} - - +

Прочее

- + {PROFILE_SETTING_SECTIONS.other.map((section, index) => ( + {isLoading && } + {!isLoading && ( + <> + +
+

+ {user?.firstName} {user?.lastName} +

+

{user?.about}

+
+ + )} + + ); +} diff --git a/src/app/(auth)/(withMenuNavigation)/profile/settings/UserClubs.tsx b/src/app/(auth)/(withMenuNavigation)/profile/settings/UserClubs.tsx new file mode 100644 index 0000000..20d79cb --- /dev/null +++ b/src/app/(auth)/(withMenuNavigation)/profile/settings/UserClubs.tsx @@ -0,0 +1,33 @@ +'use client'; + +import { LuPlus } from 'react-icons/lu'; + +import { Menu, MenuLink } from '@/components/ui/menu'; +import { SkeletonList } from '@/components/ui/skeleton'; + +import { AUTH_PAGE } from '@/lib/config/routes.config'; + +import { useOwnedClubs } from '@/app/(auth)/(withMenuNavigation)/profile/settings/useOwnedClubs'; +import { getStaticImg } from '@/lib/helpers/getStaticImg.helper'; + +export default function UserClubs() { + const { data: ownedClubs, isLoading } = useOwnedClubs(); + return ( + + {isLoading && ( + + )} + {!isLoading && + ownedClubs?.map((club) => ( + + ))} + + + ); +} diff --git a/src/app/(auth)/(withMenuNavigation)/profile/settings/page.tsx b/src/app/(auth)/(withMenuNavigation)/profile/settings/page.tsx index c78b0c7..3f5dfa0 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/settings/page.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/settings/page.tsx @@ -1,16 +1,14 @@ import type { Metadata } from 'next'; -import { userApi } from '@/api/api'; -import type { PersonDetailDTO } from '@/api/axios-client'; - import Settings from './Settings'; export const metadata: Metadata = { title: 'Настройки профиля', - description: '', + description: 'Настройки вашего аккаунта и приложения', }; +export const dynamic = 'force-static'; + export default async function Page() { - const user: PersonDetailDTO = (await userApi.userGetPersonalDetails()).data; - return ; + return ; } diff --git a/src/hooks/useProfile.tsx b/src/hooks/useProfile.tsx index edf4a2b..d7d3d6f 100644 --- a/src/hooks/useProfile.tsx +++ b/src/hooks/useProfile.tsx @@ -1,18 +1,14 @@ import { useQuery } from '@tanstack/react-query'; import { userApi } from '@/api/api'; -import type { PersonDetailDTO } from '@/api/axios-client'; /** * Данные об авторизованном пользователе в системе (api/user/personalDetails) */ -export const useProfile = (initData?: PersonDetailDTO) => { - const profileData = useQuery({ +export const useProfile = () => { + return useQuery({ queryKey: ['fetch-profile'], queryFn: async () => (await userApi.userGetPersonalDetails()).data, - staleTime: 60 * 1000 * 5, // кешируем данные о пользователе на 5 минут, - initialData: initData, + staleTime: 60 * 1000 * 5, }); - - return profileData; }; From 571cc2a9e3533eb6a514e24159b4a22b97b7432c Mon Sep 17 00:00:00 2001 From: Konstantin Zhigaylo Date: Sun, 28 Sep 2025 19:37:03 +0300 Subject: [PATCH 10/20] =?UTF-8?q?feat:=20SSR=20=D0=B4=D0=BB=D1=8F=20=D1=81?= =?UTF-8?q?=D0=BE=D0=B1=D1=8B=D1=82=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(withMenuNavigation)/events/Events.tsx | 42 --------- .../events/[id]/EventNotFound.tsx | 11 +-- .../events/[id]/EventView.tsx | 88 ----------------- .../(withMenuNavigation)/events/[id]/page.tsx | 94 ++++++++++++++++++- .../events/calendar/Calendar.tsx | 13 +-- .../events/calendar/page.tsx | 2 +- .../(withMenuNavigation)/events/page.tsx | 34 ++++++- .../EventCalendar/EventCalendar.tsx | 4 +- 8 files changed, 128 insertions(+), 160 deletions(-) delete mode 100644 src/app/(auth)/(withMenuNavigation)/events/Events.tsx delete mode 100644 src/app/(auth)/(withMenuNavigation)/events/[id]/EventView.tsx diff --git a/src/app/(auth)/(withMenuNavigation)/events/Events.tsx b/src/app/(auth)/(withMenuNavigation)/events/Events.tsx deleted file mode 100644 index 5c5b945..0000000 --- a/src/app/(auth)/(withMenuNavigation)/events/Events.tsx +++ /dev/null @@ -1,42 +0,0 @@ -'use client'; - -import { m } from 'framer-motion'; -import { useRouter } from 'next/navigation'; - -import { CalendarBadge } from '@/components/Badges'; -import { EventCard } from '@/components/EventCard/EventCard'; -import { Page } from '@/components/Page'; - -import { AUTH_PAGE } from '@/lib/config/routes.config'; - -import type { EventDetailDTO } from '@/api/axios-client'; - -import { Header, HeaderTitle } from '@/hoc/Header/Header'; -import { MainContent } from '@/hoc/MainContent/MainContent'; - -export function Events({ events }: { events: EventDetailDTO[] }) { - const router = useRouter(); - - return ( - -
- События - router.push(AUTH_PAGE.EVENTS_CALENDAR())} /> -
- - {events && - events.map((event, index) => ( - - - - ))} - -
- ); -} diff --git a/src/app/(auth)/(withMenuNavigation)/events/[id]/EventNotFound.tsx b/src/app/(auth)/(withMenuNavigation)/events/[id]/EventNotFound.tsx index 3f1be10..0718ef0 100644 --- a/src/app/(auth)/(withMenuNavigation)/events/[id]/EventNotFound.tsx +++ b/src/app/(auth)/(withMenuNavigation)/events/[id]/EventNotFound.tsx @@ -1,16 +1,11 @@ -'use client'; - -import { useRouter } from 'next/navigation'; - -import { Button } from '@/components/ui/button'; +import { BackButton } from '@/components/ui/BackButton'; export default function EventNotFound() { - const router = useRouter(); return ( -
+

404

Событие не найдено

- +
); } diff --git a/src/app/(auth)/(withMenuNavigation)/events/[id]/EventView.tsx b/src/app/(auth)/(withMenuNavigation)/events/[id]/EventView.tsx deleted file mode 100644 index d3697ca..0000000 --- a/src/app/(auth)/(withMenuNavigation)/events/[id]/EventView.tsx +++ /dev/null @@ -1,88 +0,0 @@ -'use client'; - -import { EllipsisVertical } from 'lucide-react'; -import Image from 'next/image'; -import { FaCalendar, FaCompass } from 'react-icons/fa'; -import { IoAlertCircle, IoCopy } from 'react-icons/io5'; - -import { Page } from '@/components/Page'; -import { BackButton } from '@/components/ui/BackButton'; -import { Button } from '@/components/ui/button'; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from '@/components/ui/dropdown-menu'; - -import type { EventDetailDTO } from '@/api/axios-client'; - -import { getStaticImg } from '@/lib/helpers/getStaticImg.helper'; -import { parseLocalTime } from '@/lib/utils/time.util'; - -export default function EventView({ event }: { event: EventDetailDTO }) { - return ( - -
-
- - {/*

*/} - {/* Подробности */} - {/*

*/} -
- - - - - - { - navigator.clipboard.writeText(window.location.href); - const { toast } = await import('react-hot-toast'); - toast.success('Ссылка скопирована'); - }} - > - - Скопировать ссылку - - - - Пожаловаться - - - -
-
- 0 && getStaticImg(event.eventImages[0])) || - '/img/default-club-banner.jpg' - } - fill - alt={event.title || 'Event banner'} - className="object-cover" - priority - /> -
-
-
-
-
-

{event.title}

-

{event.description}

-
-
- -

{event.location}

-
-
- - -
-
-
-
- ); -} diff --git a/src/app/(auth)/(withMenuNavigation)/events/[id]/page.tsx b/src/app/(auth)/(withMenuNavigation)/events/[id]/page.tsx index 092bc04..a9a3972 100644 --- a/src/app/(auth)/(withMenuNavigation)/events/[id]/page.tsx +++ b/src/app/(auth)/(withMenuNavigation)/events/[id]/page.tsx @@ -1,7 +1,23 @@ +import { EllipsisVertical } from 'lucide-react'; +import Image from 'next/image'; +import { FaCalendar, FaCompass } from 'react-icons/fa'; +import { IoAlertCircle, IoCopy } from 'react-icons/io5'; + +import { Page } from '@/components/Page'; +import { BackButton } from '@/components/ui/BackButton'; +import { Button } from '@/components/ui/button'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu'; + import { eventsApi } from '@/api/api'; import EventNotFound from './EventNotFound'; -import EventView from './EventView'; +import { getStaticImg } from '@/lib/helpers/getStaticImg.helper'; +import { parseLocalTime } from '@/lib/utils/time.util'; export async function generateMetadata({ params }: { params: Promise<{ id: string }> }) { const id = (await params).id; @@ -10,21 +26,89 @@ export async function generateMetadata({ params }: { params: Promise<{ id: strin if (!event) { return { title: 'Событие не найдено', - description: '', + description: 'Проверьте правильность ссылки на событие.', }; } return { - title: `${event.title} - Событие`, + title: `Событие "${event.title}"`, description: event.description, }; } +export const dynamic = 'force-dynamic'; export const revalidate = 100; -export default async function Page({ params }: { params: Promise<{ id: string }> }) { +export default async function EventViewPage({ params }: { params: Promise<{ id: string }> }) { const id = (await params).id; const event = (await eventsApi.eventsGetById(Number(id))).data; - return event ? : ; + if (!event) { + return ; + } + + return ( + +
+
+ + {/*

*/} + {/* Подробности */} + {/*

*/} +
+ + + + + + { + navigator.clipboard.writeText(window.location.href); + const { toast } = await import('react-hot-toast'); + toast.success('Ссылка скопирована'); + }} + > + + Скопировать ссылку + + + + Пожаловаться + + + +
+
+ 0 && getStaticImg(event.eventImages[0])) || + '/img/default-club-banner.jpg' + } + fill + alt={event.title || 'Event banner'} + className="object-cover" + priority + /> +
+
+
+
+
+

{event.title}

+

{event.description}

+
+
+ +

{event.location}

+
+
+ + +
+
+
+
+ ); } diff --git a/src/app/(auth)/(withMenuNavigation)/events/calendar/Calendar.tsx b/src/app/(auth)/(withMenuNavigation)/events/calendar/Calendar.tsx index 3982969..4e2e149 100644 --- a/src/app/(auth)/(withMenuNavigation)/events/calendar/Calendar.tsx +++ b/src/app/(auth)/(withMenuNavigation)/events/calendar/Calendar.tsx @@ -2,7 +2,6 @@ import { useQuery } from '@tanstack/react-query'; import { format, parseISO } from 'date-fns'; -import { m } from 'framer-motion'; import { useMemo, useState } from 'react'; import EventCalendar from '@/components/EventCalendar/EventCalendar'; @@ -70,16 +69,10 @@ export function Calendar() { {hasEvents ? eventsPages?.pages .flatMap((page) => page) - .map((event, index) => ( - + .map((event) => ( +
- +
)) : !isLoading && (
diff --git a/src/app/(auth)/(withMenuNavigation)/events/calendar/page.tsx b/src/app/(auth)/(withMenuNavigation)/events/calendar/page.tsx index 1a91e74..8650bbe 100644 --- a/src/app/(auth)/(withMenuNavigation)/events/calendar/page.tsx +++ b/src/app/(auth)/(withMenuNavigation)/events/calendar/page.tsx @@ -4,7 +4,7 @@ import { Calendar } from './Calendar'; export const metadata: Metadata = { title: 'Календарь событий', - description: '', + description: 'Календарь со всеми событиями', }; export default async function Page() { diff --git a/src/app/(auth)/(withMenuNavigation)/events/page.tsx b/src/app/(auth)/(withMenuNavigation)/events/page.tsx index 70cc495..0cf772c 100644 --- a/src/app/(auth)/(withMenuNavigation)/events/page.tsx +++ b/src/app/(auth)/(withMenuNavigation)/events/page.tsx @@ -1,16 +1,44 @@ import type { Metadata } from 'next'; +import Link from 'next/link'; + +import { CalendarBadge } from '@/components/Badges'; +import { EventCard } from '@/components/EventCard/EventCard'; +import { Page } from '@/components/Page'; + +import { AUTH_PAGE } from '@/lib/config/routes.config'; import { eventsApi } from '@/api/api'; -import { Events } from './Events'; +import { Header, HeaderTitle } from '@/hoc/Header/Header'; +import { MainContent } from '@/hoc/MainContent/MainContent'; export const metadata: Metadata = { title: 'События', description: '', }; -export default async function Page() { +export const dynamic = 'force-dynamic'; +export const revalidate = 60; + +export default async function EventsPage() { const events = (await eventsApi.eventsGetAll()).data; - return ; + return ( + +
+ События + + + +
+ + {events && + events.map((event) => ( +
+ +
+ ))} +
+
+ ); } diff --git a/src/components/EventCalendar/EventCalendar.tsx b/src/components/EventCalendar/EventCalendar.tsx index d4b7e9b..5a4a526 100644 --- a/src/components/EventCalendar/EventCalendar.tsx +++ b/src/components/EventCalendar/EventCalendar.tsx @@ -139,7 +139,7 @@ const EventCalendar = ({ events = {}, onDateChange }: CalendarProps) => { key={day.toString()} onClick={() => handleDateClick(day)} disabled={!isCurrentMonth} - className={`relative aspect-square rounded-xl text-sm font-medium transition-all duration-200 sm:text-base ${isCurrentMonth ? 'hover:scale-105 active:scale-95' : 'cursor-not-allowed opacity-30'} ${ + className={`relative aspect-square rounded-xl text-sm font-medium transition-all duration-200 sm:text-base ${isCurrentMonth ? '' : 'cursor-not-allowed opacity-30'} ${ isSelected ? 'bg-primary scale-105 text-white' : isDayToday @@ -183,8 +183,6 @@ const EventCalendar = ({ events = {}, onDateChange }: CalendarProps) => { ); })}
- - {/* Toggle view button */}
); From a5d006379c0ed9fa8624a1ba79e065910311071d Mon Sep 17 00:00:00 2001 From: Konstantin Zhigaylo Date: Sun, 28 Sep 2025 19:39:53 +0300 Subject: [PATCH 11/20] =?UTF-8?q?chore:=20=D1=80=D0=B5=D1=81=D1=82=D1=80?= =?UTF-8?q?=D1=83=D0=BA=D1=82=D1=83=D1=80=D0=B8=D0=B7=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../clubs/[id]/subscribers/Subscribers.tsx | 2 +- .../(withMenuNavigation)/events/calendar/Calendar.tsx | 4 ++-- src/app/(auth)/(withMenuNavigation)/events/page.tsx | 2 +- src/app/(auth)/(withMenuNavigation)/loading.tsx | 2 +- .../profile/settings/about/About.tsx | 2 +- src/app/(auth)/loading.tsx | 2 +- src/components/ClubComponents/ClubHeader.tsx | 5 ----- src/components/ClubComponents/ClubInfo.tsx | 2 +- src/components/{EventCalendar => }/EventCalendar.tsx | 2 +- src/components/{EventCard => }/EventCard.tsx | 3 +-- src/components/{ImageLoader => }/ImageLoader.tsx | 3 +-- src/components/{MainLoader => }/MainLoader.tsx | 2 +- .../PostCard/PostImageSwiper/PostImageSwiper.tsx | 10 +++++----- .../PostCard/PostImageSwiper/PostImageWrapper.tsx | 2 +- src/components/{Sidebar => }/Sidebar.tsx | 0 src/components/{SubscriberCard => }/SubscriberCard.tsx | 0 src/components/ui/Avatar.tsx | 2 +- 17 files changed, 19 insertions(+), 26 deletions(-) rename src/components/{EventCalendar => }/EventCalendar.tsx (99%) rename src/components/{EventCard => }/EventCard.tsx (96%) rename src/components/{ImageLoader => }/ImageLoader.tsx (96%) rename src/components/{MainLoader => }/MainLoader.tsx (91%) rename src/components/{Sidebar => }/Sidebar.tsx (100%) rename src/components/{SubscriberCard => }/SubscriberCard.tsx (100%) diff --git a/src/app/(auth)/(withMenuNavigation)/clubs/[id]/subscribers/Subscribers.tsx b/src/app/(auth)/(withMenuNavigation)/clubs/[id]/subscribers/Subscribers.tsx index 84d2075..6598a22 100644 --- a/src/app/(auth)/(withMenuNavigation)/clubs/[id]/subscribers/Subscribers.tsx +++ b/src/app/(auth)/(withMenuNavigation)/clubs/[id]/subscribers/Subscribers.tsx @@ -4,7 +4,7 @@ import Link from 'next/link'; import Loader from '@/components/Loader'; import { Page } from '@/components/Page'; -import { SubscriberCard } from '@/components/SubscriberCard/SubscriberCard'; +import { SubscriberCard } from '@/components/SubscriberCard'; import { BackButton } from '@/components/ui/BackButton'; import { AUTH_PAGE } from '@/lib/config/routes.config'; diff --git a/src/app/(auth)/(withMenuNavigation)/events/calendar/Calendar.tsx b/src/app/(auth)/(withMenuNavigation)/events/calendar/Calendar.tsx index 4e2e149..8864215 100644 --- a/src/app/(auth)/(withMenuNavigation)/events/calendar/Calendar.tsx +++ b/src/app/(auth)/(withMenuNavigation)/events/calendar/Calendar.tsx @@ -4,8 +4,8 @@ import { useQuery } from '@tanstack/react-query'; import { format, parseISO } from 'date-fns'; import { useMemo, useState } from 'react'; -import EventCalendar from '@/components/EventCalendar/EventCalendar'; -import { EventCard } from '@/components/EventCard/EventCard'; +import EventCalendar from '@/components/EventCalendar'; +import { EventCard } from '@/components/EventCard'; import { Page } from '@/components/Page'; import { BackButton } from '@/components/ui/BackButton'; diff --git a/src/app/(auth)/(withMenuNavigation)/events/page.tsx b/src/app/(auth)/(withMenuNavigation)/events/page.tsx index 0cf772c..0c37a23 100644 --- a/src/app/(auth)/(withMenuNavigation)/events/page.tsx +++ b/src/app/(auth)/(withMenuNavigation)/events/page.tsx @@ -2,7 +2,7 @@ import type { Metadata } from 'next'; import Link from 'next/link'; import { CalendarBadge } from '@/components/Badges'; -import { EventCard } from '@/components/EventCard/EventCard'; +import { EventCard } from '@/components/EventCard'; import { Page } from '@/components/Page'; import { AUTH_PAGE } from '@/lib/config/routes.config'; diff --git a/src/app/(auth)/(withMenuNavigation)/loading.tsx b/src/app/(auth)/(withMenuNavigation)/loading.tsx index 24b250d..d3527cc 100644 --- a/src/app/(auth)/(withMenuNavigation)/loading.tsx +++ b/src/app/(auth)/(withMenuNavigation)/loading.tsx @@ -1,4 +1,4 @@ -import { MainLoader } from '@/components/MainLoader/MainLoader'; +import { MainLoader } from '@/components/MainLoader'; export default function Loading() { return ; diff --git a/src/app/(auth)/(withMenuNavigation)/profile/settings/about/About.tsx b/src/app/(auth)/(withMenuNavigation)/profile/settings/about/About.tsx index d436cf9..8f03865 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/settings/about/About.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/settings/about/About.tsx @@ -1,6 +1,6 @@ import { FaGithub, FaGraduationCap } from 'react-icons/fa'; -import LoaderImage from '@/components/ImageLoader/ImageLoader'; +import LoaderImage from '@/components/ImageLoader'; import { Page } from '@/components/Page'; import SettingsSection from '@/components/ProfileComponents/SettingSection'; import { BackButton } from '@/components/ui/BackButton'; diff --git a/src/app/(auth)/loading.tsx b/src/app/(auth)/loading.tsx index 24b250d..d3527cc 100644 --- a/src/app/(auth)/loading.tsx +++ b/src/app/(auth)/loading.tsx @@ -1,4 +1,4 @@ -import { MainLoader } from '@/components/MainLoader/MainLoader'; +import { MainLoader } from '@/components/MainLoader'; export default function Loading() { return ; diff --git a/src/components/ClubComponents/ClubHeader.tsx b/src/components/ClubComponents/ClubHeader.tsx index 8a04cd2..2cef889 100644 --- a/src/components/ClubComponents/ClubHeader.tsx +++ b/src/components/ClubComponents/ClubHeader.tsx @@ -1,10 +1,8 @@ 'use client'; import { EllipsisVertical, Settings } from 'lucide-react'; -import dynamic from 'next/dynamic'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; -import { useMemo } from 'react'; import toast from 'react-hot-toast'; import { IoAlertCircle, IoCopy } from 'react-icons/io5'; @@ -22,13 +20,10 @@ import { useClubsRole } from '@/hooks/useClubsRole'; import type { ClubDetailDTO } from '@/api/axios-client'; -import LoaderImage from '../ImageLoader/ImageLoader'; import { BackButton } from '../ui/BackButton'; import { Button } from '../ui/button'; -import { Skeleton } from '../ui/skeleton'; import { CLUB_ROLES } from '@/lib/enums/club-roles.enum'; -import { getStaticImg } from '@/lib/helpers/getStaticImg.helper'; export function ClubHeader({ club }: { club: ClubDetailDTO }) { const pathname = usePathname(); diff --git a/src/components/ClubComponents/ClubInfo.tsx b/src/components/ClubComponents/ClubInfo.tsx index 37f5580..31e93a6 100644 --- a/src/components/ClubComponents/ClubInfo.tsx +++ b/src/components/ClubComponents/ClubInfo.tsx @@ -1,7 +1,7 @@ import dynamic from 'next/dynamic'; import Link from 'next/link'; -import LoaderImage from '@/components/ImageLoader/ImageLoader'; +import LoaderImage from '@/components/ImageLoader'; import { Skeleton } from '@/components/ui/skeleton'; import type { ClubDetailDTO } from '@/api/axios-client'; diff --git a/src/components/EventCalendar/EventCalendar.tsx b/src/components/EventCalendar.tsx similarity index 99% rename from src/components/EventCalendar/EventCalendar.tsx rename to src/components/EventCalendar.tsx index 5a4a526..bde0a62 100644 --- a/src/components/EventCalendar/EventCalendar.tsx +++ b/src/components/EventCalendar.tsx @@ -21,7 +21,7 @@ import { useEffect, useState } from 'react'; import type { EventDetailDTO } from '@/api/axios-client'; -import { Button } from '../ui/button'; +import { Button } from './ui/button'; interface CalendarProps { events?: { [key: string]: EventDetailDTO[] }; diff --git a/src/components/EventCard/EventCard.tsx b/src/components/EventCard.tsx similarity index 96% rename from src/components/EventCard/EventCard.tsx rename to src/components/EventCard.tsx index 1908004..2d591bf 100644 --- a/src/components/EventCard/EventCard.tsx +++ b/src/components/EventCard.tsx @@ -6,8 +6,7 @@ import { AUTH_PAGE } from '@/lib/config/routes.config'; import type { EventDetailDTO } from '@/api/axios-client'; -import LoaderImage from '../ImageLoader/ImageLoader'; - +import LoaderImage from './ImageLoader'; import { getStaticImg } from '@/lib/helpers/getStaticImg.helper'; import { parseLocalTime } from '@/lib/utils/time.util'; diff --git a/src/components/ImageLoader/ImageLoader.tsx b/src/components/ImageLoader.tsx similarity index 96% rename from src/components/ImageLoader/ImageLoader.tsx rename to src/components/ImageLoader.tsx index 934ef3e..6f206fd 100644 --- a/src/components/ImageLoader/ImageLoader.tsx +++ b/src/components/ImageLoader.tsx @@ -3,8 +3,7 @@ import Image, { type ImageProps } from 'next/image'; import { useState } from 'react'; -import Loader from '../Loader'; - +import Loader from './Loader'; import { cn } from '@/lib/utils/utils'; interface LoaderImageProps extends ImageProps { diff --git a/src/components/MainLoader/MainLoader.tsx b/src/components/MainLoader.tsx similarity index 91% rename from src/components/MainLoader/MainLoader.tsx rename to src/components/MainLoader.tsx index 5b07c99..19a65f7 100644 --- a/src/components/MainLoader/MainLoader.tsx +++ b/src/components/MainLoader.tsx @@ -1,4 +1,4 @@ -import Loader from '../Loader'; +import Loader from './Loader'; export function MainLoader() { return ( diff --git a/src/components/PostCard/PostImageSwiper/PostImageSwiper.tsx b/src/components/PostCard/PostImageSwiper/PostImageSwiper.tsx index 6d50688..b1d1e9d 100644 --- a/src/components/PostCard/PostImageSwiper/PostImageSwiper.tsx +++ b/src/components/PostCard/PostImageSwiper/PostImageSwiper.tsx @@ -3,7 +3,7 @@ import 'swiper/css/pagination'; import { Pagination } from 'swiper/modules'; import { Swiper, SwiperSlide } from 'swiper/react'; -import LoaderImage from '@/components/ImageLoader/ImageLoader'; +import LoaderImage from '@/components/ImageLoader'; import { getStaticImg } from '@/lib/helpers/getStaticImg.helper'; @@ -22,15 +22,15 @@ export function PostImageSwiper({ images }: Props) { className="mySwiper" > {images.map((img, index) => ( - + {/* Blurred background image */} -
+
{/* Main image */} @@ -39,7 +39,7 @@ export function PostImageSwiper({ images }: Props) { width={1000} height={1000} alt={'Прикрепленное изображение'} - className=" w-full object-contain relative z-10" + className="relative z-10 w-full object-contain" /> ))} diff --git a/src/components/PostCard/PostImageSwiper/PostImageWrapper.tsx b/src/components/PostCard/PostImageSwiper/PostImageWrapper.tsx index 3132e23..66b8792 100644 --- a/src/components/PostCard/PostImageSwiper/PostImageWrapper.tsx +++ b/src/components/PostCard/PostImageSwiper/PostImageWrapper.tsx @@ -1,7 +1,7 @@ import dynamic from 'next/dynamic'; import { useMemo } from 'react'; -import LoaderImage from '@/components/ImageLoader/ImageLoader'; +import LoaderImage from '@/components/ImageLoader'; import { getStaticImg } from '@/lib/helpers/getStaticImg.helper'; diff --git a/src/components/Sidebar/Sidebar.tsx b/src/components/Sidebar.tsx similarity index 100% rename from src/components/Sidebar/Sidebar.tsx rename to src/components/Sidebar.tsx diff --git a/src/components/SubscriberCard/SubscriberCard.tsx b/src/components/SubscriberCard.tsx similarity index 100% rename from src/components/SubscriberCard/SubscriberCard.tsx rename to src/components/SubscriberCard.tsx diff --git a/src/components/ui/Avatar.tsx b/src/components/ui/Avatar.tsx index 569ef73..28e6f8c 100644 --- a/src/components/ui/Avatar.tsx +++ b/src/components/ui/Avatar.tsx @@ -1,6 +1,6 @@ 'use client'; -import LoaderImage from '@/components/ImageLoader/ImageLoader'; +import LoaderImage from '@/components/ImageLoader'; import { getStaticImg } from '@/lib/helpers/getStaticImg.helper'; import { cn } from '@/lib/utils/utils'; From fec2fc4c5373682964d14a6b479498730edb6c6b Mon Sep 17 00:00:00 2001 From: Konstantin Zhigaylo Date: Sun, 28 Sep 2025 19:46:30 +0300 Subject: [PATCH 12/20] =?UTF-8?q?feat(css):=20=D1=83=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D0=BB=20=D0=BF=D0=BE=D0=B4=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D1=83?= =?UTF-8?q?=20=D1=88=D1=80=D0=B8=D1=84=D1=82=D0=BE=D0=B2=20=D1=81=20Google?= =?UTF-8?q?=20Fonts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Оптимизация шрифтов в Next.js работает так, что при сборке он качает их себе локально. В проде, когда юзер открывает страницу, шрифты будут отправляться юзеру напрямую, без надобности отправлять запрос в Google Fonts. До этого коммита у нас получилось так, что юзер запрашивал шрифты И у нашего сайта И у Google Fonts. --- src/css/globals.css | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/css/globals.css b/src/css/globals.css index 9429220..c9c5bfd 100644 --- a/src/css/globals.css +++ b/src/css/globals.css @@ -1,7 +1,3 @@ -@import url('https://fonts.googleapis.com/css2?family=Geologica:wght@100..900&display=swap'); -@import url('https://fonts.googleapis.com/css2?family=Inter&display=swap'); -@import url('https://fonts.googleapis.com/css2?family=Unbounded&display=swap'); - @import 'tailwindcss'; @import 'tw-animate-css'; From 416f6b0f69e89ed8cabaed62efbb813e1be39a76 Mon Sep 17 00:00:00 2001 From: Konstantin Zhigaylo Date: Sun, 28 Sep 2025 19:48:34 +0300 Subject: [PATCH 13/20] =?UTF-8?q?chore(gitignore):=20=D0=B4=D0=BE=D0=B1?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D0=BB=20bun.lock=20=D0=B2=20.gitignore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + src/components/MainLoader.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 1cea65a..c50ca70 100644 --- a/.gitignore +++ b/.gitignore @@ -43,5 +43,6 @@ yarn-error.log* next-env.d.ts certificates certbot +bun.lock /.idea diff --git a/src/components/MainLoader.tsx b/src/components/MainLoader.tsx index 19a65f7..dcde2d0 100644 --- a/src/components/MainLoader.tsx +++ b/src/components/MainLoader.tsx @@ -2,7 +2,7 @@ import Loader from './Loader'; export function MainLoader() { return ( -
+
Загрузка From 2556d4afae8962d4de7256baa0d94c13a9fb7b1a Mon Sep 17 00:00:00 2001 From: Konstantin Zhigaylo Date: Mon, 29 Sep 2025 15:24:20 +0300 Subject: [PATCH 14/20] =?UTF-8?q?chore:=20=D1=80=D0=B5=D1=81=D1=82=D1=80?= =?UTF-8?q?=D1=83=D0=BA=D1=82=D1=83=D1=80=D0=B8=D0=B7=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(auth)/(withMenuNavigation)/(home)/Home.tsx | 4 ++-- src/app/(auth)/(withMenuNavigation)/clubs/Clubs.tsx | 4 ++-- .../clubs/[id]/subscribers/Subscribers.tsx | 2 +- .../(auth)/(withMenuNavigation)/events/calendar/Calendar.tsx | 4 ++-- src/app/(auth)/(withMenuNavigation)/events/page.tsx | 4 ++-- src/app/(auth)/(withMenuNavigation)/finder/Finder.tsx | 2 +- src/app/(auth)/(withMenuNavigation)/profile/[id]/Profile.tsx | 4 ++-- .../(auth)/(withMenuNavigation)/profile/[id]/clubs/page.tsx | 4 ++-- .../(auth)/(withMenuNavigation)/profile/settings/Settings.tsx | 4 ++-- .../(withMenuNavigation)/profile/settings/about/About.tsx | 4 ++-- .../profile/settings/appearance/appearance.tsx | 4 ++-- .../profile/settings/club/[id]/SettingClubPage.tsx | 4 ++-- .../profile/settings/create-club/CreateClub.tsx | 4 ++-- .../(withMenuNavigation)/profile/settings/edit/Edit.tsx | 4 ++-- .../profile/settings/feedback/Feedback.tsx | 4 ++-- .../profile/settings/notifications/notifications.tsx | 4 ++-- src/app/(auth)/post/comments/[id]/CommentsPage.tsx | 4 ++-- src/app/(auth)/post/draft/DraftPage.tsx | 4 ++-- src/components/ClubComponents/ClubFeed.tsx | 2 +- src/hoc/{Header => }/Header.tsx | 0 src/hoc/{MainContent => }/MainContent.tsx | 0 21 files changed, 35 insertions(+), 35 deletions(-) rename src/hoc/{Header => }/Header.tsx (100%) rename src/hoc/{MainContent => }/MainContent.tsx (100%) diff --git a/src/app/(auth)/(withMenuNavigation)/(home)/Home.tsx b/src/app/(auth)/(withMenuNavigation)/(home)/Home.tsx index c3a81c6..aad22ba 100644 --- a/src/app/(auth)/(withMenuNavigation)/(home)/Home.tsx +++ b/src/app/(auth)/(withMenuNavigation)/(home)/Home.tsx @@ -3,8 +3,8 @@ import { Suspense } from 'react'; import { Page } from '@/components/Page'; import HomeFeed from '@/app/(auth)/(withMenuNavigation)/(home)/HomeFeed'; -import { Header, HeaderTitle } from '@/hoc/Header/Header'; -import { MainContent } from '@/hoc/MainContent/MainContent'; +import { Header, HeaderTitle } from '@/hoc/Header'; +import { MainContent } from '@/hoc/MainContent'; import { parseLocalTime } from '@/lib/utils/time.util'; export const dynamic = 'force-static'; diff --git a/src/app/(auth)/(withMenuNavigation)/clubs/Clubs.tsx b/src/app/(auth)/(withMenuNavigation)/clubs/Clubs.tsx index f2cc513..be198a8 100644 --- a/src/app/(auth)/(withMenuNavigation)/clubs/Clubs.tsx +++ b/src/app/(auth)/(withMenuNavigation)/clubs/Clubs.tsx @@ -13,8 +13,8 @@ import { useInfinityScroll } from '@/hooks/useInfinityScroll'; import { clubsApi } from '@/api/api'; -import { Header, HeaderTitle } from '@/hoc/Header/Header'; -import { MainContent } from '@/hoc/MainContent/MainContent'; +import { Header, HeaderTitle } from '@/hoc/Header'; +import { MainContent } from '@/hoc/MainContent'; export function Clubs() { const [searchQuery, setSearchQuery] = useState(''); diff --git a/src/app/(auth)/(withMenuNavigation)/clubs/[id]/subscribers/Subscribers.tsx b/src/app/(auth)/(withMenuNavigation)/clubs/[id]/subscribers/Subscribers.tsx index 6598a22..c3eb720 100644 --- a/src/app/(auth)/(withMenuNavigation)/clubs/[id]/subscribers/Subscribers.tsx +++ b/src/app/(auth)/(withMenuNavigation)/clubs/[id]/subscribers/Subscribers.tsx @@ -13,7 +13,7 @@ import { useInfinityScroll } from '@/hooks/useInfinityScroll'; import { clubsApi } from '@/api/api'; -import { Header, HeaderTitle } from '@/hoc/Header/Header'; +import { Header, HeaderTitle } from '@/hoc/Header'; export function Subscribers({ id }: { id: string }) { const { diff --git a/src/app/(auth)/(withMenuNavigation)/events/calendar/Calendar.tsx b/src/app/(auth)/(withMenuNavigation)/events/calendar/Calendar.tsx index 8864215..e4119dc 100644 --- a/src/app/(auth)/(withMenuNavigation)/events/calendar/Calendar.tsx +++ b/src/app/(auth)/(withMenuNavigation)/events/calendar/Calendar.tsx @@ -14,8 +14,8 @@ import { useInfinityScroll } from '@/hooks/useInfinityScroll'; import { eventsApi } from '@/api/api'; import type { EventDetailDTO } from '@/api/axios-client'; -import { Header, HeaderTitle } from '@/hoc/Header/Header'; -import { MainContent } from '@/hoc/MainContent/MainContent'; +import { Header, HeaderTitle } from '@/hoc/Header'; +import { MainContent } from '@/hoc/MainContent'; import { toUTCDate } from '@/lib/utils/time.util'; export function Calendar() { diff --git a/src/app/(auth)/(withMenuNavigation)/events/page.tsx b/src/app/(auth)/(withMenuNavigation)/events/page.tsx index 0c37a23..1111f0c 100644 --- a/src/app/(auth)/(withMenuNavigation)/events/page.tsx +++ b/src/app/(auth)/(withMenuNavigation)/events/page.tsx @@ -9,8 +9,8 @@ import { AUTH_PAGE } from '@/lib/config/routes.config'; import { eventsApi } from '@/api/api'; -import { Header, HeaderTitle } from '@/hoc/Header/Header'; -import { MainContent } from '@/hoc/MainContent/MainContent'; +import { Header, HeaderTitle } from '@/hoc/Header'; +import { MainContent } from '@/hoc/MainContent'; export const metadata: Metadata = { title: 'События', diff --git a/src/app/(auth)/(withMenuNavigation)/finder/Finder.tsx b/src/app/(auth)/(withMenuNavigation)/finder/Finder.tsx index 759ed10..dc301bf 100644 --- a/src/app/(auth)/(withMenuNavigation)/finder/Finder.tsx +++ b/src/app/(auth)/(withMenuNavigation)/finder/Finder.tsx @@ -1,4 +1,4 @@ -import { Header, HeaderTitle } from '@/hoc/Header/Header'; +import { Header, HeaderTitle } from '@/hoc/Header'; export function Finder() { return ( diff --git a/src/app/(auth)/(withMenuNavigation)/profile/[id]/Profile.tsx b/src/app/(auth)/(withMenuNavigation)/profile/[id]/Profile.tsx index 7dbc1aa..8846501 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/[id]/Profile.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/[id]/Profile.tsx @@ -19,8 +19,8 @@ import { AUTH_PAGE } from '@/lib/config/routes.config'; import type { PersonDetailDTO } from '@/api/axios-client/models'; import UserProfileClubs from '@/app/(auth)/(withMenuNavigation)/profile/[id]/UserProfileClubs'; -import { Header } from '@/hoc/Header/Header'; -import { MainContent } from '@/hoc/MainContent/MainContent'; +import { Header } from '@/hoc/Header'; +import { MainContent } from '@/hoc/MainContent'; type Props = { user: PersonDetailDTO; diff --git a/src/app/(auth)/(withMenuNavigation)/profile/[id]/clubs/page.tsx b/src/app/(auth)/(withMenuNavigation)/profile/[id]/clubs/page.tsx index 58c8e90..0b61261 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/[id]/clubs/page.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/[id]/clubs/page.tsx @@ -7,8 +7,8 @@ import { usersApi } from '@/api/api'; import type { PersonDetailDTO } from '@/api/axios-client'; import ProfileClubs from './ProfileClubs'; -import { Header, HeaderTitle } from '@/hoc/Header/Header'; -import { MainContent } from '@/hoc/MainContent/MainContent'; +import { Header, HeaderTitle } from '@/hoc/Header'; +import { MainContent } from '@/hoc/MainContent'; export const metadata: Metadata = { title: 'Подписки', diff --git a/src/app/(auth)/(withMenuNavigation)/profile/settings/Settings.tsx b/src/app/(auth)/(withMenuNavigation)/profile/settings/Settings.tsx index 972953b..ada36c9 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/settings/Settings.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/settings/Settings.tsx @@ -6,8 +6,8 @@ import { Menu, MenuLink } from '@/components/ui/menu'; import MenuLogoutItem from '@/app/(auth)/(withMenuNavigation)/profile/settings/MenuLogoutItem'; import UserCard from '@/app/(auth)/(withMenuNavigation)/profile/settings/UserCard'; import UserClubs from '@/app/(auth)/(withMenuNavigation)/profile/settings/UserClubs'; -import { Header, HeaderTitle } from '@/hoc/Header/Header'; -import { MainContent } from '@/hoc/MainContent/MainContent'; +import { Header, HeaderTitle } from '@/hoc/Header'; +import { MainContent } from '@/hoc/MainContent'; export default function Settings() { return ( diff --git a/src/app/(auth)/(withMenuNavigation)/profile/settings/about/About.tsx b/src/app/(auth)/(withMenuNavigation)/profile/settings/about/About.tsx index 8f03865..fec9f1d 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/settings/about/About.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/settings/about/About.tsx @@ -5,8 +5,8 @@ import { Page } from '@/components/Page'; import SettingsSection from '@/components/ProfileComponents/SettingSection'; import { BackButton } from '@/components/ui/BackButton'; -import { Header, HeaderTitle } from '@/hoc/Header/Header'; -import { MainContent } from '@/hoc/MainContent/MainContent'; +import { Header, HeaderTitle } from '@/hoc/Header'; +import { MainContent } from '@/hoc/MainContent'; type Developer = { name: string; diff --git a/src/app/(auth)/(withMenuNavigation)/profile/settings/appearance/appearance.tsx b/src/app/(auth)/(withMenuNavigation)/profile/settings/appearance/appearance.tsx index 0a8b3c2..4529067 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/settings/appearance/appearance.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/settings/appearance/appearance.tsx @@ -7,8 +7,8 @@ import { Page } from '@/components/Page'; import { BackButton } from '@/components/ui/BackButton'; import { Menu, MenuRadio } from '@/components/ui/menu'; -import { Header, HeaderTitle } from '@/hoc/Header/Header'; -import { MainContent } from '@/hoc/MainContent/MainContent'; +import { Header, HeaderTitle } from '@/hoc/Header'; +import { MainContent } from '@/hoc/MainContent'; export default function Appearance() { const { theme, setTheme } = useTheme(); diff --git a/src/app/(auth)/(withMenuNavigation)/profile/settings/club/[id]/SettingClubPage.tsx b/src/app/(auth)/(withMenuNavigation)/profile/settings/club/[id]/SettingClubPage.tsx index 27a34bc..c8e5b34 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/settings/club/[id]/SettingClubPage.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/settings/club/[id]/SettingClubPage.tsx @@ -17,8 +17,8 @@ import { AUTH_PAGE } from '@/lib/config/routes.config'; import { clubsApi } from '@/api/api'; import type { ClubDetailDTO, PersonDetailDTO } from '@/api/axios-client'; -import { Header, HeaderTitle } from '@/hoc/Header/Header'; -import { MainContent } from '@/hoc/MainContent/MainContent'; +import { Header, HeaderTitle } from '@/hoc/Header'; +import { MainContent } from '@/hoc/MainContent'; const RemovePopupDynamic = dynamic( () => import('@/components/SettingClubComponents/RemovePopup').then((mod) => mod.RemovePopup), diff --git a/src/app/(auth)/(withMenuNavigation)/profile/settings/create-club/CreateClub.tsx b/src/app/(auth)/(withMenuNavigation)/profile/settings/create-club/CreateClub.tsx index ecc736a..7a2786e 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/settings/create-club/CreateClub.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/settings/create-club/CreateClub.tsx @@ -17,8 +17,8 @@ import { Textarea } from '@/components/ui/textarea'; import { clubCreationRequestsApi } from '@/api/api.admin'; -import { Header, HeaderTitle } from '@/hoc/Header/Header'; -import { MainContent } from '@/hoc/MainContent/MainContent'; +import { Header, HeaderTitle } from '@/hoc/Header'; +import { MainContent } from '@/hoc/MainContent'; import { type ClubFormValues, clubSchema } from '@/lib/schemas/create-club'; type ImageType = 'banner' | 'avatar'; diff --git a/src/app/(auth)/(withMenuNavigation)/profile/settings/edit/Edit.tsx b/src/app/(auth)/(withMenuNavigation)/profile/settings/edit/Edit.tsx index 3dacd67..d2573f8 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/settings/edit/Edit.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/settings/edit/Edit.tsx @@ -1,8 +1,8 @@ import { Page } from '@/components/Page'; import { BackButton } from '@/components/ui/BackButton'; -import { Header, HeaderTitle } from '@/hoc/Header/Header'; -import { MainContent } from '@/hoc/MainContent/MainContent'; +import { Header, HeaderTitle } from '@/hoc/Header'; +import { MainContent } from '@/hoc/MainContent'; export default function Edit() { return ( diff --git a/src/app/(auth)/(withMenuNavigation)/profile/settings/feedback/Feedback.tsx b/src/app/(auth)/(withMenuNavigation)/profile/settings/feedback/Feedback.tsx index 609d788..4db344c 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/settings/feedback/Feedback.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/settings/feedback/Feedback.tsx @@ -1,8 +1,8 @@ import { Page } from '@/components/Page'; import { BackButton } from '@/components/ui/BackButton'; -import { Header, HeaderTitle } from '@/hoc/Header/Header'; -import { MainContent } from '@/hoc/MainContent/MainContent'; +import { Header, HeaderTitle } from '@/hoc/Header'; +import { MainContent } from '@/hoc/MainContent'; export default function Feedback() { return ( diff --git a/src/app/(auth)/(withMenuNavigation)/profile/settings/notifications/notifications.tsx b/src/app/(auth)/(withMenuNavigation)/profile/settings/notifications/notifications.tsx index 31e2a3f..3746a56 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/settings/notifications/notifications.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/settings/notifications/notifications.tsx @@ -1,8 +1,8 @@ import { Page } from '@/components/Page'; import { BackButton } from '@/components/ui/BackButton'; -import { Header, HeaderTitle } from '@/hoc/Header/Header'; -import { MainContent } from '@/hoc/MainContent/MainContent'; +import { Header, HeaderTitle } from '@/hoc/Header'; +import { MainContent } from '@/hoc/MainContent'; export default function Notifications() { return ( diff --git a/src/app/(auth)/post/comments/[id]/CommentsPage.tsx b/src/app/(auth)/post/comments/[id]/CommentsPage.tsx index 29781b5..6de7ba8 100644 --- a/src/app/(auth)/post/comments/[id]/CommentsPage.tsx +++ b/src/app/(auth)/post/comments/[id]/CommentsPage.tsx @@ -5,8 +5,8 @@ import { BackButton } from '@/components/ui/BackButton'; import type { PostDetailDTO } from '@/api/axios-client/models/post-detail-dto'; import { CommentList } from './CommentList'; -import { Header, HeaderTitle } from '@/hoc/Header/Header'; -import { MainContent } from '@/hoc/MainContent/MainContent'; +import { Header, HeaderTitle } from '@/hoc/Header'; +import { MainContent } from '@/hoc/MainContent'; export function CommentsPage({ post }: { post: PostDetailDTO }) { console.log('123'); diff --git a/src/app/(auth)/post/draft/DraftPage.tsx b/src/app/(auth)/post/draft/DraftPage.tsx index c99667b..5602e8e 100644 --- a/src/app/(auth)/post/draft/DraftPage.tsx +++ b/src/app/(auth)/post/draft/DraftPage.tsx @@ -10,8 +10,8 @@ import { BackButton } from '@/components/ui/BackButton'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; -import { Header, HeaderTitle } from '@/hoc/Header/Header'; -import { MainContent } from '@/hoc/MainContent/MainContent'; +import { Header, HeaderTitle } from '@/hoc/Header'; +import { MainContent } from '@/hoc/MainContent'; const EditorDynamic = dynamic(() => import('@/components/ui/TextEditor/Editor').then((mod) => mod.Editor), { ssr: false, diff --git a/src/components/ClubComponents/ClubFeed.tsx b/src/components/ClubComponents/ClubFeed.tsx index 5027ab3..12dfb82 100644 --- a/src/components/ClubComponents/ClubFeed.tsx +++ b/src/components/ClubComponents/ClubFeed.tsx @@ -15,7 +15,7 @@ import Loader from '../Loader'; import { PostCard } from '../PostCard/PostCard'; import { Button } from '../ui/button'; -import { MainContent } from '@/hoc/MainContent/MainContent'; +import { MainContent } from '@/hoc/MainContent'; export function ClubFeed() { const { id } = useParams(); diff --git a/src/hoc/Header/Header.tsx b/src/hoc/Header.tsx similarity index 100% rename from src/hoc/Header/Header.tsx rename to src/hoc/Header.tsx diff --git a/src/hoc/MainContent/MainContent.tsx b/src/hoc/MainContent.tsx similarity index 100% rename from src/hoc/MainContent/MainContent.tsx rename to src/hoc/MainContent.tsx From 21ac182221cd0552b2c9ffc7afc21dda07797eb0 Mon Sep 17 00:00:00 2001 From: Konstantin Zhigaylo Date: Mon, 29 Sep 2025 20:37:45 +0300 Subject: [PATCH 15/20] =?UTF-8?q?feat:=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B9=20?= =?UTF-8?q?=D1=81=D1=82=D0=B8=D0=BB=D1=8C=20=D1=81=D1=82=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=86=D1=8B=20404?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(withMenuNavigation)/clubs/Clubs.tsx | 14 ++------ .../clubs/[id]/ClubNotFound.tsx | 23 ++++++++----- .../(withMenuNavigation)/clubs/[id]/page.tsx | 6 ++-- .../(auth)/(withMenuNavigation)/not-found.tsx | 23 +++++++++++++ .../profile/[id]/page.tsx | 24 +++++++------ .../profile/settings/Settings.tsx | 6 ++-- .../settings/create-club/CreateClub.tsx | 20 +++++------ src/app/not-found.tsx | 18 +++++++--- src/components/ClubComponents/ClubInfo.tsx | 12 ++----- .../SubscribeButtons/SubscribeButton.tsx | 34 +++++++++++++------ src/components/MainLoader.tsx | 15 ++++++-- .../MenuNavigation/MenuNavigation.tsx | 2 +- src/components/SubscriberCard.tsx | 2 -- src/css/globals.css | 6 ++-- 14 files changed, 125 insertions(+), 80 deletions(-) create mode 100644 src/app/(auth)/(withMenuNavigation)/not-found.tsx diff --git a/src/app/(auth)/(withMenuNavigation)/clubs/Clubs.tsx b/src/app/(auth)/(withMenuNavigation)/clubs/Clubs.tsx index be198a8..7cd141c 100644 --- a/src/app/(auth)/(withMenuNavigation)/clubs/Clubs.tsx +++ b/src/app/(auth)/(withMenuNavigation)/clubs/Clubs.tsx @@ -1,6 +1,5 @@ 'use client'; -import { m } from 'framer-motion'; import { useState } from 'react'; import { useDebounce } from 'use-debounce'; @@ -48,19 +47,12 @@ export function Clubs() { -
+
{isLoading && } {clubs?.pages .flatMap((page) => page) - .map((club, index) => ( - - - + .map((club) => ( + ))}
diff --git a/src/app/(auth)/(withMenuNavigation)/clubs/[id]/ClubNotFound.tsx b/src/app/(auth)/(withMenuNavigation)/clubs/[id]/ClubNotFound.tsx index ca4c26e..2d19f14 100644 --- a/src/app/(auth)/(withMenuNavigation)/clubs/[id]/ClubNotFound.tsx +++ b/src/app/(auth)/(withMenuNavigation)/clubs/[id]/ClubNotFound.tsx @@ -1,16 +1,23 @@ -'use client'; - -import { useRouter } from 'next/navigation'; +import Link from 'next/link'; +import { MdOutlineWifiTetheringError } from 'react-icons/md'; import { Button } from '@/components/ui/button'; export default function ClubNotFound() { - const router = useRouter(); return ( -
-

404

-

Клуб не найден

- +
+ + +
+

Клуб не найден

+

+ Проверьте, что ID клуба в адресе указан верно. В другом случае, его могло и не существовать. +

+
+ + + +
); } diff --git a/src/app/(auth)/(withMenuNavigation)/clubs/[id]/page.tsx b/src/app/(auth)/(withMenuNavigation)/clubs/[id]/page.tsx index 1829d3f..6fb82ca 100644 --- a/src/app/(auth)/(withMenuNavigation)/clubs/[id]/page.tsx +++ b/src/app/(auth)/(withMenuNavigation)/clubs/[id]/page.tsx @@ -1,11 +1,9 @@ -import { notFound } from 'next/navigation'; - import type { ClubDetailDTO } from '@/api/axios-client'; import { Club } from './Club'; +import ClubNotFound from '@/app/(auth)/(withMenuNavigation)/clubs/[id]/ClubNotFound'; import { getClubGetByIdAction } from '@/server-actions/actions/clubs.action'; -export const dynamic = 'force-dynamic'; export const revalidate = 60; export async function generateMetadata({ params }: { params: Promise<{ id: string }> }) { @@ -31,6 +29,6 @@ export default async function Page(props: { params: Promise<{ id: string }> }) { console.log(club); return ; } catch { - return notFound(); + return ; } } diff --git a/src/app/(auth)/(withMenuNavigation)/not-found.tsx b/src/app/(auth)/(withMenuNavigation)/not-found.tsx new file mode 100644 index 0000000..fbc9ec2 --- /dev/null +++ b/src/app/(auth)/(withMenuNavigation)/not-found.tsx @@ -0,0 +1,23 @@ +import Link from 'next/link'; +import { MdOutlineWifiTetheringError } from 'react-icons/md'; + +import { Button } from '@/components/ui/button'; + +export default async function NotFound() { + return ( +
+ + +
+

Страница не найдена

+

+ Страница которую вы искали к сожаления не найдена. Проверьте правильность адреса. +

+
+ + + + +
+ ); +} diff --git a/src/app/(auth)/(withMenuNavigation)/profile/[id]/page.tsx b/src/app/(auth)/(withMenuNavigation)/profile/[id]/page.tsx index df26689..eae3ae6 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/[id]/page.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/[id]/page.tsx @@ -1,4 +1,5 @@ import type { Metadata } from 'next'; +import { notFound } from 'next/navigation'; import { usersApi } from '@/api/api'; import type { PersonDetailDTO } from '@/api/axios-client'; @@ -16,15 +17,18 @@ export const metadata: Metadata = { export default async function Page({ params }: { params: Promise<{ id: number }> }) { const { id } = await params; - const user: PersonDetailDTO = (await usersApi.usersGetById(id)).data; - - let currentPerson = await getPersonIdFromToken(); - if (!currentPerson) { - console.log('Не удалось получить текущего пользователя из токена'); - currentPerson = '0'; + try { + const user: PersonDetailDTO = (await usersApi.usersGetById(id)).data; + let currentPerson = await getPersonIdFromToken(); + if (!currentPerson) { + console.log('Не удалось получить текущего пользователя из токена'); + currentPerson = '0'; + } + + const isCurrent = user.id === Number(currentPerson); + + return ; + } catch (error) { + return notFound(); } - - const isCurrent = user.id === Number(currentPerson); - - return ; } diff --git a/src/app/(auth)/(withMenuNavigation)/profile/settings/Settings.tsx b/src/app/(auth)/(withMenuNavigation)/profile/settings/Settings.tsx index ada36c9..0d54b9d 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/settings/Settings.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/settings/Settings.tsx @@ -21,15 +21,15 @@ export default function Settings() {
-

Основные

+

Основные

{PROFILE_SETTING_SECTIONS.sections.map((section, index) => ( ))} -

Ваши клубы

+

Ваши клубы

-

Прочее

+

Прочее

{PROFILE_SETTING_SECTIONS.other.map((section, index) => ( diff --git a/src/app/(auth)/(withMenuNavigation)/profile/settings/create-club/CreateClub.tsx b/src/app/(auth)/(withMenuNavigation)/profile/settings/create-club/CreateClub.tsx index 7a2786e..e2a8463 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/settings/create-club/CreateClub.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/settings/create-club/CreateClub.tsx @@ -178,9 +178,7 @@ export default function CreateClub() { )} />
-

- Нажмите на аватар или баннер чтобы изменить его -

+

Нажмите на аватар или баннер чтобы изменить его

{errors.banner && (

{errors.banner.message?.toString()}

)} @@ -189,18 +187,18 @@ export default function CreateClub() { )}
-

Название клуба

+

Название клуба

{errors.clubName && (

{errors.clubName.message?.toString()}

)} -

+

Название клуба отражает к какой тематике он относиться, или к какому отделу в РТУ МИРЭА оно относиться.

-

Описание клуба

+

Описание клуба