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/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)/(withMenuNavigation)/(home)/Home.tsx b/src/app/(auth)/(withMenuNavigation)/(home)/Home.tsx index d49a58b..aad22ba 100644 --- a/src/app/(auth)/(withMenuNavigation)/(home)/Home.tsx +++ b/src/app/(auth)/(withMenuNavigation)/(home)/Home.tsx @@ -1,33 +1,15 @@ -'use client'; +import { Suspense } from 'react'; -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 { Header, HeaderTitle } from '@/hoc/Header/Header'; -import { MainContent } from '@/hoc/MainContent/MainContent'; +import HomeFeed from '@/app/(auth)/(withMenuNavigation)/(home)/HomeFeed'; +import { Header, HeaderTitle } from '@/hoc/Header'; +import { MainContent } from '@/hoc/MainContent'; import { parseLocalTime } from '@/lib/utils/time.util'; -const PostCardDynamic = dynamic(() => import('@/components/PostCard/PostCard').then((mod) => mod.PostCard), { - loading: () => , -}); +export const dynamic = 'force-static'; 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 +18,10 @@ 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/Clubs.tsx b/src/app/(auth)/(withMenuNavigation)/clubs/Clubs.tsx index 251da76..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'; @@ -13,8 +12,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(''); @@ -52,15 +51,8 @@ export function Clubs() { {isLoading && } {clubs?.pages .flatMap((page) => page) - .map((club, index) => ( - - - + .map((club) => ( + ))}
diff --git a/src/app/(auth)/(withMenuNavigation)/clubs/[id]/Club.tsx b/src/app/(auth)/(withMenuNavigation)/clubs/[id]/Club.tsx index 887a9ae..f1267d1 100644 --- a/src/app/(auth)/(withMenuNavigation)/clubs/[id]/Club.tsx +++ b/src/app/(auth)/(withMenuNavigation)/clubs/[id]/Club.tsx @@ -1,25 +1,20 @@ -'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 ClubReportDialog from '@/components/ClubComponents/ClubReportDialog'; 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, -}); - export function Club({ club }: { club: ClubDetailDTO }) { return (
- + + - +
); } 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 af6e132..6fb82ca 100644 --- a/src/app/(auth)/(withMenuNavigation)/clubs/[id]/page.tsx +++ b/src/app/(auth)/(withMenuNavigation)/clubs/[id]/page.tsx @@ -1,7 +1,11 @@ +import type { ClubDetailDTO } from '@/api/axios-client'; + import { Club } from './Club'; -import ClubNotFound from './ClubNotFound'; +import ClubNotFound from '@/app/(auth)/(withMenuNavigation)/clubs/[id]/ClubNotFound'; import { getClubGetByIdAction } from '@/server-actions/actions/clubs.action'; +export const revalidate = 60; + export async function generateMetadata({ params }: { params: Promise<{ id: string }> }) { try { const id = (await params).id; @@ -20,9 +24,9 @@ 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 getClubGetByIdAction(id); + console.log(club); return ; } catch { return ; diff --git a/src/app/(auth)/(withMenuNavigation)/clubs/[id]/subscribers/Subscribers.tsx b/src/app/(auth)/(withMenuNavigation)/clubs/[id]/subscribers/Subscribers.tsx index 84d2075..c3eb720 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'; @@ -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/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..a6a7aa2 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,88 @@ 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 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..e4119dc 100644 --- a/src/app/(auth)/(withMenuNavigation)/events/calendar/Calendar.tsx +++ b/src/app/(auth)/(withMenuNavigation)/events/calendar/Calendar.tsx @@ -2,11 +2,10 @@ 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'; -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'; @@ -15,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() { @@ -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..1111f0c 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'; +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'; +import { MainContent } from '@/hoc/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/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)/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)/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/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..8846501 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,37 @@ 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 { Header } from '@/hoc/Header/Header'; -import { MainContent } from '@/hoc/MainContent/MainContent'; +import UserProfileClubs from '@/app/(auth)/(withMenuNavigation)/profile/[id]/UserProfileClubs'; +import { Header } from '@/hoc/Header'; +import { MainContent } from '@/hoc/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 const dynamic = 'force-dynamic'; +export const revalidate = 60; +export default function Profile({ user, isCurrent }: Props) { return ( -
+
+

