Spaces:
Sleeping
Sleeping
| import React, { useState } from 'react'; | |
| export default function InviteModal({ server, onClose, token }) { | |
| const [inviteCode, setInviteCode] = useState(''); | |
| const [loading, setLoading] = useState(false); | |
| const [copied, setCopied] = useState(false); | |
| const [error, setError] = useState(''); | |
| const generateInvite = async () => { | |
| setLoading(true); | |
| setError(''); | |
| try { | |
| const res = await fetch(`/api/invites/servers/${server.id}/invites`, { | |
| method: 'POST', | |
| headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, | |
| }); | |
| if (!res.ok) throw new Error('Failed to generate invite'); | |
| const data = await res.json(); | |
| setInviteCode(data.code); | |
| } catch (err) { | |
| setError(err.message); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const inviteLink = inviteCode ? `${window.location.origin}/invite/${inviteCode}` : ''; | |
| const copyToClipboard = async () => { | |
| try { | |
| await navigator.clipboard.writeText(inviteLink); | |
| setCopied(true); | |
| setTimeout(() => setCopied(false), 2000); | |
| } catch { | |
| // Fallback | |
| const ta = document.createElement('textarea'); | |
| ta.value = inviteLink; | |
| document.body.appendChild(ta); | |
| ta.select(); | |
| document.execCommand('copy'); | |
| document.body.removeChild(ta); | |
| setCopied(true); | |
| setTimeout(() => setCopied(false), 2000); | |
| } | |
| }; | |
| return ( | |
| <div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50" onClick={onClose}> | |
| <div className="bg-[#1e1e22] rounded-xl p-6 max-w-md w-full mx-4 shadow-2xl" onClick={e => e.stopPropagation()}> | |
| <h2 className="text-lg font-bold text-gray-100 mb-1"> | |
| Invite Friends to <span className="text-[#FFD700]">{server?.name}</span> | |
| </h2> | |
| <p className="text-gray-500 text-sm mb-6">Share this link to let others join your server</p> | |
| {!inviteCode ? ( | |
| <button | |
| onClick={generateInvite} | |
| disabled={loading} | |
| className="w-full py-2.5 bg-[#FFD700] text-black font-semibold rounded-lg hover:bg-yellow-400 disabled:opacity-50 transition-colors duration-200" | |
| > | |
| {loading ? 'Generating…' : 'Generate Invite Link'} | |
| </button> | |
| ) : ( | |
| <div className="space-y-3"> | |
| <div className="flex items-center gap-2"> | |
| <input | |
| type="text" | |
| readOnly | |
| value={inviteLink} | |
| className="flex-1 bg-[#111114] border border-[#3a3a42] rounded-lg px-4 py-2.5 text-gray-100 text-sm outline-none" | |
| /> | |
| <button | |
| onClick={copyToClipboard} | |
| className={`px-4 py-2.5 rounded-lg font-semibold text-sm transition-colors duration-200 flex-shrink-0 ${ | |
| copied | |
| ? 'bg-green-600 text-white' | |
| : 'bg-[#FFD700] text-black hover:bg-yellow-400' | |
| }`} | |
| > | |
| {copied ? 'Copied!' : 'Copy'} | |
| </button> | |
| </div> | |
| </div> | |
| )} | |
| {error && <p className="text-red-400 text-sm mt-3">{error}</p>} | |
| <button | |
| onClick={onClose} | |
| className="mt-4 w-full py-2 text-gray-400 hover:text-gray-200 text-sm transition-colors duration-200" | |
| > | |
| Close | |
| </button> | |
| </div> | |
| </div> | |
| ); | |
| } | |