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
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user