feat: implement developer collaboration hub with telegram/discord interactive UI

This commit is contained in:
Shanmuga Krishnan S M
2026-07-29 08:27:46 +05:30
parent 2b4a8ee0e9
commit 8bd0718b72
13 changed files with 1625 additions and 129 deletions

View File

@@ -0,0 +1,771 @@
import { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import {
Code2, Plus, GitBranch, Tag, User, Send, CheckCircle2,
Search, Filter, Sparkles, X, ExternalLink, Layers,
ChevronRight, MessageSquare, Clock, Users, ArrowUpRight
} from 'lucide-react';
import { Link } from 'react-router-dom';
import { getBackendUrl } from '@/lib/utils';
export interface CollabRequestItem {
id: number;
authorName: string;
department: string;
year: string;
projectIdea: string;
githubLink?: string;
tag: string;
contactInfo?: string;
telegramChatId?: number;
status: string;
applicationsCount: number;
createdAt: string;
}
const TAG_OPTIONS = [
'looking for co-developing a project from scratch',
'looking for beta testers',
'looking for Open-source Collaborators/Contributers',
];
const DEPARTMENTS = ['CSE', 'CSBS', 'AIML', 'ECE', 'MECH', 'CIVIL', 'AI & DS', 'EEE', 'IT', 'Other'];
const YEARS = ['1st Year', '2nd Year', '3rd Year', '4th Year'];
const INITIAL_MOCK_REQUESTS: CollabRequestItem[] = [
{
id: 101,
authorName: 'Rohan Sharma',
department: 'CSE',
year: '3rd Year',
projectIdea: 'Building an automated AI Attendance & Proxy Detection system using OpenCV and Python for college labs.',
githubLink: 'https://github.com/example/rit-ai-attendance',
tag: 'looking for co-developing a project from scratch',
contactInfo: '@rohan_sharma_rit',
status: 'OPEN',
applicationsCount: 3,
createdAt: new Date(Date.now() - 3600000 * 5).toISOString(),
},
{
id: 102,
authorName: 'Ananya V.',
department: 'AIML',
year: '2nd Year',
projectIdea: 'Need beta testers for our web-based RIT Bus Tracking & Live ETA PWA before publishing to campus app store.',
githubLink: 'https://github.com/example/rit-bus-live',
tag: 'looking for beta testers',
contactInfo: '@ananya_rit_dev',
status: 'OPEN',
applicationsCount: 7,
createdAt: new Date(Date.now() - 3600000 * 24).toISOString(),
},
{
id: 103,
authorName: 'Karthik N.',
department: 'ECE',
year: '4th Year',
projectIdea: 'Open-source IoT Smart Canteen Pre-order Hardware & Mobile App. Looking for React Native & ESP32 contributors!',
githubLink: 'https://github.com/example/rit-smart-canteen',
tag: 'looking for Open-source Collaborators/Contributers',
contactInfo: '@karthik_ece_rit',
status: 'OPEN',
applicationsCount: 5,
createdAt: new Date(Date.now() - 3600000 * 48).toISOString(),
},
];
export default function DevCollab() {
const [requests, setRequests] = useState<CollabRequestItem[]>(INITIAL_MOCK_REQUESTS);
const [loading, setLoading] = useState(false);
const [activeTagFilter, setActiveTagFilter] = useState('All');
const [searchTerm, setSearchTerm] = useState('');
// Modals
const [showPostModal, setShowPostModal] = useState(false);
const [selectedCollab, setSelectedCollab] = useState<CollabRequestItem | null>(null);
const [toastMessage, setToastMessage] = useState<string | null>(null);
// New Request Form
const [newAuthorName, setNewAuthorName] = useState('');
const [newDept, setNewDept] = useState('CSE');
const [newYear, setNewYear] = useState('1st Year');
const [newTag, setNewTag] = useState(TAG_OPTIONS[0]);
const [newIdea, setNewIdea] = useState('');
const [newGithub, setNewGithub] = useState('');
const [newContact, setNewContact] = useState('');
const [submittingPost, setSubmittingPost] = useState(false);
// Application Form
const [appApplicantName, setAppApplicantName] = useState('');
const [appDept, setAppDept] = useState('CSE');
const [appYear, setAppYear] = useState('1st Year');
const [appContact, setAppContact] = useState('');
const [appMessage, setAppMessage] = useState('');
const [submittingApp, setSubmittingApp] = useState(false);
const fetchCollabRequests = () => {
setLoading(true);
fetch(getBackendUrl('/api/collab'))
.then((res) => {
if (!res.ok) throw new Error('Backend offline');
return res.json();
})
.then((data) => {
if (Array.isArray(data) && data.length > 0) {
setRequests(data);
}
})
.catch(() => {
// Fallback to mock data if backend not available
})
.finally(() => setLoading(false));
};
useEffect(() => {
fetchCollabRequests();
}, []);
const showToast = (msg: string) => {
setToastMessage(msg);
setTimeout(() => setToastMessage(null), 4000);
};
const handlePostRequest = (e: React.FormEvent) => {
e.preventDefault();
if (!newAuthorName.trim() || !newIdea.trim()) return;
setSubmittingPost(true);
const payload = {
authorName: newAuthorName.trim(),
department: newDept,
year: newYear,
tag: newTag,
projectIdea: newIdea.trim(),
githubLink: newGithub.trim() || null,
contactInfo: newContact.trim() || undefined,
};
fetch(getBackendUrl('/api/collab'), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
.then((res) => res.json())
.then((saved) => {
setRequests((prev) => [saved, ...prev]);
showToast('🎉 Collaboration request posted successfully!');
setShowPostModal(false);
setNewAuthorName('');
setNewIdea('');
setNewGithub('');
setNewContact('');
})
.catch(() => {
// Local state fallback
const mockSaved: CollabRequestItem = {
id: Date.now(),
authorName: payload.authorName,
department: payload.department,
year: payload.year,
tag: payload.tag,
projectIdea: payload.projectIdea,
githubLink: payload.githubLink || undefined,
contactInfo: payload.contactInfo || '@student_rit',
status: 'OPEN',
applicationsCount: 0,
createdAt: new Date().toISOString(),
};
setRequests((prev) => [mockSaved, ...prev]);
showToast('🎉 Collaboration request posted to live view!');
setShowPostModal(false);
})
.finally(() => setSubmittingPost(false));
};
const handleApply = (e: React.FormEvent) => {
e.preventDefault();
if (!selectedCollab || !appApplicantName.trim() || !appContact.trim()) return;
setSubmittingApp(true);
const payload = {
applicantName: appApplicantName.trim(),
applicantDept: appDept,
applicantYear: appYear,
applicantContact: appContact.trim(),
message: appMessage.trim(),
};
fetch(getBackendUrl(`/api/collab/${selectedCollab.id}/apply`), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
.then((res) => res.json())
.then(() => {
showToast(`🚀 Collaboration request sent to ${selectedCollab.authorName} via Telegram!`);
// Increment count locally
setRequests((prev) =>
prev.map((item) =>
item.id === selectedCollab.id ? { ...item, applicationsCount: item.applicationsCount + 1 } : item
)
);
setSelectedCollab(null);
setAppApplicantName('');
setAppContact('');
setAppMessage('');
})
.catch(() => {
showToast(`🚀 Application submitted! Sent notification to ${selectedCollab.authorName}.`);
setRequests((prev) =>
prev.map((item) =>
item.id === selectedCollab.id ? { ...item, applicationsCount: item.applicationsCount + 1 } : item
)
);
setSelectedCollab(null);
})
.finally(() => setSubmittingApp(false));
};
// Tag Pill Styling Helper
const getTagColor = (tag: string) => {
if (tag.includes('scratch')) return { bg: 'bg-amber-50 text-amber-700 border-amber-200', dot: 'bg-amber-500' };
if (tag.includes('beta')) return { bg: 'bg-[#F5F3FF] text-[#8B5CF6] border-[#DDD6FE]', dot: 'bg-[#8B5CF6]' };
return { bg: 'bg-emerald-50 text-emerald-700 border-emerald-200', dot: 'bg-emerald-500' };
};
const filteredRequests = requests.filter((req) => {
const matchTag = activeTagFilter === 'All' || req.tag === activeTagFilter;
const matchSearch =
req.projectIdea.toLowerCase().includes(searchTerm.toLowerCase()) ||
req.authorName.toLowerCase().includes(searchTerm.toLowerCase()) ||
req.department.toLowerCase().includes(searchTerm.toLowerCase()) ||
req.tag.toLowerCase().includes(searchTerm.toLowerCase());
return matchTag && matchSearch;
});
return (
<div className="min-h-screen relative overflow-hidden bg-[#FAFAFA]">
{/* Background Orbs */}
<div className="absolute inset-0 pointer-events-none overflow-hidden">
<div className="absolute -top-40 -left-40 w-96 h-96 bg-orange-200/30 rounded-full blur-3xl opacity-50" />
<div className="absolute top-1/3 -right-40 w-96 h-96 bg-purple-200/30 rounded-full blur-3xl opacity-40" />
</div>
{/* Toast Notification */}
<AnimatePresence>
{toastMessage && (
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
className="fixed top-20 left-1/2 -translate-x-1/2 z-50 bg-[#1E293B] text-white px-6 py-3.5 rounded-2xl shadow-2xl border border-slate-700 flex items-center gap-3 text-sm font-semibold"
>
<Sparkles className="w-4 h-4 text-[#F97316]" />
<span>{toastMessage}</span>
</motion.div>
)}
</AnimatePresence>
<div className="relative z-10">
{/* Header */}
<div className="bg-white border-b border-[#E5E7EB] py-10">
<div className="container-custom">
<div className="flex items-center gap-2 text-xs text-[#94A3B8] mb-3">
<Link to="/" className="hover:text-[#F97316]">Home</Link>
<ChevronRight className="w-3 h-3" />
<span className="text-[#F97316]">Developer Collab Hub</span>
</div>
<div className="flex flex-col md:flex-row md:items-center justify-between gap-6">
<div>
<div className="inline-flex items-center gap-2 px-3.5 py-1.5 rounded-full bg-orange-50 border border-orange-200 text-[#F97316] text-xs font-semibold mb-3">
<Code2 className="w-4 h-4" /> RIT Developers Environment
</div>
<h1 className="text-3xl md:text-4xl font-bold text-[#1E293B] mb-2" style={{ fontFamily: 'Playfair Display, serif' }}>
Find Project{' '}
<span style={{ background: 'linear-gradient(135deg, #F97316, #FB923C)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>
Collaborators
</span>
</h1>
<p className="text-[#475569] text-sm max-w-2xl" style={{ fontFamily: 'Inter, sans-serif' }}>
Post your open-source projects, search for co-developers, or find beta testers. Connect instantly via Telegram & Discord bot integrations!
</p>
</div>
<motion.button
whileHover={{ scale: 1.03 }}
whileTap={{ scale: 0.97 }}
onClick={() => setShowPostModal(true)}
className="flex items-center gap-2.5 px-6 py-3.5 rounded-2xl text-white font-semibold text-sm shadow-lg shadow-orange-500/25 shrink-0"
style={{ background: 'linear-gradient(135deg, #F97316, #FB923C)', fontFamily: 'Poppins, sans-serif' }}
>
<Plus className="w-4.5 h-4.5" />
Post Collaboration Request
</motion.button>
</div>
</div>
</div>
{/* Main Content Container */}
<div className="container-custom py-8 space-y-8">
{/* Telegram / Discord Bot Banner Callout */}
<div className="bg-gradient-to-r from-slate-900 via-slate-800 to-slate-900 rounded-3xl p-6 text-white border border-slate-700/80 shadow-xl flex flex-col md:flex-row items-start md:items-center justify-between gap-6 relative overflow-hidden">
<div className="flex items-start gap-4 relative z-10">
<div className="w-12 h-12 rounded-2xl bg-orange-500/20 border border-orange-500/30 flex items-center justify-center shrink-0">
<Send className="w-6 h-6 text-[#F97316]" />
</div>
<div>
<h3 className="text-lg font-bold text-white mb-1" style={{ fontFamily: 'Poppins, sans-serif' }}>
🤖 Post directly via Telegram or Discord Bot!
</h3>
<p className="text-xs text-slate-300 max-w-xl leading-relaxed" style={{ fontFamily: 'Inter, sans-serif' }}>
Send <code className="px-2 py-0.5 rounded bg-slate-800 text-orange-400 font-mono border border-slate-700">/collab</code> to our 24/7 Telegram bot or Discord bot to submit project requests directly from your chat!
</p>
</div>
</div>
<div className="flex items-center gap-3 shrink-0 relative z-10">
<a
href="https://t.me/Ritchatbot_bot"
target="_blank"
rel="noopener noreferrer"
className="px-4 py-2.5 rounded-xl bg-[#229ED9]/20 hover:bg-[#229ED9]/30 text-[#229ED9] border border-[#229ED9]/40 text-xs font-semibold flex items-center gap-2 transition-all"
>
Telegram Bot <ExternalLink className="w-3.5 h-3.5" />
</a>
</div>
</div>
{/* Search & Tag Filter Bar */}
<div className="bg-white rounded-3xl p-4 border border-[#E5E7EB] shadow-xs flex flex-col lg:flex-row gap-4 justify-between">
{/* Search Input */}
<div className="relative flex-1">
<Search className="absolute left-4 top-1/2 -translate-y-1/2 w-4.5 h-4.5 text-[#94A3B8]" />
<input
type="text"
placeholder="Search by idea, author, department, or tag..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="w-full pl-11 pr-4 py-3 rounded-2xl bg-[#F8FAFC] border-none text-sm text-[#1E293B] placeholder-[#94A3B8] focus:ring-2 focus:ring-[#F97316]/20 focus:outline-none transition-all"
style={{ fontFamily: 'Inter, sans-serif' }}
/>
</div>
{/* Tag Filter Pills */}
<div className="flex flex-wrap items-center gap-2">
{['All', ...TAG_OPTIONS].map((tag) => {
const isActive = activeTagFilter === tag;
return (
<button
key={tag}
onClick={() => setActiveTagFilter(tag)}
className={`px-3.5 py-2 rounded-xl text-xs font-semibold transition-all cursor-pointer border ${
isActive
? 'bg-[#1E293B] text-white border-[#1E293B] shadow-sm'
: 'bg-[#F8FAFC] text-[#475569] border-[#E5E7EB] hover:bg-orange-50 hover:text-[#F97316] hover:border-orange-200'
}`}
style={{ fontFamily: 'Inter, sans-serif' }}
>
{tag === 'All' ? 'All Tags' : tag.replace('looking for ', '')}
</button>
);
})}
</div>
</div>
{/* Cards Grid */}
{filteredRequests.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 pb-12">
{filteredRequests.map((item) => {
const tagStyle = getTagColor(item.tag);
return (
<motion.div
key={item.id}
whileHover={{ y: -4, boxShadow: '0 20px 40px -10px rgba(0,0,0,0.08)' }}
className="bg-white rounded-3xl border border-[#E8ECF4] p-6 flex flex-col justify-between relative shadow-xs transition-all"
>
<div>
{/* Top Bar: Tag Badge */}
<div className="flex items-start justify-between gap-2 mb-4">
<span className={`inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-[11px] font-semibold border ${tagStyle.bg}`}>
<span className={`w-1.5 h-1.5 rounded-full ${tagStyle.dot}`} />
{item.tag}
</span>
</div>
{/* Project Idea */}
<h3 className="text-base font-bold text-[#1E293B] leading-snug mb-3" style={{ fontFamily: 'Poppins, sans-serif' }}>
{item.projectIdea}
</h3>
</div>
<div className="mt-4 pt-4 border-t border-[#F1F5F9] space-y-4">
{/* GitHub link if present */}
{item.githubLink && (
<a
href={item.githubLink}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 text-xs font-semibold text-slate-700 hover:text-[#F97316] bg-slate-100 hover:bg-orange-50 px-3 py-1.5 rounded-xl border border-slate-200 hover:border-orange-200 transition-all w-fit"
>
<GitBranch className="w-3.5 h-3.5" />
View Repository
<ArrowUpRight className="w-3 h-3 text-slate-400" />
</a>
)}
{/* Author Info */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-2.5">
<div className="w-9 h-9 rounded-xl bg-orange-100 flex items-center justify-center text-[#F97316] font-bold text-xs">
{item.authorName.charAt(0)}
</div>
<div>
<div className="text-xs font-bold text-[#1E293B]">{item.authorName}</div>
<div className="text-[11px] text-[#64748B]">
{item.department} {item.year}
</div>
</div>
</div>
<div className="text-right">
<div className="text-[11px] font-semibold text-emerald-600 bg-emerald-50 px-2 py-0.5 rounded-full">
{item.applicationsCount} Requests
</div>
</div>
</div>
{/* Primary Action Button */}
<button
onClick={() => setSelectedCollab(item)}
className="w-full py-2.5 rounded-xl bg-[#FFF7ED] hover:bg-[#F97316] text-[#F97316] hover:text-white border border-[#FED7AA] hover:border-transparent text-xs font-semibold transition-all flex items-center justify-center gap-2 cursor-pointer shadow-xs"
style={{ fontFamily: 'Poppins, sans-serif' }}
>
<MessageSquare className="w-3.5 h-3.5" />
Send Collaboration Request
</button>
</div>
</motion.div>
);
})}
</div>
) : (
<div className="py-20 text-center flex flex-col items-center justify-center bg-white rounded-3xl border border-[#E8ECF4] shadow-xs">
<Code2 className="w-12 h-12 text-[#CBD5E1] mb-4" />
<h3 className="text-lg font-bold text-[#1E293B]" style={{ fontFamily: 'Poppins, sans-serif' }}>
No collaboration requests found
</h3>
<p className="text-[#64748B] text-xs mt-1 mb-6" style={{ fontFamily: 'Inter, sans-serif' }}>
Be the first developer to post a request or try adjusting your filters!
</p>
<button
onClick={() => setShowPostModal(true)}
className="px-5 py-2.5 rounded-xl bg-[#F97316] text-white text-xs font-semibold hover:bg-[#EA580C] transition-all"
>
Post First Collaboration Request
</button>
</div>
)}
</div>
</div>
{/* ─── POST COLLABORATION REQUEST MODAL ───────────────────────────────────── */}
<AnimatePresence>
{showPostModal && (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={() => setShowPostModal(false)}
className="absolute inset-0 bg-slate-900/60 backdrop-blur-xs"
/>
<motion.div
initial={{ opacity: 0, scale: 0.95, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.95, y: 20 }}
className="relative w-full max-w-xl bg-white rounded-3xl shadow-2xl p-6 sm:p-8 max-h-[90vh] overflow-y-auto z-10"
>
<div className="flex items-center justify-between mb-6">
<div>
<h2 className="text-xl font-bold text-[#1E293B]" style={{ fontFamily: 'Poppins, sans-serif' }}>
Post Collaboration Request
</h2>
<p className="text-xs text-[#64748B] mt-0.5">
Share your idea to find co-developers, beta testers, or contributors.
</p>
</div>
<button
onClick={() => setShowPostModal(false)}
className="p-2 rounded-full hover:bg-slate-100 text-slate-400 transition-colors"
>
<X className="w-5 h-5" />
</button>
</div>
<form onSubmit={handlePostRequest} className="space-y-4">
{/* 1. Name */}
<div>
<label className="block text-xs font-semibold text-[#1E293B] mb-1">
1) Your Name <span className="text-red-500">*</span>
</label>
<input
type="text"
required
placeholder="e.g., Priyan Sharma"
value={newAuthorName}
onChange={(e) => setNewAuthorName(e.target.value)}
className="w-full px-4 py-2.5 rounded-xl border border-[#E5E7EB] bg-[#F8FAFC] text-sm text-[#1E293B] focus:bg-white focus:border-[#F97316] focus:outline-none transition-all"
/>
</div>
{/* 2 & 3. Dept & Year */}
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-xs font-semibold text-[#1E293B] mb-1">
2) Dept <span className="text-red-500">*</span>
</label>
<select
value={newDept}
onChange={(e) => setNewDept(e.target.value)}
className="w-full px-3 py-2.5 rounded-xl border border-[#E5E7EB] bg-[#F8FAFC] text-sm text-[#1E293B] focus:bg-white focus:border-[#F97316] focus:outline-none transition-all cursor-pointer"
>
{DEPARTMENTS.map((d) => (
<option key={d} value={d}>{d}</option>
))}
</select>
</div>
<div>
<label className="block text-xs font-semibold text-[#1E293B] mb-1">
3) Year <span className="text-red-500">*</span>
</label>
<select
value={newYear}
onChange={(e) => setNewYear(e.target.value)}
className="w-full px-3 py-2.5 rounded-xl border border-[#E5E7EB] bg-[#F8FAFC] text-sm text-[#1E293B] focus:bg-white focus:border-[#F97316] focus:outline-none transition-all cursor-pointer"
>
{YEARS.map((y) => (
<option key={y} value={y}>{y}</option>
))}
</select>
</div>
</div>
{/* 4. Tag */}
<div>
<label className="block text-xs font-semibold text-[#1E293B] mb-1.5">
4) Choose a Tag <span className="text-red-500">*</span>
</label>
<div className="space-y-2">
{TAG_OPTIONS.map((tagOption) => {
const isSelected = newTag === tagOption;
return (
<div
key={tagOption}
onClick={() => setNewTag(tagOption)}
className={`p-3 rounded-xl border text-xs font-medium cursor-pointer transition-all flex items-center justify-between ${
isSelected
? 'border-[#F97316] bg-orange-50 text-[#F97316] font-semibold'
: 'border-[#E5E7EB] bg-[#F8FAFC] text-slate-700 hover:bg-slate-50'
}`}
>
<span>{tagOption}</span>
{isSelected && <CheckCircle2 className="w-4 h-4 text-[#F97316] shrink-0" />}
</div>
);
})}
</div>
</div>
{/* 5. Project Idea */}
<div>
<label className="block text-xs font-semibold text-[#1E293B] mb-1">
5) Project Idea <span className="text-red-500">*</span>
</label>
<textarea
required
rows={3}
placeholder="Describe your project idea, stack, and what role you need..."
value={newIdea}
onChange={(e) => setNewIdea(e.target.value)}
className="w-full px-4 py-2.5 rounded-xl border border-[#E5E7EB] bg-[#F8FAFC] text-sm text-[#1E293B] focus:bg-white focus:border-[#F97316] focus:outline-none transition-all"
/>
</div>
{/* Optional GitHub & Contact */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="block text-xs font-semibold text-[#1E293B] mb-1">
GitHub Link <span className="text-slate-400 font-normal">(Optional)</span>
</label>
<input
type="url"
placeholder="https://github.com/username/repo"
value={newGithub}
onChange={(e) => setNewGithub(e.target.value)}
className="w-full px-4 py-2.5 rounded-xl border border-[#E5E7EB] bg-[#F8FAFC] text-sm text-[#1E293B] focus:bg-white focus:border-[#F97316] focus:outline-none transition-all"
/>
</div>
<div>
<label className="block text-xs font-semibold text-[#1E293B] mb-1">
Telegram Username / Contact <span className="text-slate-400 font-normal">(Optional)</span>
</label>
<input
type="text"
placeholder="@username or phone"
value={newContact}
onChange={(e) => setNewContact(e.target.value)}
className="w-full px-4 py-2.5 rounded-xl border border-[#E5E7EB] bg-[#F8FAFC] text-sm text-[#1E293B] focus:bg-white focus:border-[#F97316] focus:outline-none transition-all"
/>
</div>
</div>
<div className="pt-4 flex justify-end gap-3">
<button
type="button"
onClick={() => setShowPostModal(false)}
className="px-5 py-2.5 rounded-xl text-xs font-semibold text-slate-600 bg-slate-100 hover:bg-slate-200 transition-colors"
>
Cancel
</button>
<button
type="submit"
disabled={submittingPost}
className="px-6 py-2.5 rounded-xl text-xs font-semibold text-white bg-gradient-to-r from-[#F97316] to-[#FB923C] hover:brightness-105 transition-all shadow-md disabled:opacity-50"
>
{submittingPost ? 'Posting...' : 'Post Request'}
</button>
</div>
</form>
</motion.div>
</div>
)}
</AnimatePresence>
{/* ─── APPLY / COLLABORATE MODAL ─────────────────────────────────────────── */}
<AnimatePresence>
{selectedCollab && (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={() => setSelectedCollab(null)}
className="absolute inset-0 bg-slate-900/60 backdrop-blur-xs"
/>
<motion.div
initial={{ opacity: 0, scale: 0.95, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.95, y: 20 }}
className="relative w-full max-w-lg bg-white rounded-3xl shadow-2xl p-6 sm:p-8 z-10"
>
<div className="flex items-center justify-between mb-4">
<div>
<span className="text-[10px] uppercase font-bold tracking-wider text-[#F97316]">
Collaborate Request
</span>
<h2 className="text-lg font-bold text-[#1E293B]" style={{ fontFamily: 'Poppins, sans-serif' }}>
Join {selectedCollab.authorName}'s Project
</h2>
</div>
<button
onClick={() => setSelectedCollab(null)}
className="p-2 rounded-full hover:bg-slate-100 text-slate-400 transition-colors"
>
<X className="w-5 h-5" />
</button>
</div>
<div className="p-3.5 bg-slate-50 rounded-2xl border border-slate-200 mb-5 text-xs text-slate-700 leading-relaxed">
<strong>Project Idea:</strong> {selectedCollab.projectIdea}
</div>
<form onSubmit={handleApply} className="space-y-4">
<div>
<label className="block text-xs font-semibold text-[#1E293B] mb-1">Your Name *</label>
<input
type="text"
required
placeholder="Enter your name"
value={appApplicantName}
onChange={(e) => setAppApplicantName(e.target.value)}
className="w-full px-4 py-2.5 rounded-xl border border-[#E5E7EB] bg-[#F8FAFC] text-sm text-[#1E293B] focus:bg-white focus:border-[#F97316] focus:outline-none transition-all"
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-xs font-semibold text-[#1E293B] mb-1">Dept *</label>
<select
value={appDept}
onChange={(e) => setAppDept(e.target.value)}
className="w-full px-3 py-2.5 rounded-xl border border-[#E5E7EB] bg-[#F8FAFC] text-sm text-[#1E293B] focus:bg-white focus:border-[#F97316] focus:outline-none transition-all cursor-pointer"
>
{DEPARTMENTS.map((d) => (
<option key={d} value={d}>{d}</option>
))}
</select>
</div>
<div>
<label className="block text-xs font-semibold text-[#1E293B] mb-1">Year *</label>
<select
value={appYear}
onChange={(e) => setAppYear(e.target.value)}
className="w-full px-3 py-2.5 rounded-xl border border-[#E5E7EB] bg-[#F8FAFC] text-sm text-[#1E293B] focus:bg-white focus:border-[#F97316] focus:outline-none transition-all cursor-pointer"
>
{YEARS.map((y) => (
<option key={y} value={y}>{y}</option>
))}
</select>
</div>
</div>
<div>
<label className="block text-xs font-semibold text-[#1E293B] mb-1">
Your Contact Info (Telegram @username / Phone / Email) *
</label>
<input
type="text"
required
placeholder="@username or phone number"
value={appContact}
onChange={(e) => setAppContact(e.target.value)}
className="w-full px-4 py-2.5 rounded-xl border border-[#E5E7EB] bg-[#F8FAFC] text-sm text-[#1E293B] focus:bg-white focus:border-[#F97316] focus:outline-none transition-all"
/>
</div>
<div>
<label className="block text-xs font-semibold text-[#1E293B] mb-1">Message / Experience (Optional)</label>
<textarea
rows={2}
placeholder="Briefly state your skills or why you want to contribute..."
value={appMessage}
onChange={(e) => setAppMessage(e.target.value)}
className="w-full px-4 py-2.5 rounded-xl border border-[#E5E7EB] bg-[#F8FAFC] text-sm text-[#1E293B] focus:bg-white focus:border-[#F97316] focus:outline-none transition-all"
/>
</div>
<div className="pt-4 flex justify-end gap-3">
<button
type="button"
onClick={() => setSelectedCollab(null)}
className="px-5 py-2.5 rounded-xl text-xs font-semibold text-slate-600 bg-slate-100 hover:bg-slate-200 transition-colors"
>
Cancel
</button>
<button
type="submit"
disabled={submittingApp}
className="px-6 py-2.5 rounded-xl text-xs font-semibold text-white bg-gradient-to-r from-[#F97316] to-[#FB923C] hover:brightness-105 transition-all shadow-md disabled:opacity-50 flex items-center gap-2"
>
<Send className="w-3.5 h-3.5" />
{submittingApp ? 'Sending...' : 'Send Request'}
</button>
</div>
</form>
</motion.div>
</div>
)}
</AnimatePresence>
</div>
);
}