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 (
|
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}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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> = {
|
||||||
|
|||||||
Reference in New Issue
Block a user