Профиль

+ + {isCurrent && ( + + + + )} - { - navigator.clipboard.writeText(`https://setka-rtu.ru${pathname}`); - toast.success('Ссылка скопирована'); - }} - > + Скопировать ссылку - - - Пожаловаться - + {!isCurrent && ( + + + Пожаловаться + + )}
@@ -101,18 +94,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..0b61261 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'; +import { MainContent } from '@/hoc/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..9b307c2 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/[id]/page.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/[id]/page.tsx @@ -1,24 +1,33 @@ import type { Metadata } from 'next'; -import { redirect } from 'next/navigation'; +import { notFound } 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 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; + try { + const user: PersonDetailDTO = (await usersApi.usersGetById(id)).data; + let currentPerson = await getPersonIdFromToken(); + if (!currentPerson) { + console.log('Не удалось получить текущего пользователя из токена'); + currentPerson = '0'; + } - if (currentUser.id === user.id) redirect(AUTH_PAGE.PROFILE); + const isCurrent = user.id === Number(currentPerson); - return ; + return ; + } catch (error) { + return notFound(); + } } 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..8f0af9b 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/page.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/page.tsx @@ -1,12 +1,15 @@ -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 const dynamic = 'force-dynamic'; export default async function Page() { - return ; + const currentPerson = await getPersonIdFromToken(); + if (!currentPerson) { + redirect('/'); + } + redirect(AUTH_PAGE.USER_PROFILE(Number(currentPerson))); } 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..0d54b9d 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 { useProfile } from '@/hooks/useProfile'; - -import { authApi } from '@/api/api'; -import { type PersonDetailDTO } from '@/api/axios-client'; - -import { useOwnedClubs } from './useOwnedClubs'; -import { Header, HeaderTitle } from '@/hoc/Header/Header'; -import { MainContent } from '@/hoc/MainContent/MainContent'; -import { getStaticImg } from '@/lib/helpers/getStaticImg.helper'; +import { Menu, MenuLink } from '@/components/ui/menu'; -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', - } - ); - }; +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'; +import { MainContent } from '@/hoc/MainContent'; +export default function Settings() { return (
@@ -66,45 +18,20 @@ export default function Settings({ initUser }: { initUser: PersonDetailDTO }) {
- -
-

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

-

{user?.about}

-
+
-

Основные

+

Основные

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

Ваши клубы

- - {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/about/About.tsx b/src/app/(auth)/(withMenuNavigation)/profile/settings/about/About.tsx index 6d3de4f..fec9f1d 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/settings/about/About.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/settings/about/About.tsx @@ -1,14 +1,12 @@ -'use client'; - 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'; -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; @@ -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} /> diff --git a/src/app/(auth)/(withMenuNavigation)/profile/settings/appearance/appearance.tsx b/src/app/(auth)/(withMenuNavigation)/profile/settings/appearance/appearance.tsx index 8d87633..4529067 100644 --- a/src/app/(auth)/(withMenuNavigation)/profile/settings/appearance/appearance.tsx +++ b/src/app/(auth)/(withMenuNavigation)/profile/settings/appearance/appearance.tsx @@ -1,27 +1,17 @@ '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'; 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(); - const [mounted, setMounted] = useState(false); - - useEffect(() => { - setMounted(true); - }, []); - - if (!mounted) { - return null; - } const handleThemeChange = (newTheme: string) => { setTheme(newTheme); 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..e2a8463 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'; @@ -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()}

)} -

+

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

-

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

+

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