feat(hackathon): enhance dark mode, city select, and auth-aware navbar

- Apply rose-pine dark theme across all pages for darker appearance
- Update Button component with dark mode variants
- Replace basic city select with searchable CitySelect on create team page
- Show Dashboard button in navbar when user is authenticated
- Apply dark mode styling to onboarding, team pages, and user profile

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Maulana Sodiqin
2025-11-26 16:06:23 +07:00
co-authored by Claude
parent 62780c8889
commit 6e6c361eb8
11 changed files with 288 additions and 271 deletions
+15 -15
View File
@@ -109,13 +109,13 @@ const UserOnboardingPage: FC = (): ReactElement => {
}); });
return ( return (
<div className="flex flex-col justify-center items-center min-h-screen bg-gray-50 px-4 py-8"> <div className="flex flex-col justify-center items-center min-h-screen bg-gray-50 dark:bg-neutral-950 px-4 py-8">
<div className="bg-white w-full max-w-2xl p-8 rounded-xl shadow-lg"> <div className="bg-white dark:bg-neutral-900 w-full max-w-2xl p-8 rounded-xl shadow-lg dark:shadow-neutral-950/50">
<div className="mb-6"> <div className="mb-6">
<h1 className="text-3xl font-bold text-gray-900 mb-2"> <h1 className="text-3xl font-bold text-gray-900 dark:text-white mb-2">
Complete Your Profile Complete Your Profile
</h1> </h1>
<p className="text-gray-600"> <p className="text-gray-600 dark:text-neutral-400">
Tell us more about yourself to get started Tell us more about yourself to get started
</p> </p>
</div> </div>
@@ -128,17 +128,17 @@ const UserOnboardingPage: FC = (): ReactElement => {
<img <img
src={avatarPreview} src={avatarPreview}
alt="Avatar preview" alt="Avatar preview"
className="w-32 h-32 rounded-full object-cover border-4 border-gray-200" className="w-32 h-32 rounded-full object-cover border-4 border-gray-200 dark:border-neutral-700"
/> />
) : ( ) : (
<div className="w-32 h-32 rounded-full bg-gray-200 flex items-center justify-center"> <div className="w-32 h-32 rounded-full bg-gray-200 dark:bg-neutral-700 flex items-center justify-center">
<span className="text-gray-400 text-4xl">👤</span> <span className="text-gray-400 dark:text-neutral-500 text-4xl">👤</span>
</div> </div>
)} )}
</div> </div>
<div> <div>
<label htmlFor="avatar" className="cursor-pointer"> <label htmlFor="avatar" className="cursor-pointer">
<span className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 inline-block"> <span className="px-4 py-2 bg-blue-600 dark:bg-primary-600 text-white rounded-lg hover:bg-blue-700 dark:hover:bg-primary-700 inline-block">
{avatarPreview ? 'Change Photo' : 'Upload Photo'} {avatarPreview ? 'Change Photo' : 'Upload Photo'}
</span> </span>
<input <input
@@ -149,7 +149,7 @@ const UserOnboardingPage: FC = (): ReactElement => {
onChange={handleAvatarChange} onChange={handleAvatarChange}
/> />
</label> </label>
<p className="text-xs text-gray-500 mt-2 text-center"> <p className="text-xs text-gray-500 dark:text-neutral-500 mt-2 text-center">
Optional, but highly recommended Optional, but highly recommended
</p> </p>
</div> </div>
@@ -165,7 +165,7 @@ const UserOnboardingPage: FC = (): ReactElement => {
{/* City */} {/* City */}
<div className="space-y-2"> <div className="space-y-2">
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700 dark:text-neutral-300">
City <span className="text-red-500">*</span> City <span className="text-red-500">*</span>
</label> </label>
<Controller <Controller
@@ -184,7 +184,7 @@ const UserOnboardingPage: FC = (): ReactElement => {
{/* Role/Skills */} {/* Role/Skills */}
<div className="space-y-2"> <div className="space-y-2">
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700 dark:text-neutral-300">
Role / Skills Role / Skills
</label> </label>
<Controller <Controller
@@ -207,9 +207,9 @@ const UserOnboardingPage: FC = (): ReactElement => {
: (field.value || []).filter((v) => v !== role); : (field.value || []).filter((v) => v !== role);
field.onChange(newValue); field.onChange(newValue);
}} }}
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500" className="rounded border-gray-300 dark:border-neutral-600 text-blue-600 dark:text-primary-500 focus:ring-blue-500 dark:focus:ring-primary-500 dark:bg-neutral-800"
/> />
<span className="text-sm">{role}</span> <span className="text-sm dark:text-neutral-300">{role}</span>
</label> </label>
))} ))}
</div> </div>
@@ -220,8 +220,8 @@ const UserOnboardingPage: FC = (): ReactElement => {
{/* Bio */} {/* Bio */}
<div className="space-y-2"> <div className="space-y-2">
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700 dark:text-neutral-300">
Bio <span className="text-gray-400">(Optional)</span> Bio <span className="text-gray-400 dark:text-neutral-500">(Optional)</span>
</label> </label>
<Controller <Controller
control={form.control} control={form.control}
+50 -24
View File
@@ -3,11 +3,14 @@ import { useState } from 'react';
import { Button } from '@imphnen-frontend-service/ui/atoms'; import { Button } from '@imphnen-frontend-service/ui/atoms';
import { Icon } from '@iconify/react'; import { Icon } from '@iconify/react';
import { ThemeToggle } from '../components/theme-toggle'; import { ThemeToggle } from '../components/theme-toggle';
import { useAuthStore } from '@imphnen-frontend-service/service';
export default function HomePage() { export default function HomePage() {
const navigate = useNavigate(); const navigate = useNavigate();
const [mobileMenuOpen, setMobileMenuOpen] = useState(false); const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const [openFaq, setOpenFaq] = useState<number | null>(0); const [openFaq, setOpenFaq] = useState<number | null>(0);
const { session } = useAuthStore();
const isAuthenticated = !!session?.token;
const faqs = [ const faqs = [
{ {
@@ -98,21 +101,33 @@ export default function HomePage() {
FAQ FAQ
</a> </a>
<ThemeToggle /> <ThemeToggle />
<Button {isAuthenticated ? (
onClick={() => navigate('/auth/login')} <Button
size="sm" onClick={() => navigate('/dashboard')}
variant="bordered" size="sm"
className="rounded-lg text-base" className="rounded-lg text-base"
> >
Masuk Dashboard
</Button> </Button>
<Button ) : (
onClick={() => navigate('/auth/signup')} <>
size="sm" <Button
className="rounded-lg text-base" onClick={() => navigate('/auth/login')}
> size="sm"
Daftar Sekarang variant="bordered"
</Button> className="rounded-lg text-base"
>
Masuk
</Button>
<Button
onClick={() => navigate('/auth/signup')}
size="sm"
className="rounded-lg text-base"
>
Daftar Sekarang
</Button>
</>
)}
</div> </div>
{/* Mobile Menu Button */} {/* Mobile Menu Button */}
<div className="flex items-center gap-2 md:hidden"> <div className="flex items-center gap-2 md:hidden">
@@ -158,15 +173,26 @@ export default function HomePage() {
<a href="#faq" className="ms-3 text-gray-600 dark:text-neutral-400 hover:text-gray-900 dark:hover:text-white"> <a href="#faq" className="ms-3 text-gray-600 dark:text-neutral-400 hover:text-gray-900 dark:hover:text-white">
FAQ FAQ
</a> </a>
<a href="#masuk" className="ms-3 text-gray-600 dark:text-neutral-400 hover:text-gray-900 dark:hover:text-white"> {isAuthenticated ? (
Masuk <button
</a> onClick={() => navigate('/dashboard')}
<button className="px-4 py-2 bg-primary-500 text-white text-base rounded-lg hover:bg-primary-600 transition-colors text-center cursor-pointer"
onClick={() => navigate('/auth/login')} >
className="px-4 py-2 bg-primary-500 text-white text-base rounded-lg hover:bg-primary-600 transition-colors text-center cursor-pointer" Dashboard
> </button>
Daftar Sekarang ) : (
</button> <>
<a href="#masuk" className="ms-3 text-gray-600 dark:text-neutral-400 hover:text-gray-900 dark:hover:text-white">
Masuk
</a>
<button
onClick={() => navigate('/auth/login')}
className="px-4 py-2 bg-primary-500 text-white text-base rounded-lg hover:bg-primary-600 transition-colors text-center cursor-pointer"
>
Daftar Sekarang
</button>
</>
)}
</div> </div>
</div> </div>
)} )}
@@ -63,14 +63,14 @@ const TeamChatPage: FC = (): ReactElement => {
}; };
return ( return (
<div className="flex flex-col h-screen bg-gray-50"> <div className="flex flex-col h-screen bg-gray-50 dark:bg-neutral-950">
{/* Header */} {/* Header */}
<div className="bg-white border-b shadow-sm"> <div className="bg-white dark:bg-neutral-900 border-b dark:border-neutral-700 shadow-sm dark:shadow-neutral-950/50">
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 py-4"> <div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<h1 className="text-2xl font-bold text-gray-900">Team Chat</h1> <h1 className="text-2xl font-bold text-gray-900 dark:text-white">Team Chat</h1>
<p className="text-gray-600 text-sm mt-0.5">{team?.name}</p> <p className="text-gray-600 dark:text-neutral-400 text-sm mt-0.5">{team?.name}</p>
</div> </div>
<Button variant="secondary" onClick={() => navigate(`/teams/${teamId}`)}> <Button variant="secondary" onClick={() => navigate(`/teams/${teamId}`)}>
Back to Team Back to Team
@@ -84,7 +84,7 @@ const TeamChatPage: FC = (): ReactElement => {
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-6"> <div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
{isLoading ? ( {isLoading ? (
<div className="flex items-center justify-center h-64"> <div className="flex items-center justify-center h-64">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div> <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 dark:border-primary-400"></div>
</div> </div>
) : messages && messages.length > 0 ? ( ) : messages && messages.length > 0 ? (
<div className="space-y-4"> <div className="space-y-4">
@@ -118,18 +118,18 @@ const TeamChatPage: FC = (): ReactElement => {
<div className={`flex-1 ${isOwnMessage ? 'text-right' : 'text-left'}`}> <div className={`flex-1 ${isOwnMessage ? 'text-right' : 'text-left'}`}>
<div className={`inline-block ${isOwnMessage ? 'items-end' : 'items-start'}`}> <div className={`inline-block ${isOwnMessage ? 'items-end' : 'items-start'}`}>
<div className="flex items-baseline gap-2 mb-1"> <div className="flex items-baseline gap-2 mb-1">
<span className="font-semibold text-sm text-gray-900"> <span className="font-semibold text-sm text-gray-900 dark:text-white">
{isOwnMessage ? 'You' : msg.user?.fullname} {isOwnMessage ? 'You' : msg.user?.fullname}
</span> </span>
<span className="text-xs text-gray-500"> <span className="text-xs text-gray-500 dark:text-neutral-500">
{formatTime(msg.created_at)} {formatTime(msg.created_at)}
</span> </span>
</div> </div>
<div <div
className={`relative group rounded-2xl px-4 py-2.5 ${ className={`relative group rounded-2xl px-4 py-2.5 ${
isOwnMessage isOwnMessage
? 'bg-blue-600 text-white' ? 'bg-blue-600 dark:bg-primary-600 text-white'
: 'bg-white text-gray-900 border border-gray-200' : 'bg-white dark:bg-neutral-800 text-gray-900 dark:text-white border border-gray-200 dark:border-neutral-700'
}`} }`}
> >
<p className="text-sm whitespace-pre-wrap break-words">{msg.message}</p> <p className="text-sm whitespace-pre-wrap break-words">{msg.message}</p>
@@ -138,7 +138,7 @@ const TeamChatPage: FC = (): ReactElement => {
{canDelete && ( {canDelete && (
<button <button
onClick={() => handleDeleteMessage(msg.id)} onClick={() => handleDeleteMessage(msg.id)}
className={`absolute top-1 ${isOwnMessage ? 'left-1' : 'right-1'} opacity-0 group-hover:opacity-100 transition-opacity p-1 rounded hover:bg-gray-200 ${isOwnMessage ? 'hover:bg-blue-700' : ''}`} className={`absolute top-1 ${isOwnMessage ? 'left-1' : 'right-1'} opacity-0 group-hover:opacity-100 transition-opacity p-1 rounded hover:bg-gray-200 dark:hover:bg-neutral-700 ${isOwnMessage ? 'hover:bg-blue-700 dark:hover:bg-primary-700' : ''}`}
title="Delete message" title="Delete message"
> >
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -158,10 +158,10 @@ const TeamChatPage: FC = (): ReactElement => {
) : ( ) : (
<div className="flex flex-col items-center justify-center h-64 text-center"> <div className="flex flex-col items-center justify-center h-64 text-center">
<div className="text-6xl mb-4">💬</div> <div className="text-6xl mb-4">💬</div>
<h3 className="text-xl font-semibold text-gray-900 mb-2"> <h3 className="text-xl font-semibold text-gray-900 dark:text-white mb-2">
No messages yet No messages yet
</h3> </h3>
<p className="text-gray-600"> <p className="text-gray-600 dark:text-neutral-400">
Be the first to start the conversation! Be the first to start the conversation!
</p> </p>
</div> </div>
@@ -170,7 +170,7 @@ const TeamChatPage: FC = (): ReactElement => {
</div> </div>
{/* Message Input */} {/* Message Input */}
<div className="bg-white border-t shadow-lg"> <div className="bg-white dark:bg-neutral-900 border-t dark:border-neutral-700 shadow-lg dark:shadow-neutral-950/50">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-4"> <div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
<form onSubmit={handleSendMessage} className="flex gap-3"> <form onSubmit={handleSendMessage} className="flex gap-3">
<input <input
@@ -178,7 +178,7 @@ const TeamChatPage: FC = (): ReactElement => {
value={message} value={message}
onChange={(e) => setMessage(e.target.value)} onChange={(e) => setMessage(e.target.value)}
placeholder="Type your message..." placeholder="Type your message..."
className="flex-1 px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" className="flex-1 px-4 py-3 border border-gray-300 dark:border-neutral-600 dark:bg-neutral-800 dark:text-white dark:placeholder-neutral-500 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-primary-500 focus:border-transparent"
disabled={isSending} disabled={isSending}
/> />
<Button <Button
@@ -46,17 +46,17 @@ const EditTeamPage: FC = (): ReactElement => {
if (isLoadingTeam) { if (isLoadingTeam) {
return ( return (
<div className="flex items-center justify-center min-h-screen"> <div className="flex items-center justify-center min-h-screen bg-gray-50 dark:bg-neutral-950">
<div className="text-gray-600">Loading team...</div> <div className="text-gray-600 dark:text-neutral-400">Loading team...</div>
</div> </div>
); );
} }
if (!isLeader) { if (!isLeader) {
return ( return (
<div className="flex flex-col items-center justify-center min-h-screen"> <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 mb-4">Access Denied</h2> <h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">Access Denied</h2>
<p className="text-gray-600 mb-4">Only the team leader can edit team information</p> <p className="text-gray-600 dark:text-neutral-400 mb-4">Only the team leader can edit team information</p>
<Button onClick={() => navigate(`/teams/${teamId}`)}>Back to Team</Button> <Button onClick={() => navigate(`/teams/${teamId}`)}>Back to Team</Button>
</div> </div>
); );
@@ -114,20 +114,20 @@ const EditTeamPage: FC = (): ReactElement => {
}); });
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-gray-50 dark:bg-neutral-950">
<div className="bg-white border-b"> <div className="bg-white dark:bg-neutral-900 border-b dark:border-neutral-700">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-6"> <div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
<h1 className="text-3xl font-bold text-gray-900">Edit Team Info</h1> <h1 className="text-3xl font-bold text-gray-900 dark:text-white">Edit Team Info</h1>
<p className="text-gray-600 mt-1">Update your team details</p> <p className="text-gray-600 dark:text-neutral-400 mt-1">Update your team details</p>
</div> </div>
</div> </div>
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> <div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="bg-white rounded-lg shadow-md p-8"> <div className="bg-white dark:bg-neutral-900 rounded-lg shadow-md dark:shadow-neutral-950/50 p-8">
<form onSubmit={onSubmit} className="space-y-6"> <form onSubmit={onSubmit} className="space-y-6">
{/* Banner Upload */} {/* Banner Upload */}
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-2"> <label className="block text-sm font-medium text-gray-700 dark:text-neutral-300 mb-2">
Team Banner Team Banner
</label> </label>
{bannerPreview ? ( {bannerPreview ? (
@@ -135,7 +135,7 @@ const EditTeamPage: FC = (): ReactElement => {
<img <img
src={bannerPreview} src={bannerPreview}
alt="Banner preview" alt="Banner preview"
className="w-full h-48 object-cover rounded-lg border-2 border-gray-200" className="w-full h-48 object-cover rounded-lg border-2 border-gray-200 dark:border-neutral-700"
/> />
<button <button
type="button" type="button"
@@ -150,10 +150,10 @@ const EditTeamPage: FC = (): ReactElement => {
</button> </button>
</div> </div>
) : ( ) : (
<label className="flex flex-col items-center justify-center w-full h-48 border-2 border-dashed border-gray-300 rounded-lg cursor-pointer hover:bg-gray-50"> <label className="flex flex-col items-center justify-center w-full h-48 border-2 border-dashed border-gray-300 dark:border-neutral-600 rounded-lg cursor-pointer hover:bg-gray-50 dark:hover:bg-neutral-800">
<div className="text-center"> <div className="text-center">
<p className="text-gray-500">Click to upload banner</p> <p className="text-gray-500 dark:text-neutral-400">Click to upload banner</p>
<p className="text-xs text-gray-400 mt-1">1200x400 recommended</p> <p className="text-xs text-gray-400 dark:text-neutral-500 mt-1">1200x400 recommended</p>
</div> </div>
<input <input
type="file" type="file"
@@ -167,7 +167,7 @@ const EditTeamPage: FC = (): ReactElement => {
{/* Logo Upload */} {/* Logo Upload */}
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-2"> <label className="block text-sm font-medium text-gray-700 dark:text-neutral-300 mb-2">
Team Logo Team Logo
</label> </label>
<div className="flex items-center space-x-4"> <div className="flex items-center space-x-4">
@@ -175,11 +175,11 @@ const EditTeamPage: FC = (): ReactElement => {
<img <img
src={logoPreview} src={logoPreview}
alt="Logo preview" alt="Logo preview"
className="w-24 h-24 rounded-full object-cover border-2 border-gray-200" className="w-24 h-24 rounded-full object-cover border-2 border-gray-200 dark:border-neutral-700"
/> />
) : ( ) : (
<div className="w-24 h-24 rounded-full bg-gray-200 flex items-center justify-center"> <div className="w-24 h-24 rounded-full bg-gray-200 dark:bg-neutral-700 flex items-center justify-center">
<span className="text-gray-400 text-3xl">👥</span> <span className="text-gray-400 dark:text-neutral-500 text-3xl">👥</span>
</div> </div>
)} )}
<div> <div>
@@ -222,7 +222,7 @@ const EditTeamPage: FC = (): ReactElement => {
{/* City */} {/* City */}
<div className="space-y-2"> <div className="space-y-2">
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700 dark:text-neutral-300">
City City
</label> </label>
<Controller <Controller
@@ -232,7 +232,7 @@ const EditTeamPage: FC = (): ReactElement => {
<div> <div>
<select <select
{...field} {...field}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" className="w-full px-3 py-2 border border-gray-300 dark:border-neutral-600 dark:bg-neutral-800 dark:text-white rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
> >
<option value="">Select city</option> <option value="">Select city</option>
{INDONESIAN_CITIES.map((city) => ( {INDONESIAN_CITIES.map((city) => (
@@ -251,7 +251,7 @@ const EditTeamPage: FC = (): ReactElement => {
{/* Description */} {/* Description */}
<div className="space-y-2"> <div className="space-y-2">
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700 dark:text-neutral-300">
Description Description
</label> </label>
<Controller <Controller
@@ -276,7 +276,7 @@ const EditTeamPage: FC = (): ReactElement => {
{/* Visibility */} {/* Visibility */}
<div className="space-y-2"> <div className="space-y-2">
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700 dark:text-neutral-300">
Team Visibility Team Visibility
</label> </label>
<Controller <Controller
@@ -284,7 +284,7 @@ const EditTeamPage: FC = (): ReactElement => {
name="visibility" name="visibility"
render={({ field }) => ( render={({ field }) => (
<div className="space-y-3"> <div className="space-y-3">
<label className="flex items-start space-x-3 cursor-pointer border rounded-lg p-4 hover:bg-gray-50"> <label className="flex items-start space-x-3 cursor-pointer border dark:border-neutral-700 rounded-lg p-4 hover:bg-gray-50 dark:hover:bg-neutral-800">
<input <input
type="radio" type="radio"
{...field} {...field}
@@ -293,13 +293,13 @@ const EditTeamPage: FC = (): ReactElement => {
className="mt-1" className="mt-1"
/> />
<div> <div>
<p className="font-medium text-gray-900">Public</p> <p className="font-medium text-gray-900 dark:text-white">Public</p>
<p className="text-sm text-gray-600"> <p className="text-sm text-gray-600 dark:text-neutral-400">
Team will be visible in Browse Teams Team will be visible in Browse Teams
</p> </p>
</div> </div>
</label> </label>
<label className="flex items-start space-x-3 cursor-pointer border rounded-lg p-4 hover:bg-gray-50"> <label className="flex items-start space-x-3 cursor-pointer border dark:border-neutral-700 rounded-lg p-4 hover:bg-gray-50 dark:hover:bg-neutral-800">
<input <input
type="radio" type="radio"
{...field} {...field}
@@ -308,8 +308,8 @@ const EditTeamPage: FC = (): ReactElement => {
className="mt-1" className="mt-1"
/> />
<div> <div>
<p className="font-medium text-gray-900">Private</p> <p className="font-medium text-gray-900 dark:text-white">Private</p>
<p className="text-sm text-gray-600"> <p className="text-sm text-gray-600 dark:text-neutral-400">
Team hidden, invite-only Team hidden, invite-only
</p> </p>
</div> </div>
@@ -45,17 +45,17 @@ const ManageMembersPage: FC = (): ReactElement => {
// Show loading state while fetching team data // Show loading state while fetching team data
if (isLoadingTeam) { if (isLoadingTeam) {
return ( return (
<div className="flex flex-col items-center justify-center min-h-screen"> <div className="flex flex-col items-center justify-center min-h-screen bg-gray-50 dark:bg-neutral-950">
<p className="text-gray-600">Loading...</p> <p className="text-gray-600 dark:text-neutral-400">Loading...</p>
</div> </div>
); );
} }
if (!isLeader) { if (!isLeader) {
return ( return (
<div className="flex flex-col items-center justify-center min-h-screen"> <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 mb-4">Access Denied</h2> <h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">Access Denied</h2>
<p className="text-gray-600 mb-4">Only the team leader can manage members</p> <p className="text-gray-600 dark:text-neutral-400 mb-4">Only the team leader can manage members</p>
<Button onClick={() => navigate(`/teams/${teamId}`)}>Back to Team</Button> <Button onClick={() => navigate(`/teams/${teamId}`)}>Back to Team</Button>
</div> </div>
); );
@@ -103,13 +103,13 @@ const ManageMembersPage: FC = (): ReactElement => {
}; };
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-gray-50 dark:bg-neutral-950">
<div className="bg-white border-b"> <div className="bg-white dark:bg-neutral-900 border-b dark:border-neutral-700">
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 py-6"> <div className="max-w-6xl 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">Manage Members</h1> <h1 className="text-3xl font-bold text-gray-900 dark:text-white">Manage Members</h1>
<p className="text-gray-600 mt-1">{team?.name}</p> <p className="text-gray-600 dark:text-neutral-400 mt-1">{team?.name}</p>
</div> </div>
<div className="flex space-x-3"> <div className="flex space-x-3">
<Button onClick={() => setShowInviteModal(true)}> <Button onClick={() => setShowInviteModal(true)}>
@@ -126,13 +126,13 @@ const ManageMembersPage: FC = (): ReactElement => {
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 py-8 space-y-6"> <div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 py-8 space-y-6">
{/* Join Requests */} {/* Join Requests */}
{joinRequests.length > 0 && ( {joinRequests.length > 0 && (
<div className="bg-white rounded-lg shadow-md p-6"> <div className="bg-white dark:bg-neutral-900 rounded-lg shadow-md dark:shadow-neutral-950/50 p-6">
<h2 className="text-xl font-bold text-gray-900 mb-4"> <h2 className="text-xl font-bold text-gray-900 dark:text-white mb-4">
Join Requests ({joinRequests.length}) Join Requests ({joinRequests.length})
</h2> </h2>
<div className="space-y-3"> <div className="space-y-3">
{joinRequests.map((request) => ( {joinRequests.map((request) => (
<div key={request.id} className="border rounded-lg p-4"> <div key={request.id} className="border dark:border-neutral-700 rounded-lg p-4">
<div className="flex items-start justify-between"> <div className="flex items-start justify-between">
<div className="flex items-center space-x-3 flex-1"> <div className="flex items-center space-x-3 flex-1">
{request.user.avatar ? ( {request.user.avatar ? (
@@ -142,17 +142,17 @@ const ManageMembersPage: FC = (): ReactElement => {
className="w-12 h-12 rounded-full object-cover" className="w-12 h-12 rounded-full object-cover"
/> />
) : ( ) : (
<div className="w-12 h-12 rounded-full bg-gray-200 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">👤</span> <span className="text-gray-500 dark:text-neutral-400">👤</span>
</div> </div>
)} )}
<div className="flex-1"> <div className="flex-1">
<p className="font-medium text-gray-900">{request.user.fullname}</p> <p className="font-medium text-gray-900 dark:text-white">{request.user.fullname}</p>
<p className="text-sm text-gray-600">{request.user.email}</p> <p className="text-sm text-gray-600 dark:text-neutral-400">{request.user.email}</p>
{request.user.location && ( {request.user.location && (
<p className="text-sm text-gray-500">📍 {request.user.location}</p> <p className="text-sm text-gray-500 dark:text-neutral-500">📍 {request.user.location}</p>
)} )}
<p className="text-sm text-gray-700 mt-2 italic">"{request.message}"</p> <p className="text-sm text-gray-700 dark:text-neutral-300 mt-2 italic">"{request.message}"</p>
</div> </div>
</div> </div>
<div className="flex space-x-2 ml-4"> <div className="flex space-x-2 ml-4">
@@ -180,16 +180,16 @@ const ManageMembersPage: FC = (): ReactElement => {
)} )}
{/* Current Members */} {/* Current Members */}
<div className="bg-white rounded-lg shadow-md p-6"> <div className="bg-white dark:bg-neutral-900 rounded-lg shadow-md dark:shadow-neutral-950/50 p-6">
<h2 className="text-xl font-bold text-gray-900 mb-4"> <h2 className="text-xl font-bold text-gray-900 dark:text-white mb-4">
Current Members ({members.length}) Current Members ({members.length})
</h2> </h2>
{isLoadingMembers ? ( {isLoadingMembers ? (
<p className="text-gray-600">Loading members...</p> <p className="text-gray-600 dark:text-neutral-400">Loading members...</p>
) : ( ) : (
<div className="space-y-3"> <div className="space-y-3">
{members.map((member) => ( {members.map((member) => (
<div key={member.id} className="border rounded-lg p-4 flex items-center justify-between"> <div key={member.id} className="border dark:border-neutral-700 rounded-lg p-4 flex items-center justify-between">
<div className="flex items-center space-x-3"> <div className="flex items-center space-x-3">
{member.user.avatar ? ( {member.user.avatar ? (
<img <img
@@ -198,24 +198,24 @@ const ManageMembersPage: FC = (): ReactElement => {
className="w-12 h-12 rounded-full object-cover" className="w-12 h-12 rounded-full object-cover"
/> />
) : ( ) : (
<div className="w-12 h-12 rounded-full bg-gray-200 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">👤</span> <span className="text-gray-500 dark:text-neutral-400">👤</span>
</div> </div>
)} )}
<div> <div>
<p className="font-medium text-gray-900">{member.user.fullname}</p> <p className="font-medium text-gray-900 dark:text-white">{member.user.fullname}</p>
<p className="text-sm text-gray-600">{member.user.email}</p> <p className="text-sm text-gray-600 dark:text-neutral-400">{member.user.email}</p>
{member.user.location && ( {member.user.location && (
<p className="text-sm text-gray-500">📍 {member.user.location}</p> <p className="text-sm text-gray-500 dark:text-neutral-500">📍 {member.user.location}</p>
)} )}
<div className="flex items-center space-x-2 mt-1"> <div className="flex items-center space-x-2 mt-1">
{member.role === 'leader' && ( {member.role === 'leader' && (
<span className="px-2 py-1 bg-blue-100 text-blue-800 rounded text-xs font-medium"> <span className="px-2 py-1 bg-blue-100 dark:bg-primary-900/30 text-blue-800 dark:text-primary-300 rounded text-xs font-medium">
Leader Leader
</span> </span>
)} )}
{member.status === ETeamMemberStatus.PENDING && ( {member.status === ETeamMemberStatus.PENDING && (
<span className="px-2 py-1 bg-yellow-100 text-yellow-800 rounded text-xs font-medium"> <span className="px-2 py-1 bg-yellow-100 dark:bg-yellow-900/30 text-yellow-800 dark:text-yellow-300 rounded text-xs font-medium">
Pending Invitation Pending Invitation
</span> </span>
)} )}
@@ -239,8 +239,8 @@ const ManageMembersPage: FC = (): ReactElement => {
</div> </div>
{/* Warning */} {/* Warning */}
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4"> <div className="bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4">
<p className="text-sm text-yellow-800"> <p className="text-sm text-yellow-800 dark:text-yellow-200">
<strong>Note:</strong> Members cannot leave the team without your approval. Only you can remove members from the team. <strong>Note:</strong> Members cannot leave the team without your approval. Only you can remove members from the team.
</p> </p>
</div> </div>
@@ -248,22 +248,22 @@ const ManageMembersPage: FC = (): ReactElement => {
{/* Invite Member Modal */} {/* Invite Member Modal */}
{showInviteModal && ( {showInviteModal && (
<div className="fixed inset-0 bg-black/20 backdrop-blur-sm flex items-center justify-center z-50 p-4"> <div className="fixed inset-0 bg-black/20 dark:bg-black/50 backdrop-blur-sm flex items-center justify-center z-50 p-4">
<div className="bg-white rounded-lg shadow-xl max-w-md w-full p-6"> <div className="bg-white dark:bg-neutral-900 rounded-lg shadow-xl dark:shadow-neutral-950/50 max-w-md w-full p-6">
<h2 className="text-2xl font-bold text-gray-900 mb-4"> <h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">
Invite Member Invite Member
</h2> </h2>
<p className="text-gray-600 mb-4"> <p className="text-gray-600 dark:text-neutral-400 mb-4">
Send an invitation to join your team. The invited member will see the invitation on their dashboard after logging in. Send an invitation to join your team. The invited member will see the invitation on their dashboard after logging in.
</p> </p>
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-3 mb-6"> <div className="bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-3 mb-6">
<p className="text-sm text-yellow-800"> <p className="text-sm text-yellow-800 dark:text-yellow-200">
<strong>Important:</strong> The email you enter must match the GitHub email address the member uses to sign in. <strong>Important:</strong> The email you enter must match the GitHub email address the member uses to sign in.
</p> </p>
</div> </div>
<form onSubmit={handleInvite} className="space-y-4"> <form onSubmit={handleInvite} className="space-y-4">
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-2"> <label className="block text-sm font-medium text-gray-700 dark:text-neutral-300 mb-2">
Email Address Email Address
</label> </label>
<Input <Input
@@ -15,18 +15,18 @@ const SubmissionViewPage: FC = (): ReactElement => {
if (isLoading) { if (isLoading) {
return ( return (
<div className="flex items-center justify-center min-h-screen"> <div className="flex items-center justify-center min-h-screen bg-gray-50 dark:bg-neutral-950">
<div className="text-gray-600">Loading submission...</div> <div className="text-gray-600 dark:text-neutral-400">Loading submission...</div>
</div> </div>
); );
} }
if (!submission) { if (!submission) {
return ( return (
<div className="flex flex-col items-center justify-center min-h-screen"> <div className="flex flex-col items-center justify-center min-h-screen bg-gray-50 dark:bg-neutral-950">
<div className="text-6xl mb-4">📄</div> <div className="text-6xl mb-4">📄</div>
<h2 className="text-2xl font-bold text-gray-900 mb-4">No Submission Yet</h2> <h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">No Submission Yet</h2>
<p className="text-gray-600 mb-4">Your team hasn't submitted a project</p> <p className="text-gray-600 dark:text-neutral-400 mb-4">Your team hasn't submitted a project</p>
<Button onClick={() => navigate(`/teams/${teamId}`)}>Back to Team</Button> <Button onClick={() => navigate(`/teams/${teamId}`)}>Back to Team</Button>
</div> </div>
); );
@@ -43,13 +43,13 @@ const SubmissionViewPage: FC = (): ReactElement => {
: 'Not submitted'; : 'Not submitted';
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-gray-50 dark:bg-neutral-950">
<div className="bg-white border-b"> <div className="bg-white dark:bg-neutral-900 border-b dark:border-neutral-700">
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 py-6"> <div className="max-w-6xl 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">Project Submission</h1> <h1 className="text-3xl font-bold text-gray-900 dark:text-white">Project Submission</h1>
<p className="text-gray-600 mt-1">{team?.name}</p> <p className="text-gray-600 dark:text-neutral-400 mt-1">{team?.name}</p>
</div> </div>
<Button variant="secondary" onClick={() => navigate(`/teams/${teamId}`)}> <Button variant="secondary" onClick={() => navigate(`/teams/${teamId}`)}>
Back to Team Back to Team
@@ -60,22 +60,22 @@ const SubmissionViewPage: FC = (): ReactElement => {
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> <div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Status Banner */} {/* Status Banner */}
<div className="bg-green-50 border border-green-500 rounded-lg p-6 mb-6"> <div className="bg-green-50 dark:bg-green-900/20 border border-green-500 rounded-lg p-6 mb-6">
<div className="flex items-center space-x-3"> <div className="flex items-center space-x-3">
<span className="text-4xl">✅</span> <span className="text-4xl">✅</span>
<div> <div>
<h3 className="font-bold text-green-900 text-lg">Project Submitted Successfully</h3> <h3 className="font-bold text-green-900 dark:text-green-100 text-lg">Project Submitted Successfully</h3>
<p className="text-green-700 text-sm"> <p className="text-green-700 dark:text-green-300 text-sm">
Submitted on {submittedDate} Submitted on {submittedDate}
</p> </p>
<p className="text-green-600 text-xs mt-1"> <p className="text-green-600 dark:text-green-400 text-xs mt-1">
This submission is now read-only and cannot be edited This submission is now read-only and cannot be edited
</p> </p>
</div> </div>
</div> </div>
</div> </div>
<div className="bg-white rounded-lg shadow-md overflow-hidden"> <div className="bg-white dark:bg-neutral-900 rounded-lg shadow-md dark:shadow-neutral-950/50 overflow-hidden">
{/* Project Header */} {/* Project Header */}
<div className="bg-gradient-to-r from-blue-600 to-blue-800 text-white p-8"> <div className="bg-gradient-to-r from-blue-600 to-blue-800 text-white p-8">
<h2 className="text-3xl font-bold mb-2">{submission.project_name}</h2> <h2 className="text-3xl font-bold mb-2">{submission.project_name}</h2>
@@ -86,9 +86,9 @@ const SubmissionViewPage: FC = (): ReactElement => {
<div className="p-8 space-y-6"> <div className="p-8 space-y-6">
{/* Description */} {/* Description */}
<div> <div>
<h3 className="text-lg font-bold text-gray-900 mb-3">Project Description</h3> <h3 className="text-lg font-bold text-gray-900 dark:text-white mb-3">Project Description</h3>
<div className="bg-gray-50 rounded-lg p-4"> <div className="bg-gray-50 dark:bg-neutral-800 rounded-lg p-4">
<p className="text-gray-700 whitespace-pre-wrap">{submission.description}</p> <p className="text-gray-700 dark:text-neutral-300 whitespace-pre-wrap">{submission.description}</p>
</div> </div>
</div> </div>
@@ -96,12 +96,12 @@ const SubmissionViewPage: FC = (): ReactElement => {
<div className="grid gap-6 md:grid-cols-2"> <div className="grid gap-6 md:grid-cols-2">
{/* Repository */} {/* Repository */}
<div> <div>
<h3 className="text-lg font-bold text-gray-900 mb-3">Repository</h3> <h3 className="text-lg font-bold text-gray-900 dark:text-white mb-3">Repository</h3>
<a <a
href={submission.repository_url} href={submission.repository_url}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="flex items-center space-x-2 text-blue-600 hover:text-blue-800" className="flex items-center space-x-2 text-blue-600 dark:text-primary-400 hover:text-blue-800 dark:hover:text-primary-300"
> >
<span>🔗</span> <span>🔗</span>
<span className="break-all">{submission.repository_url}</span> <span className="break-all">{submission.repository_url}</span>
@@ -111,12 +111,12 @@ const SubmissionViewPage: FC = (): ReactElement => {
{/* Demo URL */} {/* Demo URL */}
{submission.demo_url && ( {submission.demo_url && (
<div> <div>
<h3 className="text-lg font-bold text-gray-900 mb-3">Live Demo</h3> <h3 className="text-lg font-bold text-gray-900 dark:text-white mb-3">Live Demo</h3>
<a <a
href={submission.demo_url} href={submission.demo_url}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="flex items-center space-x-2 text-blue-600 hover:text-blue-800" className="flex items-center space-x-2 text-blue-600 dark:text-primary-400 hover:text-blue-800 dark:hover:text-primary-300"
> >
<span>🌐</span> <span>🌐</span>
<span className="break-all">{submission.demo_url}</span> <span className="break-all">{submission.demo_url}</span>
@@ -127,12 +127,12 @@ const SubmissionViewPage: FC = (): ReactElement => {
{/* Presentation URL */} {/* Presentation URL */}
{submission.presentation_url && ( {submission.presentation_url && (
<div> <div>
<h3 className="text-lg font-bold text-gray-900 mb-3">Presentation</h3> <h3 className="text-lg font-bold text-gray-900 dark:text-white mb-3">Presentation</h3>
<a <a
href={submission.presentation_url} href={submission.presentation_url}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="flex items-center space-x-2 text-blue-600 hover:text-blue-800" className="flex items-center space-x-2 text-blue-600 dark:text-primary-400 hover:text-blue-800 dark:hover:text-primary-300"
> >
<span>📊</span> <span>📊</span>
<span className="break-all">{submission.presentation_url}</span> <span className="break-all">{submission.presentation_url}</span>
@@ -144,7 +144,7 @@ const SubmissionViewPage: FC = (): ReactElement => {
{/* Screenshots */} {/* Screenshots */}
{submission.screenshots && submission.screenshots.length > 0 && ( {submission.screenshots && submission.screenshots.length > 0 && (
<div> <div>
<h3 className="text-lg font-bold text-gray-900 mb-3"> <h3 className="text-lg font-bold text-gray-900 dark:text-white mb-3">
Screenshots ({submission.screenshots.length}) Screenshots ({submission.screenshots.length})
</h3> </h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
@@ -159,7 +159,7 @@ const SubmissionViewPage: FC = (): ReactElement => {
<img <img
src={url} src={url}
alt={`Screenshot ${index + 1}`} alt={`Screenshot ${index + 1}`}
className="w-full h-48 object-cover rounded-lg border-2 border-gray-200 hover:border-blue-500 transition-colors cursor-pointer" className="w-full h-48 object-cover rounded-lg border-2 border-gray-200 dark:border-neutral-700 hover:border-blue-500 dark:hover:border-primary-500 transition-colors cursor-pointer"
/> />
</a> </a>
))} ))}
@@ -168,22 +168,22 @@ const SubmissionViewPage: FC = (): ReactElement => {
)} )}
{/* Submission Info */} {/* Submission Info */}
<div className="bg-gray-50 rounded-lg p-4 border-t-4 border-blue-600"> <div className="bg-gray-50 dark:bg-neutral-800 rounded-lg p-4 border-t-4 border-blue-600 dark:border-primary-500">
<h3 className="text-sm font-bold text-gray-900 mb-2">Submission Information</h3> <h3 className="text-sm font-bold text-gray-900 dark:text-white mb-2">Submission Information</h3>
<div className="grid gap-2 text-sm"> <div className="grid gap-2 text-sm">
<div className="flex justify-between"> <div className="flex justify-between">
<span className="text-gray-600">Status:</span> <span className="text-gray-600 dark:text-neutral-400">Status:</span>
<span className="font-medium text-green-600"> <span className="font-medium text-green-600 dark:text-green-400">
{submission.status === 'submitted' ? ' Submitted' : 'Draft'} {submission.status === 'submitted' ? ' Submitted' : 'Draft'}
</span> </span>
</div> </div>
<div className="flex justify-between"> <div className="flex justify-between">
<span className="text-gray-600">Submitted:</span> <span className="text-gray-600 dark:text-neutral-400">Submitted:</span>
<span className="font-medium text-gray-900">{submittedDate}</span> <span className="font-medium text-gray-900 dark:text-white">{submittedDate}</span>
</div> </div>
<div className="flex justify-between"> <div className="flex justify-between">
<span className="text-gray-600">Submission ID:</span> <span className="text-gray-600 dark:text-neutral-400">Submission ID:</span>
<span className="font-medium text-gray-900 font-mono text-xs"> <span className="font-medium text-gray-900 dark:text-white font-mono text-xs">
{submission.id} {submission.id}
</span> </span>
</div> </div>
@@ -191,8 +191,8 @@ const SubmissionViewPage: FC = (): ReactElement => {
</div> </div>
{/* Read-only Notice */} {/* Read-only Notice */}
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4"> <div className="bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4">
<p className="text-sm text-yellow-800"> <p className="text-sm text-yellow-800 dark:text-yellow-200">
<strong>Note:</strong> This submission is now locked and cannot be edited or deleted. <strong>Note:</strong> This submission is now locked and cannot be edited or deleted.
If you need to make changes, please contact the hackathon organizers. If you need to make changes, please contact the hackathon organizers.
</p> </p>
@@ -38,9 +38,9 @@ const SubmitProjectPage: FC = (): ReactElement => {
if (!isLeader) { if (!isLeader) {
return ( return (
<div className="flex flex-col items-center justify-center min-h-screen"> <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 mb-4">Access Denied</h2> <h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">Access Denied</h2>
<p className="text-gray-600 mb-4">Only the team leader can submit projects</p> <p className="text-gray-600 dark:text-neutral-400 mb-4">Only the team leader can submit projects</p>
<Button onClick={() => navigate(`/teams/${teamId}`)}>Back to Team</Button> <Button onClick={() => navigate(`/teams/${teamId}`)}>Back to Team</Button>
</div> </div>
); );
@@ -48,10 +48,10 @@ const SubmitProjectPage: FC = (): ReactElement => {
if (hasSubmission) { if (hasSubmission) {
return ( return (
<div className="flex flex-col items-center justify-center min-h-screen"> <div className="flex flex-col items-center justify-center min-h-screen bg-gray-50 dark:bg-neutral-950">
<div className="text-6xl mb-4"></div> <div className="text-6xl mb-4"></div>
<h2 className="text-2xl font-bold text-gray-900 mb-4">Project Already Submitted</h2> <h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">Project Already Submitted</h2>
<p className="text-gray-600 mb-4">Your team has already submitted a project</p> <p className="text-gray-600 dark:text-neutral-400 mb-4">Your team has already submitted a project</p>
<div className="flex space-x-3"> <div className="flex space-x-3">
<Button onClick={() => navigate(`/teams/${teamId}/submission`)}> <Button onClick={() => navigate(`/teams/${teamId}/submission`)}>
View Submission View Submission
@@ -95,22 +95,22 @@ const SubmitProjectPage: FC = (): ReactElement => {
}); });
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-gray-50 dark:bg-neutral-950">
<div className="bg-white border-b"> <div className="bg-white dark:bg-neutral-900 border-b dark:border-neutral-700">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-6"> <div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
<h1 className="text-3xl font-bold text-gray-900">Submit Project</h1> <h1 className="text-3xl font-bold text-gray-900 dark:text-white">Submit Project</h1>
<p className="text-gray-600 mt-1">{team?.name}</p> <p className="text-gray-600 dark:text-neutral-400 mt-1">{team?.name}</p>
</div> </div>
</div> </div>
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> <div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Warning Banner */} {/* Warning Banner */}
<div className="bg-red-50 border-2 border-red-500 rounded-lg p-6 mb-6"> <div className="bg-red-50 dark:bg-red-900/20 border-2 border-red-500 rounded-lg p-6 mb-6">
<div className="flex items-start space-x-3"> <div className="flex items-start space-x-3">
<span className="text-3xl"></span> <span className="text-3xl"></span>
<div> <div>
<h3 className="font-bold text-red-900 text-lg">IMPORTANT WARNING</h3> <h3 className="font-bold text-red-900 dark:text-red-100 text-lg">IMPORTANT WARNING</h3>
<ul className="text-red-800 mt-2 space-y-1 text-sm"> <ul className="text-red-800 dark:text-red-200 mt-2 space-y-1 text-sm">
<li> You can only submit your project ONCE</li> <li> You can only submit your project ONCE</li>
<li> After submission, you CANNOT edit or change anything</li> <li> After submission, you CANNOT edit or change anything</li>
<li> Make sure all information is correct before submitting</li> <li> Make sure all information is correct before submitting</li>
@@ -120,7 +120,7 @@ const SubmitProjectPage: FC = (): ReactElement => {
</div> </div>
</div> </div>
<div className="bg-white rounded-lg shadow-md p-8"> <div className="bg-white dark:bg-neutral-900 rounded-lg shadow-md dark:shadow-neutral-950/50 p-8">
<form onSubmit={(e) => { <form onSubmit={(e) => {
e.preventDefault(); e.preventDefault();
setShowConfirmModal(true); setShowConfirmModal(true);
@@ -135,7 +135,7 @@ const SubmitProjectPage: FC = (): ReactElement => {
{/* Description */} {/* Description */}
<div className="space-y-2"> <div className="space-y-2">
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700 dark:text-neutral-300">
Project Description <span className="text-red-500">*</span> Project Description <span className="text-red-500">*</span>
</label> </label>
<Controller <Controller
@@ -186,8 +186,8 @@ const SubmitProjectPage: FC = (): ReactElement => {
{/* Screenshots */} {/* Screenshots */}
<div className="space-y-2"> <div className="space-y-2">
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700 dark:text-neutral-300">
Project Screenshots <span className="text-gray-400">(Optional)</span> Project Screenshots <span className="text-gray-400 dark:text-neutral-500">(Optional)</span>
</label> </label>
<div className="grid grid-cols-2 md:grid-cols-3 gap-4 mb-4"> <div className="grid grid-cols-2 md:grid-cols-3 gap-4 mb-4">
{screenshots.map((url, index) => ( {screenshots.map((url, index) => (
@@ -195,7 +195,7 @@ const SubmitProjectPage: FC = (): ReactElement => {
<img <img
src={url} src={url}
alt={`Screenshot ${index + 1}`} alt={`Screenshot ${index + 1}`}
className="w-full h-32 object-cover rounded-lg border-2 border-gray-200" className="w-full h-32 object-cover rounded-lg border-2 border-gray-200 dark:border-neutral-700"
/> />
<button <button
type="button" type="button"
@@ -207,10 +207,10 @@ const SubmitProjectPage: FC = (): ReactElement => {
</div> </div>
))} ))}
</div> </div>
<label className="flex flex-col items-center justify-center w-full h-32 border-2 border-dashed border-gray-300 rounded-lg cursor-pointer hover:bg-gray-50"> <label className="flex flex-col items-center justify-center w-full h-32 border-2 border-dashed border-gray-300 dark:border-neutral-600 rounded-lg cursor-pointer hover:bg-gray-50 dark:hover:bg-neutral-800">
<div className="text-center"> <div className="text-center">
<p className="text-gray-500">Click to upload screenshots</p> <p className="text-gray-500 dark:text-neutral-400">Click to upload screenshots</p>
<p className="text-xs text-gray-400 mt-1">PNG, JPG up to 5MB each</p> <p className="text-xs text-gray-400 dark:text-neutral-500 mt-1">PNG, JPG up to 5MB each</p>
</div> </div>
<input <input
type="file" type="file"
@@ -247,22 +247,22 @@ const SubmitProjectPage: FC = (): ReactElement => {
{/* Confirmation Modal */} {/* Confirmation Modal */}
{showConfirmModal && ( {showConfirmModal && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4"> <div className="fixed inset-0 bg-black/50 dark:bg-black/70 flex items-center justify-center z-50 p-4">
<div className="bg-white rounded-lg shadow-xl max-w-md w-full p-6"> <div className="bg-white dark:bg-neutral-900 rounded-lg shadow-xl dark:shadow-neutral-950/50 max-w-md w-full p-6">
<div className="text-center mb-6"> <div className="text-center mb-6">
<div className="text-5xl mb-4"></div> <div className="text-5xl mb-4"></div>
<h2 className="text-2xl font-bold text-gray-900 mb-2"> <h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-2">
Final Confirmation Final Confirmation
</h2> </h2>
<p className="text-red-600 font-medium"> <p className="text-red-600 dark:text-red-400 font-medium">
This action is IRREVERSIBLE! This action is IRREVERSIBLE!
</p> </p>
</div> </div>
<div className="bg-gray-50 rounded-lg p-4 mb-6"> <div className="bg-gray-50 dark:bg-neutral-800 rounded-lg p-4 mb-6">
<p className="text-sm text-gray-700 mb-3"> <p className="text-sm text-gray-700 dark:text-neutral-300 mb-3">
By clicking "Submit Project", you confirm that: By clicking "Submit Project", you confirm that:
</p> </p>
<ul className="text-sm text-gray-600 space-y-2"> <ul className="text-sm text-gray-600 dark:text-neutral-400 space-y-2">
<li> All information is correct and complete</li> <li> All information is correct and complete</li>
<li> You understand this can only be done once</li> <li> You understand this can only be done once</li>
<li> You cannot edit after submission</li> <li> You cannot edit after submission</li>
+35 -44
View File
@@ -5,7 +5,8 @@ import { useNavigate } from 'react-router';
import { useForm, Controller } from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import { teamCreateSchema, TTeamCreateForm, useCreateTeam, ETeamVisibility, useUploadFile } from '@imphnen-frontend-service/service'; import { teamCreateSchema, TTeamCreateForm, useCreateTeam, ETeamVisibility, useUploadFile } from '@imphnen-frontend-service/service';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import INDONESIAN_CITIES from '../../../constants/cities';
import { CitySelect } from '../../../components/city-select';
const CreateTeamPage: FC = (): ReactElement => { const CreateTeamPage: FC = (): ReactElement => {
const navigate = useNavigate(); const navigate = useNavigate();
@@ -79,29 +80,29 @@ const CreateTeamPage: FC = (): ReactElement => {
}); });
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-gray-50 dark:bg-neutral-950">
{/* Header */} {/* Header */}
<div className="bg-white border-b"> <div className="bg-white dark:bg-neutral-900 border-b dark:border-neutral-700">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-6"> <div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
<h1 className="text-3xl font-bold text-gray-900">Create Your Team</h1> <h1 className="text-3xl font-bold text-gray-900 dark:text-white">Create Your Team</h1>
<p className="text-gray-600 mt-1">Build your hackathon dream team</p> <p className="text-gray-600 dark:text-neutral-400 mt-1">Build your hackathon dream team</p>
</div> </div>
</div> </div>
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> <div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="bg-white rounded-lg shadow-md p-8"> <div className="bg-white dark:bg-neutral-900 rounded-lg shadow-md dark:shadow-neutral-900/50 p-8">
<form onSubmit={onSubmit} className="space-y-6"> <form onSubmit={onSubmit} className="space-y-6">
{/* Banner Upload */} {/* Banner Upload */}
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-2"> <label className="block text-sm font-medium text-gray-700 dark:text-neutral-300 mb-2">
Team Banner <span className="text-gray-400">(Optional)</span> Team Banner <span className="text-gray-400 dark:text-neutral-500">(Optional)</span>
</label> </label>
{bannerPreview ? ( {bannerPreview ? (
<div className="relative"> <div className="relative">
<img <img
src={bannerPreview} src={bannerPreview}
alt="Banner preview" alt="Banner preview"
className="w-full h-48 object-cover rounded-lg border-2 border-gray-200" className="w-full h-48 object-cover rounded-lg border-2 border-gray-200 dark:border-neutral-700"
/> />
<button <button
type="button" type="button"
@@ -115,10 +116,10 @@ const CreateTeamPage: FC = (): ReactElement => {
</button> </button>
</div> </div>
) : ( ) : (
<label className="flex flex-col items-center justify-center w-full h-48 border-2 border-dashed border-gray-300 rounded-lg cursor-pointer hover:bg-gray-50"> <label className="flex flex-col items-center justify-center w-full h-48 border-2 border-dashed border-gray-300 dark:border-neutral-600 rounded-lg cursor-pointer hover:bg-gray-50 dark:hover:bg-neutral-800">
<div className="text-center"> <div className="text-center">
<p className="text-gray-500">Click to upload banner</p> <p className="text-gray-500 dark:text-neutral-400">Click to upload banner</p>
<p className="text-xs text-gray-400 mt-1">1200x400 recommended</p> <p className="text-xs text-gray-400 dark:text-neutral-500 mt-1">1200x400 recommended</p>
</div> </div>
<input <input
type="file" type="file"
@@ -132,19 +133,19 @@ const CreateTeamPage: FC = (): ReactElement => {
{/* Logo Upload */} {/* Logo Upload */}
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-2"> <label className="block text-sm font-medium text-gray-700 dark:text-neutral-300 mb-2">
Team Logo <span className="text-gray-400">(Optional, but highly recommended)</span> Team Logo <span className="text-gray-400 dark:text-neutral-500">(Optional, but highly recommended)</span>
</label> </label>
<div className="flex items-center space-x-4"> <div className="flex items-center space-x-4">
{logoPreview ? ( {logoPreview ? (
<img <img
src={logoPreview} src={logoPreview}
alt="Logo preview" alt="Logo preview"
className="w-24 h-24 rounded-full object-cover border-2 border-gray-200" className="w-24 h-24 rounded-full object-cover border-2 border-gray-200 dark:border-neutral-700"
/> />
) : ( ) : (
<div className="w-24 h-24 rounded-full bg-gray-200 flex items-center justify-center"> <div className="w-24 h-24 rounded-full bg-gray-200 dark:bg-neutral-700 flex items-center justify-center">
<span className="text-gray-400 text-3xl">👥</span> <span className="text-gray-400 dark:text-neutral-500 text-3xl">👥</span>
</div> </div>
)} )}
<div> <div>
@@ -186,36 +187,26 @@ const CreateTeamPage: FC = (): ReactElement => {
{/* City */} {/* City */}
<div className="space-y-2"> <div className="space-y-2">
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700 dark:text-neutral-300">
City <span className="text-red-500">*</span> City <span className="text-red-500">*</span>
</label> </label>
<Controller <Controller
control={form.control} control={form.control}
name="city" name="city"
render={({ field, fieldState }) => ( render={({ field, fieldState }) => (
<div> <CitySelect
<select value={field.value}
{...field} onChange={field.onChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" error={fieldState.error?.message}
> placeholder="Search your city..."
<option value="">Select city</option> />
{INDONESIAN_CITIES.map((city) => (
<option key={city} value={city}>
{city}
</option>
))}
</select>
{fieldState.error && (
<p className="text-sm text-red-500 mt-1">{fieldState.error.message}</p>
)}
</div>
)} )}
/> />
</div> </div>
{/* Description */} {/* Description */}
<div className="space-y-2"> <div className="space-y-2">
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700 dark:text-neutral-300">
Description <span className="text-red-500">*</span> Description <span className="text-red-500">*</span>
</label> </label>
<Controller <Controller
@@ -239,7 +230,7 @@ const CreateTeamPage: FC = (): ReactElement => {
{/* Visibility */} {/* Visibility */}
<div className="space-y-2"> <div className="space-y-2">
<label className="block text-sm font-medium text-gray-700"> <label className="block text-sm font-medium text-gray-700 dark:text-neutral-300">
Team Visibility <span className="text-red-500">*</span> Team Visibility <span className="text-red-500">*</span>
</label> </label>
<Controller <Controller
@@ -247,7 +238,7 @@ const CreateTeamPage: FC = (): ReactElement => {
name="visibility" name="visibility"
render={({ field }) => ( render={({ field }) => (
<div className="space-y-3"> <div className="space-y-3">
<label className="flex items-start space-x-3 cursor-pointer border rounded-lg p-4 hover:bg-gray-50"> <label className="flex items-start space-x-3 cursor-pointer border dark:border-neutral-700 rounded-lg p-4 hover:bg-gray-50 dark:hover:bg-neutral-800">
<input <input
type="radio" type="radio"
{...field} {...field}
@@ -256,13 +247,13 @@ const CreateTeamPage: FC = (): ReactElement => {
className="mt-1" className="mt-1"
/> />
<div> <div>
<p className="font-medium text-gray-900">Public</p> <p className="font-medium text-gray-900 dark:text-white">Public</p>
<p className="text-sm text-gray-600"> <p className="text-sm text-gray-600 dark:text-neutral-400">
Team will be visible in Browse Teams. Anyone can request to join. Team will be visible in Browse Teams. Anyone can request to join.
</p> </p>
</div> </div>
</label> </label>
<label className="flex items-start space-x-3 cursor-pointer border rounded-lg p-4 hover:bg-gray-50"> <label className="flex items-start space-x-3 cursor-pointer border dark:border-neutral-700 rounded-lg p-4 hover:bg-gray-50 dark:hover:bg-neutral-800">
<input <input
type="radio" type="radio"
{...field} {...field}
@@ -271,8 +262,8 @@ const CreateTeamPage: FC = (): ReactElement => {
className="mt-1" className="mt-1"
/> />
<div> <div>
<p className="font-medium text-gray-900">Private</p> <p className="font-medium text-gray-900 dark:text-white">Private</p>
<p className="text-sm text-gray-600"> <p className="text-sm text-gray-600 dark:text-neutral-400">
Team is hidden from Browse Teams. Members can only join via invitation. Team is hidden from Browse Teams. Members can only join via invitation.
</p> </p>
</div> </div>
@@ -283,8 +274,8 @@ const CreateTeamPage: FC = (): ReactElement => {
</div> </div>
{/* Warning */} {/* Warning */}
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4"> <div className="bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4">
<p className="text-sm text-yellow-800"> <p className="text-sm text-yellow-800 dark:text-yellow-200">
<strong>Note:</strong> As team leader, you cannot leave or join another team after creating this team. <strong>Note:</strong> As team leader, you cannot leave or join another team after creating this team.
</p> </p>
</div> </div>
+34 -34
View File
@@ -14,26 +14,26 @@ const UserProfilePage: FC = (): ReactElement => {
if (isLoading) { if (isLoading) {
return ( return (
<div className="flex items-center justify-center min-h-screen"> <div className="flex items-center justify-center min-h-screen bg-gray-50 dark:bg-neutral-950">
<div className="text-gray-600">Loading user profile...</div> <div className="text-gray-600 dark:text-neutral-400">Loading user profile...</div>
</div> </div>
); );
} }
if (error || !user) { if (error || !user) {
return ( return (
<div className="flex flex-col items-center justify-center min-h-screen"> <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 mb-4">User not found</h2> <h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">User not found</h2>
{error && <p className="text-red-600 mb-4">{String(error)}</p>} {error && <p className="text-red-600 dark:text-red-400 mb-4">{String(error)}</p>}
<Button onClick={() => navigate('/dashboard')}>Back to Dashboard</Button> <Button onClick={() => navigate('/dashboard')}>Back to Dashboard</Button>
</div> </div>
); );
} }
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-gray-50 dark:bg-neutral-950">
{/* Header */} {/* Header */}
<div className="bg-white border-b"> <div className="bg-white dark:bg-neutral-900 border-b dark:border-neutral-700">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4 md:py-6"> <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4 md:py-6">
<div className="flex items-start justify-between"> <div className="flex items-start justify-between">
<div className="flex items-center space-x-3 md:space-x-4"> <div className="flex items-center space-x-3 md:space-x-4">
@@ -41,19 +41,19 @@ const UserProfilePage: FC = (): ReactElement => {
<img <img
src={user.avatar} src={user.avatar}
alt={user.fullname} alt={user.fullname}
className="w-16 h-16 md:w-20 md:h-20 rounded-full object-cover border-4 border-white shadow-lg" className="w-16 h-16 md:w-20 md:h-20 rounded-full object-cover 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 flex items-center justify-center border-4 border-white 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 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">{user.fullname}</h1> <h1 className="text-xl md:text-3xl font-bold text-gray-900 dark:text-white">{user.fullname}</h1>
<p className="text-sm md:text-base text-gray-600 mt-1">{user.email}</p> <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">📍 {user.location}</span> <span className="text-sm text-gray-500 dark:text-neutral-500">📍 {user.location}</span>
</div> </div>
)} )}
</div> </div>
@@ -71,21 +71,21 @@ const UserProfilePage: FC = (): ReactElement => {
<div className="lg:col-span-2 space-y-4 md:space-y-6"> <div className="lg:col-span-2 space-y-4 md:space-y-6">
{/* About Section */} {/* About Section */}
{user.bio && ( {user.bio && (
<div className="bg-white rounded-lg shadow-md 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 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">About</h2>
<p className="text-gray-700 break-all overflow-wrap-anywhere">{user.bio}</p> <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 rounded-lg shadow-md 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 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
key={skill} key={skill}
className="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg text-sm font-medium shadow-sm hover:bg-blue-700 transition-colors" className="inline-flex items-center px-4 py-2 bg-blue-600 dark:bg-primary-600 text-white rounded-lg text-sm font-medium shadow-sm hover:bg-blue-700 dark:hover:bg-primary-700 transition-colors"
> >
{skill} {skill}
</span> </span>
@@ -96,35 +96,35 @@ const UserProfilePage: FC = (): ReactElement => {
{/* Teams Section */} {/* Teams Section */}
{userTeams.length > 0 ? ( {userTeams.length > 0 ? (
<div className="bg-white rounded-lg shadow-md 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 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 rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow"> <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">
<img src={team.banner || '/images/banner-imphnen.png'} alt={team.name} className="w-full h-24 object-cover" /> <img src={team.banner || '/images/banner-imphnen.png'} 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 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 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">📍 {team.city}</p>
<p className="whitespace-nowrap shrink-0">👥 {team.members?.length || 0} members</p> <p className="whitespace-nowrap shrink-0">👥 {team.members?.length || 0} members</p>
</div> </div>
</div> </div>
</div> </div>
<p className="text-gray-600 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>
))} ))}
</div> </div>
</div> </div>
) : ( ) : (
<div className="bg-white rounded-lg shadow-md 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 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">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>
)} )}
@@ -133,17 +133,17 @@ const UserProfilePage: FC = (): ReactElement => {
{/* Sidebar */} {/* Sidebar */}
<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 rounded-lg shadow-md 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 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">Email</p> <p className="text-sm text-gray-600 dark:text-neutral-400">Email</p>
<p className="text-gray-900 font-medium">{user.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">Location</p> <p className="text-sm text-gray-600 dark:text-neutral-400">Location</p>
<p className="text-gray-900 font-medium">{user.location}</p> <p className="text-gray-900 dark:text-white font-medium">{user.location}</p>
</div> </div>
)} )}
</div> </div>
+6 -6
View File
@@ -23,12 +23,12 @@
--color-neutral-200: #d1d1d1; --color-neutral-200: #d1d1d1;
--color-neutral-300: #b0b0b0; --color-neutral-300: #b0b0b0;
--color-neutral-400: #888888; --color-neutral-400: #888888;
--color-neutral-500: #6d6d6d; --color-neutral-500: #6e6a86;
--color-neutral-600: #5d5d5d; --color-neutral-600: #56526e;
--color-neutral-700: #4f4f4f; --color-neutral-700: #44415a;
--color-neutral-800: #454545; --color-neutral-800: #393552;
--color-neutral-900: #3d3d3d; --color-neutral-900: #2a273f;
--color-neutral-950: #2b2b2b; --color-neutral-950: #232136;
--color-success-100: #e0fbd8; --color-success-100: #e0fbd8;
--color-success-200: #bcf8b0; --color-success-200: #bcf8b0;
+4 -4
View File
@@ -26,12 +26,12 @@ type TButtonProps = DetailedHTMLProps<
const variantClasses: Record<TButtonVariant, string> = { const variantClasses: Record<TButtonVariant, string> = {
primary: 'bg-primary-500 hover:bg-primary-600 text-white shadow-md', primary: 'bg-primary-500 hover:bg-primary-600 text-white shadow-md',
secondary: secondary:
'bg-white hover:text-primary-600 hover:bg-gray-50 text-primary-500 shadow-md', 'bg-white dark:bg-neutral-800 hover:text-primary-600 dark:hover:text-primary-400 hover:bg-gray-50 dark:hover:bg-neutral-700 text-primary-500 dark:text-primary-400 shadow-md dark:shadow-neutral-900/50',
text: 'bg-transparent hover:text-primary-600 hover:bg-gray-50 text-primary-500', text: 'bg-transparent hover:text-primary-600 dark:hover:text-primary-400 hover:bg-gray-50 dark:hover:bg-neutral-800 text-primary-500 dark:text-primary-400',
bordered: bordered:
'border border-primary-500 hover:border-primary-600 bg-transparent hover:text-primary-600 hover:bg-gray-50 text-primary-500', 'border border-primary-500 dark:border-primary-400 hover:border-primary-600 dark:hover:border-primary-300 bg-transparent hover:text-primary-600 dark:hover:text-primary-300 hover:bg-gray-50 dark:hover:bg-neutral-800 text-primary-500 dark:text-primary-400',
success: 'bg-success-500 hover:bg-success-600 text-white shadow-md', success: 'bg-success-500 hover:bg-success-600 text-white shadow-md',
danger: 'bg-danger-100 hover:bg-danger-200 text-danger-500 shadow-md', danger: 'bg-danger-100 dark:bg-danger-500/20 hover:bg-danger-200 dark:hover:bg-danger-500/30 text-danger-500 shadow-md dark:shadow-neutral-900/50',
}; };
const sizeClasses: Record<TButtonSize, string> = { const sizeClasses: Record<TButtonSize, string> = {