summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorl3wdfut4pwr <l3wdfut4pwr@gmail.com>2026-04-03 23:59:33 +0300
committerl3wdfut4pwr <l3wdfut4pwr@gmail.com>2026-04-03 23:59:33 +0300
commit9c65a9c271dfda5ea17c9d909bc9e7e6d0c040ab (patch)
tree9df45f5df63fb509e1b7cdac81659c4713287d70 /src
parent1d20080db8a26e4b7dd4071daac1166142592afa (diff)
add profile
Diffstat (limited to 'src')
-rw-r--r--src/app/globals.css48
-rw-r--r--src/app/profile/[username]/page.tsx83
-rw-r--r--src/app/profile/page.tsx3
-rw-r--r--src/components/header/ProfileOrLogin.tsx6
-rw-r--r--src/components/header/authdialog/RegisterForm.tsx6
-rw-r--r--src/components/settings/ProfilePage.tsx20
-rw-r--r--src/lib/api/user.ts13
-rw-r--r--src/lib/contexts/Auth.context.tsx30
8 files changed, 178 insertions, 31 deletions
diff --git a/src/app/globals.css b/src/app/globals.css
index 5ca3a6b..ca0c3c7 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -27,7 +27,6 @@
--light-violet: #8784c9;
--dark-indigo: #0d0c1c;
--red: #e64c4f;
-
--radius: 0.625rem;
--card: oklch(1 0 0);
--card-foreground: oklch(0.13 0.028 261.692);
@@ -106,6 +105,53 @@
--radius-4xl: calc(var(--radius) + 16px);
}
+@layer utilities {
+ .gradient-0 {
+ background-image: linear-gradient(89.91deg, #8784c9 0%, #464199 99.92%);
+ }
+ .gradient-1 {
+ background-image: linear-gradient(
+ 284.32deg,
+ #ffafbd 20.33%,
+ #dae2f8 79.67%
+ );
+ }
+ .gradient-2 {
+ background-image: linear-gradient(267.9deg, #473b7b 0%, #30d2be 100%);
+ }
+ .gradient-3 {
+ background-image: linear-gradient(88.87deg, #c33764 0%, #1d2671 100%);
+ }
+ .gradient-4 {
+ background-image: linear-gradient(267.9deg, #134e5e 0%, #71b280 100%);
+ }
+ .gradient-5 {
+ background-image: linear-gradient(87.9deg, #423aa2 0%, #100626 100%);
+ }
+ .gradient-6 {
+ background-image: linear-gradient(87.9deg, #edf3f6 0%, #d9eaf4 100%);
+ }
+ .gradient-7 {
+ background-image: linear-gradient(
+ 284.32deg,
+ #212121 20.33%,
+ #454545 79.67%
+ );
+ }
+ .gradient-8 {
+ background-image: linear-gradient(88.87deg, #610000 0%, #190a05 100%);
+ }
+ .gradient-9 {
+ background-image: linear-gradient(87.9deg, #ffc500 0%, #c21500 100%);
+ }
+ .gradient-10 {
+ background-image: linear-gradient(270deg, #6a11cb 0%, #69a1ff 100%);
+ }
+ .gradient-11 {
+ background-image: linear-gradient(267.9deg, #182848 0%, #4b6cb7 100%);
+ }
+}
+
@layer base {
* {
@apply border-violet outline-ring/50;
diff --git a/src/app/profile/[username]/page.tsx b/src/app/profile/[username]/page.tsx
new file mode 100644
index 0000000..cad11bb
--- /dev/null
+++ b/src/app/profile/[username]/page.tsx
@@ -0,0 +1,83 @@
+import Image from 'next/image';
+import { Button } from '@/components/ui';
+import { getUserByUsername } from '@/lib/api/user';
+import { User } from '@/lib/contexts/Auth.context';
+interface ProfilePageProps {
+ params: Promise<{ username: string }>;
+}
+
+export default async function Profile({ params }: ProfilePageProps) {
+ const { username } = await params;
+
+ const profileUser: User = await getUserByUsername(username);
+ const icons = [
+ 'facebook',
+ 'pinterest',
+ 'discord',
+ 'artstation',
+ 'behance',
+ 'instagram',
+ ] as const;
+
+ type IconKey = (typeof icons)[number];
+ return (
+ <>
+ <div className="flex gap-[20px] flex-col">
+ <div className="flex flex-col flex-start padding-0 gap-[20px] h-[551px] w-full">
+ <div className="gradient-0 flex flex-end padding-[20px] gap-[10px] h-[350px] w-full rounded-[20px]"></div>
+ <div className="flex flex-col flex-start gap-[10px] w-full h-[181px]">
+ <div className="flex flex-col gap-1.25">
+ <p className="font-medium text-[24px]">
+ {profileUser.username}
+ </p>
+ </div>
+ <div className="flex flex-row items-center gap-1.25 h-[19px]">
+ <p>
+ {profileUser.profile?.publications_count ?? 0}{' '}
+ публикаций
+ </p>
+ <p>
+ {profileUser.profile?.collections_count ?? 0}{' '}
+ коллекций
+ </p>
+ <p>
+ {profileUser.profile?.followers_count ?? 0}{' '}
+ подписчиков
+ </p>
+ <p>
+ {profileUser.profile?.following_count ?? 0}{' '}
+ подписок
+ </p>
+ </div>
+ <div className="flex max-w-[800px] font-regular text-[12px] gap-1.25">
+ <p>{profileUser.description}</p>
+ </div>
+ <div className="flex flex-row flex-end gap-1.25">
+ {icons
+ .filter(
+ (icon: IconKey) =>
+ profileUser.integrations?.[icon],
+ )
+ .map((icon: IconKey) => (
+ <a
+ key={icon}
+ href={profileUser.integrations![icon]!}
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ <img
+ src={`/profile/${icon}.svg`}
+ alt={icon}
+ className="w-[30px] h-[40px]"
+ />
+ </a>
+ ))}{' '}
+ </div>
+ </div>
+ </div>
+ <div className="flex flex-row space-between flex-start gap-1.25 w-full h-[42px]"></div>
+ <Button>Управление аккаунтом</Button>
+ </div>
+ </>
+ );
+}
diff --git a/src/app/profile/page.tsx b/src/app/profile/page.tsx
deleted file mode 100644
index 17e3f62..0000000
--- a/src/app/profile/page.tsx
+++ /dev/null
@@ -1,3 +0,0 @@
-export default function Profile() {
- return <></>;
-}
diff --git a/src/components/header/ProfileOrLogin.tsx b/src/components/header/ProfileOrLogin.tsx
index 416418b..68c81f6 100644
--- a/src/components/header/ProfileOrLogin.tsx
+++ b/src/components/header/ProfileOrLogin.tsx
@@ -21,11 +21,11 @@ export function ProfileOrLogin() {
</Dialog>
);
}
-
+ const profileLink = `/profile/${user.username}`;
return (
- <Link href={'/profile'}>
+ <Link href={profileLink}>
<Image
- src={user?.avatar ?? 'icons/avatar.svg'}
+ src={user?.avatar ?? '/icons/avatar.svg'}
alt=""
width={60}
height={60}
diff --git a/src/components/header/authdialog/RegisterForm.tsx b/src/components/header/authdialog/RegisterForm.tsx
index c8ddb55..0370671 100644
--- a/src/components/header/authdialog/RegisterForm.tsx
+++ b/src/components/header/authdialog/RegisterForm.tsx
@@ -41,12 +41,8 @@ export default function RegisterForm() {
if (error) {
setErrors(error);
} else if (data) {
- setUser({
- id: data.id,
- username: data.username,
- });
+ setUser(data);
}
-
setLoading(false);
};
diff --git a/src/components/settings/ProfilePage.tsx b/src/components/settings/ProfilePage.tsx
index 60ff41f..9d5b81b 100644
--- a/src/components/settings/ProfilePage.tsx
+++ b/src/components/settings/ProfilePage.tsx
@@ -3,21 +3,6 @@ import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui';
import { Separator } from '@/components/ui';
-const gradients = [
- 'linear-gradient(89.91deg, #8784C9 0%, #464199 99.92%)',
- 'linear-gradient(284.32deg, #FFAFBD 20.33%, #DAE2F8 79.67%)',
- 'linear-gradient(267.9deg, #473B7B 0%, #30D2BE 100%)',
- 'linear-gradient(88.87deg, #C33764 0%, #1D2671 100%)',
- 'linear-gradient(267.9deg, #134E5E 0%, #71B280 100%)',
- 'linear-gradient(87.9deg, #423AA2 0%, #100626 100%)',
- 'linear-gradient(87.9deg, #EDF3F6 0%, #D9EAF4 100%)',
- 'linear-gradient(284.32deg, #212121 20.33%, #454545 79.67%)',
- 'linear-gradient(88.87deg, #610000 0%, #190A05 100%)',
- 'linear-gradient(87.9deg, #FFC500 0%, #C21500 100%)',
- 'linear-gradient(270deg, #6A11CB 0%, #69A1FF 100%)',
- 'linear-gradient(267.9deg, #182848 0%, #4B6CB7 100%)',
-];
-
export default function ProfilePage() {
return (
<div className="flex flex-col flex-start gap-[20px] w-[900px] h-[804px]">
@@ -56,11 +41,10 @@ export default function ProfilePage() {
</p>
<div className="flex flex-row flex-wrap items-center content-start gap-[5px] w-[265px] h-[85px]">
- {gradients.map((gradient, index) => (
+ {Array.from({ length: 12 }).map((_, index) => (
<div
key={index}
- style={{ background: gradient }}
- className="w-[40px] h-[40px] rounded-full cursor-pointer"
+ className={`w-[40px] h-[40px] rounded-full cursor-pointer gradient-${index}`}
/>
))}
</div>
diff --git a/src/lib/api/user.ts b/src/lib/api/user.ts
new file mode 100644
index 0000000..86dfce8
--- /dev/null
+++ b/src/lib/api/user.ts
@@ -0,0 +1,13 @@
+const API_URL = process.env.NEXT_PUBLIC_API_URL;
+
+export async function getUserByUsername(username: string) {
+ if (!username) throw new Error('Username is required');
+
+ const res = await fetch(`${API_URL}/api/users/${username}`, {
+ cache: 'no-store',
+ });
+
+ if (!res.ok) throw new Error(`User not found: ${username}`);
+
+ return res.json();
+}
diff --git a/src/lib/contexts/Auth.context.tsx b/src/lib/contexts/Auth.context.tsx
index fbf2e24..4e567ba 100644
--- a/src/lib/contexts/Auth.context.tsx
+++ b/src/lib/contexts/Auth.context.tsx
@@ -4,12 +4,38 @@ import React, { createContext, useState, useContext, useEffect } from 'react';
export type User = {
id: number;
username: string;
+ email: string;
+ password?: string;
avatar?: string;
banner_file?: string;
+ description?: string;
premium?: boolean;
is_banned?: boolean;
is_moderator?: boolean;
+ token_version?: number;
+ profile?: {
+ id: number;
+ user_id: number;
+ avatar_file?: string;
+ banner_file?: string;
+ description?: string;
+ publications_count?: number;
+ collections_count?: number;
+ subscriptions_count?: number;
+ followers_count?: number;
+ following_count?: number;
+ };
+ integrations?: {
+ facebook?: string | null;
+ pinterest?: string | null;
+ discord?: string | null;
+ artstation?: string | null;
+ x?: string | null; // Twitter/X
+ behance?: string | null;
+ instagram?: string | null;
+ };
};
+
interface AuthContextType {
user: User | null;
setUser: (user: User | null) => void;
@@ -33,7 +59,9 @@ export const AuthContextProvider = ({ children }: React.PropsWithChildren) => {
if (!res.ok) return;
const userData = await res.json();
setUser(userData);
- } catch {}
+ } catch (err) {
+ console.error('Error fetching user:', err);
+ }
};
fetchUser();