Merge my profile and profile/id UI

This commit is contained in:
MartinBraquet
2025-07-31 11:02:51 +02:00
parent 3bab583969
commit 1a5bb39b54
5 changed files with 289 additions and 300 deletions

View File

@@ -534,17 +534,21 @@ function RegisterComponent() {
<div>
<label htmlFor="description" className={headingStyle}>
About You <span className="text-red-500">*</span>
About You
<p className="text-xs italic">
Feel free to include any relevant links (dating / friends docs, etc.), but the more text you share,
the easier it will be to find you by keywords in our search.
</p>
</label>
<textarea
id="description"
name="description"
rows={4}
required
// required
value={description}
onChange={(e) => setDescription(e.target.value)}
className="appearance-none rounded-md relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 focus:outline-none focus:ring-blue-500 focus:border-blue-500 focus:z-10 sm:text-sm"
placeholder="Tell us about yourself, your background, and what you're looking for in connections."
placeholder="Tell us about yourself, your background, and what you're looking for."
/>
</div>
@@ -559,7 +563,7 @@ function RegisterComponent() {
value={contactInfo}
onChange={(e) => setContactInfo(e.target.value)}
className="appearance-none rounded-md relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 focus:outline-none focus:ring-blue-500 focus:border-blue-500 focus:z-10 sm:text-sm"
placeholder="How can people reach you? (Email, social media, etc.)"
placeholder="How can people reach you? (Email, social media, phone, Google Forms, etc.)"
/>
</div>
</div>

View File

@@ -1,11 +1,11 @@
'use client';
import Link from 'next/link';
import Image from 'next/image';
import {useEffect, useState} from "react";
import LoadingSpinner from "@/lib/client/LoadingSpinner";
import {parseImage} from "@/lib/client/media";
import {usePathname} from "next/navigation";
import {getProfile} from "@/lib/client/profile";
export default function ProfilePage() {
const pathname = usePathname(); // Get the current route
@@ -31,123 +31,28 @@ export default function ProfilePage() {
}
console.log('userData', userData);
const name = userData?.name ?? '';
const email = userData?.email ?? '';
const profile = userData?.profile;
const location = profile?.location ?? '';
const description = profile?.description ?? '';
const gender = profile?.gender ?? '';
const personalityType = profile?.personalityType ?? '';
const conflictStyle = profile?.conflictStyle ?? '';
const intellectualInterests: any = profile?.intellectualInterests ?? [];
const header = (
<div className="px-4 py-5 sm:px-6 flex justify-between items-center">
<div>
<h3 className="text-lg leading-6 font-medium ">My Profile</h3>
<p className="mt-1 max-w-2xl text-sm text-gray-500">View and update your profile information</p>
</div>
<Link
href={`/complete-profile?redirect=${encodeURIComponent(pathname)}`}
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
Edit Profile
</Link>
</div>
)
return (
<div className="max-w-4xl mx-auto py-12 px-4 sm:px-6 lg:px-8 ">
<div className=" shadow overflow-hidden sm:rounded-lg ">
<div className="px-4 py-5 sm:px-6 flex justify-between items-center">
<div>
<h3 className="text-lg leading-6 font-medium ">My Profile</h3>
<p className="mt-1 max-w-2xl text-sm text-gray-500">View and update your profile information</p>
</div>
<Link
href={`/complete-profile?redirect=${encodeURIComponent(pathname)}`}
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
Edit Profile
</Link>
</div>
<div className="border-t border-gray-200 px-4 py-5 sm:p-0">
<dl className="sm:divide-y sm:divide-gray-200">
<div className="py-4 sm:py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt className="text-sm font-medium text-gray-500">Profile Picture</dt>
<dd className="mt-1 text-sm sm:mt-0 sm:col-span-2">
<div className="h-24 w-24 rounded-full overflow-hidden bg-gray-100">
{image ? (
<Image
src={image}
alt={name || 'Profile picture'}
width={96}
height={96}
className="h-full w-full object-cover"
priority
/>
) : (
<div className="h-full w-full flex items-center justify-center bg-gray-200 text-gray-400">
<span className="text-2xl">
{name?.charAt(0).toUpperCase() || 'U'}
</span>
</div>
)}
</div>
</dd>
</div>
<div className="py-4 sm:py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 ">
<dt className="text-sm font-medium text-gray-500">Name</dt>
<dd className="mt-1 text-sm sm:mt-0 sm:col-span-2">
{name || 'Not provided'}
</dd>
</div>
<div className="py-4 sm:py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt className="text-sm font-medium text-gray-500">Email</dt>
<dd className="mt-1 text-sm sm:mt-0 sm:col-span-2">
{email}
</dd>
</div>
<div className="py-4 sm:py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt className="text-sm font-medium text-gray-500">Location</dt>
<dd className="mt-1 text-sm sm:mt-0 sm:col-span-2">
{location || 'Not provided'}
</dd>
</div>
<div className="py-4 sm:py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt className="text-sm font-medium text-gray-500">About</dt>
<dd className="mt-1 text-sm sm:mt-0 sm:col-span-2">
{description || 'Not provided'}
</dd>
</div>
<div className="py-4 sm:py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt className="text-sm font-medium text-gray-500">Gender</dt>
<dd className="mt-1 text-sm sm:mt-0 sm:col-span-2">
{gender || 'Not specified'}
</dd>
</div>
<div className="py-4 sm:py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt className="text-sm font-medium text-gray-500">Personality Type</dt>
<dd className="mt-1 text-sm sm:mt-0 sm:col-span-2">
{personalityType || 'Not specified'}
</dd>
</div>
<div className="py-4 sm:py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt className="text-sm font-medium text-gray-500">Conflict Style</dt>
<dd className="mt-1 text-sm sm:mt-0 sm:col-span-2">
{conflictStyle || 'Not specified'}
</dd>
</div>
<div className="py-4 sm:py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt className="text-sm font-medium text-gray-500">Interests</dt>
<dd className="mt-1 text-sm sm:mt-0 sm:col-span-2">
{intellectualInterests.length > 0 ? (
<div className="flex flex-wrap gap-2">
{intellectualInterests.map((value: any, index: number) => (
<span
key={index}
className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800"
>
{value?.interest?.name}
</span>
))}
</div>
) : (
'No interests selected'
)}
</dd>
</div>
</dl>
</div>
</div>
<div className="min-h-screen py-12 px-4 sm:px-6 lg:px-8">
{getProfile(userData, image, header)}
</div>
);
)
;
} catch (error) {
console.error('Error fetching user data:', error);
return <div className="text-center py-10">Error loading profile. Please try again later.</div>;

View File

@@ -2,16 +2,16 @@
import {useEffect, useState} from "react";
import {useParams} from "next/navigation";
import Image from "next/image";
import LoadingSpinner from "@/lib/client/LoadingSpinner";
import {ProfileData} from "@/lib/client/schema";
import {parseImage} from "@/lib/client/media";
import {getProfile} from "@/lib/client/profile";
export const dynamic = "force-dynamic"; // This disables SSG and ISR
export default function Post() {
const {id} = useParams();
const [user, setUser] = useState<ProfileData | null>(null);
const [userData, setUserData] = useState<ProfileData | null>(null);
const [image, setImage] = useState(null);
const [loading, setLoading] = useState(true);
@@ -21,7 +21,7 @@ export default function Post() {
console.log('res', res);
if (res.ok) {
const data = await res.json();
setUser(data);
setUserData(data);
console.log('userData', data);
if (data?.image) {
await parseImage(data.image, setImage);
@@ -37,7 +37,7 @@ export default function Post() {
return <LoadingSpinner/>;
}
if (!user) {
if (!userData) {
return <div>
<h1 className="text-center">Profile not found</h1>
</div>;
@@ -45,185 +45,10 @@ export default function Post() {
console.log(`Image: ${image}`)
const pStyle = "mt-1 text-gray-800 dark:text-white whitespace-pre-line"
return (
<div className="min-h-screen py-12 px-4 sm:px-6 lg:px-8">
<article className="max-w-3xl mx-auto shadow-lg rounded-lg overflow-hidden">
{/* Profile Header with Image */}
<div className="bg-gradient-to-r h-16 relative">
{image ? (
<div className="absolute -bottom-16 left-8">
<div className="h-32 w-32 rounded-full border-4 border-white overflow-hidden ">
<Image
src={image}
alt={user.name || 'Profile picture'}
className="h-full w-full object-cover"
width={200}
height={200}
// onError={(e) => {
// const target = e.target as HTMLImageElement;
// target.onerror = null;
// target.src = `https://ui-avatars.com/api/?name=${encodeURIComponent(profile.name || 'U')}&background=random`;
// }}
/>
</div>
</div>
) : (
<div
className="absolute -bottom-16 left-8 h-32 w-32 rounded-full border-4 border-white bg-gray-200 flex items-center justify-center">
<span className="text-4xl font-bold text-gray-600">
{user.name ? user.name.charAt(0).toUpperCase() : 'U'}
</span>
</div>
)}
</div>
{/* Profile Content */}
<div className="pt-20 px-8 pb-8">
<h1 className="text-3xl font-bold mb-2">
{user.name}
</h1>
<div className="space-y-6 pt-4 border-t border-gray-200">
{user?.profile?.desiredConnections && (
<div className="mt-3"><
h2 className="text-sm font-medium text-gray-500 uppercase tracking-wider">Desired Connections</h2>
<ul className="flex flex-wrap gap-2 mt-1">
{user?.profile?.desiredConnections.map((value, idx) => (
<li
key={idx}
className="px-3 py-1 text-sm bg-blue-100 text-blue-800 dark:text-white dark:bg-gray-700 rounded-full hover:bg-gray-200 transition"
>
{value?.connection?.name}
</li>
))}
</ul>
</div>
)}
{user?.profile?.gender && (
<div>
<h2 className="text-sm font-medium text-gray-500 uppercase tracking-wider">Gender</h2>
<p className="mt-1 capitalize">{user.profile.gender}</p>
</div>
)}
{user?.profile?.location && (
<div>
<h2 className="text-sm font-medium text-gray-500 uppercase tracking-wider">Location</h2>
<p className={pStyle}>{user.profile.location}</p>
</div>
)}
{user?.profile?.occupation && (
<div>
<h2 className="text-sm font-medium text-gray-500 uppercase tracking-wider">Occupation</h2>
<p className={pStyle}>{user.profile.occupation}</p>
</div>
)}
{user?.profile?.personalityType && (
<div>
<h2 className="text-sm font-medium text-gray-500 uppercase tracking-wider">Personality Type</h2>
<p className={pStyle}>{user.profile.personalityType}</p>
</div>
)}
{user?.profile?.conflictStyle && (
<div>
<h2 className="text-sm font-medium text-gray-500 uppercase tracking-wider">Conflict Style</h2>
<p className={pStyle}>{user.profile.conflictStyle}</p>
</div>
)}
{user?.profile?.intellectualInterests && (
<div className="mt-3"><
h2 className="text-sm font-medium text-gray-500 uppercase tracking-wider">Interests</h2>
<ul className="flex flex-wrap gap-2 mt-1">
{user.profile.intellectualInterests.map((value, idx) => (
<li
key={idx}
className="px-3 py-1 text-sm bg-blue-100 text-blue-800 dark:text-white dark:bg-gray-700 rounded-full hover:bg-gray-200 transition"
>
{value?.interest?.name}
</li>
))}
</ul>
</div>
)}
{user?.profile?.causeAreas && (
<div className="mt-3"><
h2 className="text-sm font-medium text-gray-500 uppercase tracking-wider">Cause Areas</h2>
<ul className="flex flex-wrap gap-2 mt-1">
{user.profile.causeAreas.map((value, idx) => (
<li
key={idx}
className="px-3 py-1 text-sm bg-blue-100 text-blue-800 dark:text-white dark:bg-gray-700 rounded-full hover:bg-gray-200 transition"
>
{value?.causeArea?.name}
</li>
))}
</ul>
</div>
)}
{user?.profile?.description && (
<div>
<h2 className="text-sm font-medium text-gray-500 uppercase tracking-wider">About</h2>
<p className={pStyle}>{user.profile.description}</p>
</div>
)}
{user?.profile?.contactInfo && (
<div>
<h2 className="text-sm font-medium text-gray-500 uppercase tracking-wider">Contact</h2>
<p className={pStyle}>{user.profile.contactInfo}</p>
</div>
)}
{user?.profile?.promptAnswers && (
<div className="mt-3"><
h2 className="text-sm font-medium text-gray-500 uppercase tracking-wider">Prompt Answers</h2>
<ul className="flex flex-wrap gap-2 mt-1">
{user.profile.promptAnswers.map((value, idx) => (
<li
key={idx}
// className="px-3 py-1 text-sm bg-gray-100 rounded-full hover:bg-gray-200 transition"
>
{value.prompt} {value.answer}
</li>
))}
</ul>
</div>
)}
{/*<div>*/}
{/* <h2 className="text-sm font-medium text-gray-500 uppercase tracking-wider">Creation Date</h2>*/}
{/* <p className={pStyle}>*/}
{/* {user.profile.createdAt}*/}
{/* {new Date(user.profile.createdAt).toLocaleDateString("en-US", {*/}
{/* year: "numeric",*/}
{/* month: "long",*/}
{/* day: "numeric",*/}
{/* })}*/}
{/* </p>*/}
{/*</div>*/}
</div>
</div>
</article>
{getProfile(userData, image)}
</div>
);
}

View File

@@ -1 +1,3 @@
export const supportEmail = 'mysharktrash@gmail.com (official email to be created)';
export const supportEmail = 'mysharktrash@gmail.com (official email to be created)';
export const pStyle = "mt-1 text-gray-800 dark:text-white whitespace-pre-line";

253
lib/client/profile.tsx Normal file
View File

@@ -0,0 +1,253 @@
import {ProfileData} from "@/lib/client/schema";
import Image from "next/image";
import {pStyle} from "@/lib/client/constants";
export function getProfile(userData: ProfileData, image, header = null) {
return (
<article className="max-w-3xl mx-auto shadow-lg rounded-lg overflow-hidden">
{header}
{/* Profile Header with Image */}
< div
className="bg-gradient-to-r h-16 relative">
{
image ? (
<div className="absolute -bottom-16 left-8">
<div className="h-32 w-32 rounded-full border-4 border-white overflow-hidden ">
<Image
src={image}
alt={userData.name || 'Profile picture'}
className="h-full w-full object-cover"
width={200}
height={200}
// onError={(e) => {
// const target = e.target as HTMLImageElement;
// target.onerror = null;
// target.src = `https://ui-avatars.com/api/?name=${encodeURIComponent(profile.name || 'U')}&background=random`;
// }}
/>
</div>
</div>
) :
(
<div
className="absolute -bottom-16 left-8 h-32 w-32 rounded-full border-4 border-white bg-gray-200 flex items-center justify-center">
<span className="text-4xl font-bold text-gray-600">
{userData.name ? userData.name.charAt(0).toUpperCase() : 'U'}
</span>
</div>
)
}
</div>
{/* Profile Content */
}
<div className="pt-20 px-8 pb-8">
<h1 className="text-3xl font-bold mb-2">
{userData.name}
</h1>
< div
className="space-y-6 pt-4 border-t border-gray-200">
{userData?.profile?.desiredConnections && (
<div className="mt-3"><
h2 className="text-sm font-medium text-gray-500 uppercase tracking-wider"> Desired
Connections </h2>
< ul
className="flex flex-wrap gap-2 mt-1">
{userData?.profile?.desiredConnections.map((value, idx) => (
<li
key={idx}
className="px-3 py-1 text-sm bg-blue-100 text-blue-800 dark:text-white dark:bg-gray-700 rounded-full hover:bg-gray-200 transition"
>
{value?.connection?.name
}
</li>
))
}
</ul>
</div>
)
}
{
userData?.profile?.gender && (
<div>
<h2 className="text-sm font-medium text-gray-500 uppercase tracking-wider"> Gender </h2>
< p
className="mt-1 capitalize"> {userData.profile.gender} </p>
</div>
)
}
{
userData?.profile?.location && (
<div>
<h2 className="text-sm font-medium text-gray-500 uppercase tracking-wider"> Location </h2>
< p
className={pStyle}> {userData.profile.location} </p>
</div>
)
}
{
userData?.profile?.occupation && (
<div>
<h2 className="text-sm font-medium text-gray-500 uppercase tracking-wider"> Occupation </h2>
< p
className={pStyle}> {userData.profile.occupation} </p>
</div>
)
}
{
userData?.profile?.personalityType && (
<div>
<h2 className="text-sm font-medium text-gray-500 uppercase tracking-wider"> Personality
Type </h2>
< p
className={pStyle}> {userData.profile.personalityType} </p>
</div>
)
}
{
userData?.profile?.conflictStyle && (
<div>
<h2 className="text-sm font-medium text-gray-500 uppercase tracking-wider"> Conflict
Style </h2>
< p
className={pStyle}> {userData.profile.conflictStyle} </p>
</div>
)
}
{
userData?.profile?.intellectualInterests && (
<div className="mt-3"><
h2 className="text-sm font-medium text-gray-500 uppercase tracking-wider"> Interests </h2>
< ul
className="flex flex-wrap gap-2 mt-1">
{
userData.profile.intellectualInterests.map((value, idx) => (
<li
key={idx}
className="px-3 py-1 text-sm bg-blue-100 text-blue-800 dark:text-white dark:bg-gray-700 rounded-full hover:bg-gray-200 transition"
>
{value?.interest?.name
}
</li>
))
}
</ul>
</div>
)
}
{
userData?.profile?.causeAreas && (
<div className="mt-3"><
h2 className="text-sm font-medium text-gray-500 uppercase tracking-wider"> Cause
Areas </h2>
< ul
className="flex flex-wrap gap-2 mt-1">
{
userData.profile.causeAreas.map((value, idx) => (
<li
key={idx}
className="px-3 py-1 text-sm bg-blue-100 text-blue-800 dark:text-white dark:bg-gray-700 rounded-full hover:bg-gray-200 transition"
>
{value?.causeArea?.name
}
</li>
))
}
</ul>
</div>
)
}
{
userData?.profile?.description && (
<div>
<h2 className="text-sm font-medium text-gray-500 uppercase tracking-wider"> About </h2>
< p
className={pStyle}> {userData.profile.description} </p>
</div>
)
}
{
userData?.profile?.contactInfo && (
<div>
<h2 className="text-sm font-medium text-gray-500 uppercase tracking-wider"> Contact </h2>
< p
className={pStyle}> {userData.profile.contactInfo} </p>
</div>
)
}
{
userData?.profile?.promptAnswers && (
<div className="mt-3"><
h2 className="text-sm font-medium text-gray-500 uppercase tracking-wider"> Prompt
Answers </h2>
< ul
className="flex flex-wrap gap-2 mt-1">
{
userData.profile.promptAnswers.map((value, idx) => (
<li
key={idx}
// className="px-3 py-1 text-sm bg-gray-100 rounded-full hover:bg-gray-200 transition"
>
{
value.prompt
}
{
value.answer
}
</li>
))
}
</ul>
</div>
)
}
{/*<div>*/
}
{/* <h2 className="text-sm font-medium text-gray-500 uppercase tracking-wider">Creation Date</h2>*/
}
{/* <p className={pStyle}>*/
}
{/* {user.profile.createdAt}*/
}
{/* {new Date(user.profile.createdAt).toLocaleDateString("en-US", {*/
}
{/* year: "numeric",*/
}
{/* month: "long",*/
}
{/* day: "numeric",*/
}
{/* })}*/
}
{/* </p>*/
}
{/*</div>*/
}
</div>
</div>
</article>
)
;
}