mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
fix: profile issues
This commit is contained in:
parent
b3ef0eb10b
commit
fe38020f02
5 changed files with 74 additions and 26 deletions
|
|
@ -14,7 +14,8 @@ import {
|
||||||
Laptop2,
|
Laptop2,
|
||||||
Users,
|
Users,
|
||||||
Calendar,
|
Calendar,
|
||||||
Lightbulb
|
Lightbulb,
|
||||||
|
X
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
import { getUserAvatarMediaDirectory } from '@services/media/media'
|
import { getUserAvatarMediaDirectory } from '@services/media/media'
|
||||||
|
|
||||||
|
|
@ -38,7 +39,36 @@ const ICON_MAP = {
|
||||||
'calendar': Calendar,
|
'calendar': Calendar,
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
|
// Add Modal component
|
||||||
|
const ImageModal: React.FC<{
|
||||||
|
image: { url: string; caption?: string };
|
||||||
|
onClose: () => void;
|
||||||
|
}> = ({ image, onClose }) => {
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 bg-black/80 z-50 flex items-center justify-center p-4">
|
||||||
|
<div className="relative max-w-4xl w-full">
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="absolute -top-10 right-0 text-white hover:text-gray-300 transition-colors"
|
||||||
|
>
|
||||||
|
<X className="w-6 h-6" />
|
||||||
|
</button>
|
||||||
|
<img
|
||||||
|
src={image.url}
|
||||||
|
alt={image.caption || ''}
|
||||||
|
className="w-full h-auto rounded-lg"
|
||||||
|
/>
|
||||||
|
{image.caption && (
|
||||||
|
<p className="mt-4 text-white text-center text-lg">{image.caption}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
function UserProfileClient({ userData, profile }: UserProfileClientProps) {
|
function UserProfileClient({ userData, profile }: UserProfileClientProps) {
|
||||||
|
const [selectedImage, setSelectedImage] = React.useState<{ url: string; caption?: string } | null>(null);
|
||||||
|
|
||||||
const IconComponent = ({ iconName }: { iconName: string }) => {
|
const IconComponent = ({ iconName }: { iconName: string }) => {
|
||||||
const IconElement = ICON_MAP[iconName as keyof typeof ICON_MAP]
|
const IconElement = ICON_MAP[iconName as keyof typeof ICON_MAP]
|
||||||
if (!IconElement) return null
|
if (!IconElement) return null
|
||||||
|
|
@ -127,6 +157,30 @@ function UserProfileClient({ userData, profile }: UserProfileClientProps) {
|
||||||
<div key={index} className="mb-8">
|
<div key={index} className="mb-8">
|
||||||
<h2 className="text-xl font-semibold mb-4">{section.title}</h2>
|
<h2 className="text-xl font-semibold mb-4">{section.title}</h2>
|
||||||
|
|
||||||
|
{/* Add Image Gallery section */}
|
||||||
|
{section.type === 'image-gallery' && (
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
|
||||||
|
{section.images.map((image: any, imageIndex: number) => (
|
||||||
|
<div
|
||||||
|
key={imageIndex}
|
||||||
|
className="relative group cursor-pointer"
|
||||||
|
onClick={() => setSelectedImage(image)}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={image.url}
|
||||||
|
alt={image.caption || ''}
|
||||||
|
className="w-full h-48 object-cover rounded-lg"
|
||||||
|
/>
|
||||||
|
{image.caption && (
|
||||||
|
<div className="absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition-opacity duration-200 rounded-lg flex items-center justify-center p-4">
|
||||||
|
<p className="text-white text-center text-sm">{image.caption}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{section.type === 'text' && (
|
{section.type === 'text' && (
|
||||||
<div className="prose max-w-none">{section.content}</div>
|
<div className="prose max-w-none">{section.content}</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -227,6 +281,14 @@ function UserProfileClient({ userData, profile }: UserProfileClientProps) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Image Modal */}
|
||||||
|
{selectedImage && (
|
||||||
|
<ImageModal
|
||||||
|
image={selectedImage}
|
||||||
|
onClose={() => setSelectedImage(null)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,17 +17,11 @@ interface UserPageProps {
|
||||||
|
|
||||||
export async function generateMetadata({ params }: UserPageProps): Promise<Metadata> {
|
export async function generateMetadata({ params }: UserPageProps): Promise<Metadata> {
|
||||||
try {
|
try {
|
||||||
const session = await getServerSession(nextAuthOptions)
|
|
||||||
const access_token = session?.tokens?.access_token
|
|
||||||
const resolvedParams = await params
|
const resolvedParams = await params
|
||||||
|
|
||||||
if (!access_token) {
|
|
||||||
return {
|
|
||||||
title: 'User Profile',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const userData = await getUserByUsername(resolvedParams.username, access_token)
|
|
||||||
|
const userData = await getUserByUsername(resolvedParams.username)
|
||||||
return {
|
return {
|
||||||
title: `${userData.first_name} ${userData.last_name} | Profile`,
|
title: `${userData.first_name} ${userData.last_name} | Profile`,
|
||||||
description: userData.bio || `Profile page of ${userData.first_name} ${userData.last_name}`,
|
description: userData.bio || `Profile page of ${userData.first_name} ${userData.last_name}`,
|
||||||
|
|
@ -44,16 +38,8 @@ async function UserPage({ params }: UserPageProps) {
|
||||||
const { username } = resolvedParams;
|
const { username } = resolvedParams;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get access token from server session
|
|
||||||
const session = await getServerSession(nextAuthOptions)
|
|
||||||
const access_token = session?.tokens?.access_token
|
|
||||||
|
|
||||||
if (!access_token) {
|
|
||||||
throw new Error('No access token available')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch user data by username
|
// Fetch user data by username
|
||||||
const userData = await getUserByUsername(username, access_token);
|
const userData = await getUserByUsername(username);
|
||||||
const profile = userData.profile ? (
|
const profile = userData.profile ? (
|
||||||
typeof userData.profile === 'string' ? JSON.parse(userData.profile) : userData.profile
|
typeof userData.profile === 'string' ? JSON.parse(userData.profile) : userData.profile
|
||||||
) : { sections: [] };
|
) : { sections: [] };
|
||||||
|
|
|
||||||
|
|
@ -164,7 +164,7 @@ const UserProfileBuilder = () => {
|
||||||
if (session?.data?.user?.id && access_token) {
|
if (session?.data?.user?.id && access_token) {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
const userData = await getUser(session.data.user.id, access_token)
|
const userData = await getUser(session.data.user.id)
|
||||||
|
|
||||||
if (userData.profile) {
|
if (userData.profile) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -291,7 +291,7 @@ const UserProfileBuilder = () => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get fresh user data before update
|
// Get fresh user data before update
|
||||||
const userData = await getUser(session.data.user.id, access_token)
|
const userData = await getUser(session.data.user.id)
|
||||||
|
|
||||||
// Update only the profile field
|
// Update only the profile field
|
||||||
userData.profile = profileData
|
userData.profile = profileData
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,9 @@ function UserAvatar(props: UserAvatarProps) {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchUserByUsername = async () => {
|
const fetchUserByUsername = async () => {
|
||||||
if (props.username && session?.data?.tokens?.access_token) {
|
if (props.username) {
|
||||||
try {
|
try {
|
||||||
const data = await getUserByUsername(props.username, session.data.tokens.access_token)
|
const data = await getUserByUsername(props.username)
|
||||||
setUserData(data)
|
setUserData(data)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching user by username:', error)
|
console.error('Error fetching user by username:', error)
|
||||||
|
|
@ -38,7 +38,7 @@ function UserAvatar(props: UserAvatarProps) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchUserByUsername()
|
fetchUserByUsername()
|
||||||
}, [props.username, session?.data?.tokens?.access_token])
|
}, [props.username])
|
||||||
|
|
||||||
const isExternalUrl = (url: string): boolean => {
|
const isExternalUrl = (url: string): boolean => {
|
||||||
return url.startsWith('http://') || url.startsWith('https://')
|
return url.startsWith('http://') || url.startsWith('https://')
|
||||||
|
|
@ -86,7 +86,7 @@ function UserAvatar(props: UserAvatarProps) {
|
||||||
return getUserAvatarMediaDirectory(userData.user_uuid, avatarUrl)
|
return getUserAvatarMediaDirectory(userData.user_uuid, avatarUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If user has an avatar in session
|
// If user has an avatar in session (only if session exists)
|
||||||
if (session?.data?.user?.avatar_image) {
|
if (session?.data?.user?.avatar_image) {
|
||||||
const avatarUrl = session.data.user.avatar_image
|
const avatarUrl = session.data.user.avatar_image
|
||||||
// If it's an external URL (e.g., from Google, Facebook, etc.), use it directly
|
// If it's an external URL (e.g., from Google, Facebook, etc.), use it directly
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import {
|
||||||
getResponseMetadata,
|
getResponseMetadata,
|
||||||
} from '@services/utils/ts/requests'
|
} from '@services/utils/ts/requests'
|
||||||
|
|
||||||
export async function getUser(user_id: string, access_token: string) {
|
export async function getUser(user_id: string) {
|
||||||
const result = await fetch(
|
const result = await fetch(
|
||||||
`${getAPIUrl()}users/id/${user_id}`,
|
`${getAPIUrl()}users/id/${user_id}`,
|
||||||
RequestBody('GET', null, null)
|
RequestBody('GET', null, null)
|
||||||
|
|
@ -16,7 +16,7 @@ export async function getUser(user_id: string, access_token: string) {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getUserByUsername(username: string, access_token: string) {
|
export async function getUserByUsername(username: string) {
|
||||||
const result = await fetch(
|
const result = await fetch(
|
||||||
`${getAPIUrl()}users/username/${username}`,
|
`${getAPIUrl()}users/username/${username}`,
|
||||||
RequestBody('GET', null, null)
|
RequestBody('GET', null, null)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue