fix: optimize placeholder banner image & minor fix dark mode
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 109 KiB |
@@ -1,7 +1,14 @@
|
|||||||
import { FC, ReactElement, useState, useEffect } from 'react';
|
import { FC, ReactElement, useState, useEffect } from 'react';
|
||||||
import { Button } from '@imphnen-frontend-service/ui/atoms';
|
import { Button } from '@imphnen-frontend-service/ui/atoms';
|
||||||
import { Link, useNavigate } from 'react-router';
|
import { Link, useNavigate } from 'react-router';
|
||||||
import { useTeams, useJoinTeam, useMyTeams, ETeamVisibility, joinTeamSchema, TJoinTeamForm } from '@imphnen-frontend-service/service';
|
import {
|
||||||
|
useTeams,
|
||||||
|
useJoinTeam,
|
||||||
|
useMyTeams,
|
||||||
|
ETeamVisibility,
|
||||||
|
joinTeamSchema,
|
||||||
|
TJoinTeamForm,
|
||||||
|
} from '@imphnen-frontend-service/service';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { CitySelect } from '../../../components/city-select';
|
import { CitySelect } from '../../../components/city-select';
|
||||||
@@ -69,8 +76,12 @@ const BrowseTeamsPage: FC = (): ReactElement => {
|
|||||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-3xl font-bold text-gray-900 dark:text-white">Browse Teams</h1>
|
<h1 className="text-3xl font-bold text-gray-900 dark:text-white">
|
||||||
<p className="text-gray-600 dark:text-neutral-400 mt-1">Find and join teams looking for members</p>
|
Browse Teams
|
||||||
|
</h1>
|
||||||
|
<p className="text-gray-600 dark:text-neutral-400 mt-1">
|
||||||
|
Find and join teams looking for members
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Link to="/dashboard" className="hidden md:block">
|
<Link to="/dashboard" className="hidden md:block">
|
||||||
<Button variant="secondary">Back to Dashboard</Button>
|
<Button variant="secondary">Back to Dashboard</Button>
|
||||||
@@ -111,19 +122,28 @@ const BrowseTeamsPage: FC = (): ReactElement => {
|
|||||||
{/* Teams List */}
|
{/* Teams List */}
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="text-center py-12">
|
<div className="text-center py-12">
|
||||||
<p className="text-gray-600 dark:text-neutral-400">Loading teams...</p>
|
<p className="text-gray-600 dark:text-neutral-400">
|
||||||
|
Loading teams...
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
) : teams.length === 0 ? (
|
) : teams.length === 0 ? (
|
||||||
<div className="bg-white dark:bg-neutral-900 rounded-lg shadow-sm p-12 text-center">
|
<div className="bg-white dark:bg-neutral-900 rounded-lg shadow-sm p-12 text-center">
|
||||||
<p className="text-gray-600 dark:text-neutral-400 text-lg">No teams found</p>
|
<p className="text-gray-600 dark:text-neutral-400 text-lg">
|
||||||
<p className="text-gray-500 dark:text-neutral-500 mt-2">Try adjusting your filters</p>
|
No teams found
|
||||||
|
</p>
|
||||||
|
<p className="text-gray-500 dark:text-neutral-500 mt-2">
|
||||||
|
Try adjusting your filters
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||||
{teams.map((team) => (
|
{teams.map((team) => (
|
||||||
<div key={team.id} className="bg-white dark:bg-neutral-800 rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow flex flex-col">
|
<div
|
||||||
|
key={team.id}
|
||||||
|
className="bg-white dark:bg-neutral-800 rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow flex flex-col"
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
src={team.banner || '/images/banner-imphnen.png'}
|
src={team.banner || '/images/banner-imphnen.webp'}
|
||||||
alt={team.name}
|
alt={team.name}
|
||||||
className="w-full h-32 object-cover"
|
className="w-full h-32 object-cover"
|
||||||
/>
|
/>
|
||||||
@@ -137,14 +157,22 @@ const BrowseTeamsPage: FC = (): ReactElement => {
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-12 h-12 rounded-full bg-gray-200 dark:bg-neutral-700 flex items-center justify-center">
|
<div className="w-12 h-12 rounded-full bg-gray-200 dark:bg-neutral-700 flex items-center justify-center">
|
||||||
<span className="text-gray-500 dark:text-neutral-400 text-xl">👥</span>
|
<span className="text-gray-500 dark:text-neutral-400 text-xl">
|
||||||
|
👥
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<h3 className="text-lg font-bold text-gray-900 dark:text-white line-clamp-2">{team.name}</h3>
|
<h3 className="text-lg font-bold text-gray-900 dark:text-white line-clamp-2">
|
||||||
|
{team.name}
|
||||||
|
</h3>
|
||||||
<div className="text-sm text-gray-600 dark:text-neutral-400 flex gap-2">
|
<div className="text-sm text-gray-600 dark:text-neutral-400 flex gap-2">
|
||||||
<p className="truncate flex-1 min-w-0">📍 {team.city}</p>
|
<p className="truncate flex-1 min-w-0">
|
||||||
<p className="whitespace-nowrap shrink-0">👥 {team.members?.length || 0} members</p>
|
📍 {team.city}
|
||||||
|
</p>
|
||||||
|
<p className="whitespace-nowrap shrink-0">
|
||||||
|
👥 {team.members?.length || 0} members
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -162,14 +190,15 @@ const BrowseTeamsPage: FC = (): ReactElement => {
|
|||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{myTeams.length === 0 && (team.members?.length || 0) < 5 && (
|
{myTeams.length === 0 &&
|
||||||
<Button
|
(team.members?.length || 0) < 5 && (
|
||||||
className="w-full"
|
<Button
|
||||||
onClick={() => handleJoinRequest(team.id)}
|
className="w-full"
|
||||||
>
|
onClick={() => handleJoinRequest(team.id)}
|
||||||
Request to Join
|
>
|
||||||
</Button>
|
Request to Join
|
||||||
)}
|
</Button>
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
className="w-full"
|
className="w-full"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
|
|||||||
@@ -31,7 +31,9 @@ const TeamsLayout: FC = (): ReactElement => {
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<h1 className="ml-3 text-lg font-bold text-gray-900">Hackathon</h1>
|
<h1 className="ml-3 text-lg font-bold text-gray-900 dark:text-white">
|
||||||
|
Hackathon
|
||||||
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import { FC, ReactElement } from 'react';
|
import { FC, ReactElement } from 'react';
|
||||||
import { Button } from '@imphnen-frontend-service/ui/atoms';
|
import { Button } from '@imphnen-frontend-service/ui/atoms';
|
||||||
import { useParams, useNavigate, Link } from 'react-router';
|
import { useParams, useNavigate, Link } from 'react-router';
|
||||||
import { useUserDetailsById, useTeamsByUserId } from '@imphnen-frontend-service/service';
|
import {
|
||||||
|
useUserDetailsById,
|
||||||
|
useTeamsByUserId,
|
||||||
|
} from '@imphnen-frontend-service/service';
|
||||||
|
|
||||||
const UserProfilePage: FC = (): ReactElement => {
|
const UserProfilePage: FC = (): ReactElement => {
|
||||||
const { userId } = useParams<{ userId: string }>();
|
const { userId } = useParams<{ userId: string }>();
|
||||||
@@ -15,7 +18,9 @@ const UserProfilePage: FC = (): ReactElement => {
|
|||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center min-h-screen bg-gray-50 dark:bg-neutral-950">
|
<div className="flex items-center justify-center min-h-screen bg-gray-50 dark:bg-neutral-950">
|
||||||
<div className="text-gray-600 dark:text-neutral-400">Loading user profile...</div>
|
<div className="text-gray-600 dark:text-neutral-400">
|
||||||
|
Loading user profile...
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -23,9 +28,15 @@ const UserProfilePage: FC = (): ReactElement => {
|
|||||||
if (error || !user) {
|
if (error || !user) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center justify-center min-h-screen bg-gray-50 dark:bg-neutral-950">
|
<div className="flex flex-col items-center justify-center min-h-screen bg-gray-50 dark:bg-neutral-950">
|
||||||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">User not found</h2>
|
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
||||||
{error && <p className="text-red-600 dark:text-red-400 mb-4">{String(error)}</p>}
|
User not found
|
||||||
<Button onClick={() => navigate('/dashboard')}>Back to Dashboard</Button>
|
</h2>
|
||||||
|
{error && (
|
||||||
|
<p className="text-red-600 dark:text-red-400 mb-4">{String(error)}</p>
|
||||||
|
)}
|
||||||
|
<Button onClick={() => navigate('/dashboard')}>
|
||||||
|
Back to Dashboard
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -45,15 +56,23 @@ const UserProfilePage: FC = (): ReactElement => {
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-16 h-16 md:w-20 md:h-20 rounded-full bg-gray-200 dark:bg-neutral-700 flex items-center justify-center border-4 border-white dark:border-neutral-800 shadow-lg">
|
<div className="w-16 h-16 md:w-20 md:h-20 rounded-full bg-gray-200 dark:bg-neutral-700 flex items-center justify-center border-4 border-white dark:border-neutral-800 shadow-lg">
|
||||||
<span className="text-gray-500 dark:text-neutral-400 text-2xl md:text-3xl">👤</span>
|
<span className="text-gray-500 dark:text-neutral-400 text-2xl md:text-3xl">
|
||||||
|
👤
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-xl md:text-3xl font-bold text-gray-900 dark:text-white">{user.fullname}</h1>
|
<h1 className="text-xl md:text-3xl font-bold text-gray-900 dark:text-white">
|
||||||
<p className="text-sm md:text-base text-gray-600 dark:text-neutral-400 mt-1">{user.email}</p>
|
{user.fullname}
|
||||||
|
</h1>
|
||||||
|
<p className="text-sm md:text-base text-gray-600 dark:text-neutral-400 mt-1">
|
||||||
|
{user.email}
|
||||||
|
</p>
|
||||||
{user.location && (
|
{user.location && (
|
||||||
<div className="flex items-center space-x-4 mt-2">
|
<div className="flex items-center space-x-4 mt-2">
|
||||||
<span className="text-sm text-gray-500 dark:text-neutral-500">📍 {user.location}</span>
|
<span className="text-sm text-gray-500 dark:text-neutral-500">
|
||||||
|
📍 {user.location}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -72,15 +91,21 @@ const UserProfilePage: FC = (): ReactElement => {
|
|||||||
{/* About Section */}
|
{/* About Section */}
|
||||||
{user.bio && (
|
{user.bio && (
|
||||||
<div className="bg-white dark:bg-neutral-900 rounded-lg shadow-md dark:shadow-neutral-950/50 p-4 md:p-6 overflow-hidden">
|
<div className="bg-white dark:bg-neutral-900 rounded-lg shadow-md dark:shadow-neutral-950/50 p-4 md:p-6 overflow-hidden">
|
||||||
<h2 className="text-lg md:text-xl font-bold text-gray-900 dark:text-white mb-3 md:mb-4">About</h2>
|
<h2 className="text-lg md:text-xl font-bold text-gray-900 dark:text-white mb-3 md:mb-4">
|
||||||
<p className="text-gray-700 dark:text-neutral-300 break-all overflow-wrap-anywhere">{user.bio}</p>
|
About
|
||||||
|
</h2>
|
||||||
|
<p className="text-gray-700 dark:text-neutral-300 break-all overflow-wrap-anywhere">
|
||||||
|
{user.bio}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Skills Section */}
|
{/* Skills Section */}
|
||||||
{user.skills && user.skills.length > 0 && (
|
{user.skills && user.skills.length > 0 && (
|
||||||
<div className="bg-white dark:bg-neutral-900 rounded-lg shadow-md dark:shadow-neutral-950/50 p-4 md:p-6">
|
<div className="bg-white dark:bg-neutral-900 rounded-lg shadow-md dark:shadow-neutral-950/50 p-4 md:p-6">
|
||||||
<h2 className="text-lg md:text-xl font-bold text-gray-900 dark:text-white mb-3 md:mb-4">Skills</h2>
|
<h2 className="text-lg md:text-xl font-bold text-gray-900 dark:text-white mb-3 md:mb-4">
|
||||||
|
Skills
|
||||||
|
</h2>
|
||||||
<div className="flex flex-wrap gap-3">
|
<div className="flex flex-wrap gap-3">
|
||||||
{user.skills.map((skill: string) => (
|
{user.skills.map((skill: string) => (
|
||||||
<span
|
<span
|
||||||
@@ -97,23 +122,47 @@ const UserProfilePage: FC = (): ReactElement => {
|
|||||||
{/* Teams Section */}
|
{/* Teams Section */}
|
||||||
{userTeams.length > 0 ? (
|
{userTeams.length > 0 ? (
|
||||||
<div className="bg-white dark:bg-neutral-900 rounded-lg shadow-md dark:shadow-neutral-950/50 p-4 md:p-6">
|
<div className="bg-white dark:bg-neutral-900 rounded-lg shadow-md dark:shadow-neutral-950/50 p-4 md:p-6">
|
||||||
<h2 className="text-lg md:text-xl font-bold text-gray-900 dark:text-white mb-3 md:mb-4">Teams ({userTeams.length})</h2>
|
<h2 className="text-lg md:text-xl font-bold text-gray-900 dark:text-white mb-3 md:mb-4">
|
||||||
|
Teams ({userTeams.length})
|
||||||
|
</h2>
|
||||||
<div className="grid gap-4 md:grid-cols-2">
|
<div className="grid gap-4 md:grid-cols-2">
|
||||||
{userTeams.map((team: any) => (
|
{userTeams.map((team: any) => (
|
||||||
<Link key={team.id} to={'/teams/' + team.id} className="bg-white dark:bg-neutral-800 rounded-lg shadow-md dark:shadow-neutral-950/50 overflow-hidden hover:shadow-lg transition-shadow">
|
<Link
|
||||||
<img src={team.banner || '/images/banner-imphnen.png'} alt={team.name} className="w-full h-24 object-cover" />
|
key={team.id}
|
||||||
|
to={'/teams/' + team.id}
|
||||||
|
className="bg-white dark:bg-neutral-800 rounded-lg shadow-md dark:shadow-neutral-950/50 overflow-hidden hover:shadow-lg transition-shadow"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={team.banner || '/images/banner-imphnen.webp'}
|
||||||
|
alt={team.name}
|
||||||
|
className="w-full h-24 object-cover"
|
||||||
|
/>
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<div className="flex items-center space-x-3 mb-2">
|
<div className="flex items-center space-x-3 mb-2">
|
||||||
{team.logo && <img src={team.logo} alt={team.name} className="w-10 h-10 rounded-full object-cover" />}
|
{team.logo && (
|
||||||
|
<img
|
||||||
|
src={team.logo}
|
||||||
|
alt={team.name}
|
||||||
|
className="w-10 h-10 rounded-full object-cover"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<h3 className="font-bold text-gray-900 dark:text-white line-clamp-2">{team.name}</h3>
|
<h3 className="font-bold text-gray-900 dark:text-white line-clamp-2">
|
||||||
|
{team.name}
|
||||||
|
</h3>
|
||||||
<div className="text-sm text-gray-600 dark:text-neutral-400 flex gap-2">
|
<div className="text-sm text-gray-600 dark:text-neutral-400 flex gap-2">
|
||||||
<p className="truncate flex-1 min-w-0">📍 {team.city}</p>
|
<p className="truncate flex-1 min-w-0">
|
||||||
<p className="whitespace-nowrap shrink-0">👥 {team.members?.length || 0} members</p>
|
📍 {team.city}
|
||||||
|
</p>
|
||||||
|
<p className="whitespace-nowrap shrink-0">
|
||||||
|
👥 {team.members?.length || 0} members
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-gray-600 dark:text-neutral-400 text-sm line-clamp-2">{team.description}</p>
|
<p className="text-gray-600 dark:text-neutral-400 text-sm line-clamp-2">
|
||||||
|
{team.description}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
@@ -121,10 +170,14 @@ const UserProfilePage: FC = (): ReactElement => {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="bg-white dark:bg-neutral-900 rounded-lg shadow-md dark:shadow-neutral-950/50 p-4 md:p-6">
|
<div className="bg-white dark:bg-neutral-900 rounded-lg shadow-md dark:shadow-neutral-950/50 p-4 md:p-6">
|
||||||
<h2 className="text-lg md:text-xl font-bold text-gray-900 dark:text-white mb-3 md:mb-4">Teams</h2>
|
<h2 className="text-lg md:text-xl font-bold text-gray-900 dark:text-white mb-3 md:mb-4">
|
||||||
|
Teams
|
||||||
|
</h2>
|
||||||
<div className="text-center py-8">
|
<div className="text-center py-8">
|
||||||
<div className="text-4xl mb-3">👥</div>
|
<div className="text-4xl mb-3">👥</div>
|
||||||
<p className="text-gray-600 dark:text-neutral-400">Not in any team yet</p>
|
<p className="text-gray-600 dark:text-neutral-400">
|
||||||
|
Not in any team yet
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -134,16 +187,26 @@ const UserProfilePage: FC = (): ReactElement => {
|
|||||||
<div className="space-y-4 md:space-y-6">
|
<div className="space-y-4 md:space-y-6">
|
||||||
{/* Contact Info */}
|
{/* Contact Info */}
|
||||||
<div className="bg-white dark:bg-neutral-900 rounded-lg shadow-md dark:shadow-neutral-950/50 p-4 md:p-6">
|
<div className="bg-white dark:bg-neutral-900 rounded-lg shadow-md dark:shadow-neutral-950/50 p-4 md:p-6">
|
||||||
<h3 className="text-base md:text-lg font-bold text-gray-900 dark:text-white mb-3 md:mb-4">Contact Information</h3>
|
<h3 className="text-base md:text-lg font-bold text-gray-900 dark:text-white mb-3 md:mb-4">
|
||||||
|
Contact Information
|
||||||
|
</h3>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-gray-600 dark:text-neutral-400">Email</p>
|
<p className="text-sm text-gray-600 dark:text-neutral-400">
|
||||||
<p className="text-gray-900 dark:text-white font-medium">{user.email}</p>
|
Email
|
||||||
|
</p>
|
||||||
|
<p className="text-gray-900 dark:text-white font-medium">
|
||||||
|
{user.email}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{user.location && (
|
{user.location && (
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-gray-600 dark:text-neutral-400">Location</p>
|
<p className="text-sm text-gray-600 dark:text-neutral-400">
|
||||||
<p className="text-gray-900 dark:text-white font-medium">{user.location}</p>
|
Location
|
||||||
|
</p>
|
||||||
|
<p className="text-gray-900 dark:text-white font-medium">
|
||||||
|
{user.location}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user