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