Spaces:
Sleeping
Sleeping
File size: 5,143 Bytes
0a8fe79 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | import React, { useState, useRef } from 'react';
export default function CreateServerModal({ onClose, onCreated, token }) {
const [name, setName] = useState('');
const [icon, setIcon] = useState(null);
const [iconPreview, setIconPreview] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const fileRef = useRef(null);
const handleIconChange = (e) => {
const file = e.target.files?.[0];
if (file) {
setIcon(file);
const reader = new FileReader();
reader.onloadend = () => setIconPreview(reader.result);
reader.readAsDataURL(file);
}
};
const handleCreate = async (e) => {
e.preventDefault();
if (!name.trim()) { setError('Server name is required'); return; }
setLoading(true);
setError('');
try {
// Upload icon first if provided
let iconUrl = null;
if (icon) {
const form = new FormData();
form.append('file', icon);
const uploadRes = await fetch('/api/upload', {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}` },
body: form,
});
if (uploadRes.ok) {
const uploadData = await uploadRes.json();
iconUrl = uploadData.url;
}
}
const body = { name: name.trim() };
if (iconUrl) body.icon = iconUrl;
const res = await fetch('/api/servers', {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
if (!res.ok) {
const data = await res.json().catch(() => ({}));
throw new Error(data.error || 'Failed to create server');
}
const data = await res.json();
onCreated(data.server || data);
onClose();
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
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-xl font-bold text-[#FFD700] mb-6 text-center">Create Your Server</h2>
<form onSubmit={handleCreate}>
{/* Icon upload */}
<div className="flex justify-center mb-6">
<button
type="button"
onClick={() => fileRef.current?.click()}
className="w-20 h-20 rounded-full bg-[#25252a] border-2 border-dashed border-[#3a3a42] hover:border-[#FFD700]/50 flex items-center justify-center transition-colors duration-200 overflow-hidden"
>
{iconPreview ? (
<img src={iconPreview} alt="" className="w-full h-full object-cover" />
) : (
<div className="text-center">
<svg className="w-6 h-6 text-gray-500 mx-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 13a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
<span className="text-[10px] text-gray-500">Icon</span>
</div>
)}
</button>
<input ref={fileRef} type="file" accept="image/*" className="hidden" onChange={handleIconChange} />
</div>
{/* Server name */}
<div className="mb-4">
<label className="block text-sm font-medium text-gray-300 mb-1.5">Server Name</label>
<input
type="text"
value={name}
onChange={e => setName(e.target.value)}
placeholder="My Awesome Server"
className="w-full bg-[#111114] border border-[#3a3a42] rounded-lg px-4 py-2.5 text-gray-100 placeholder-gray-500 outline-none focus:border-[#FFD700]/50 transition-colors duration-200"
autoFocus
maxLength={100}
/>
</div>
{error && <p className="text-red-400 text-sm mb-4">{error}</p>}
{/* Buttons */}
<div className="flex gap-3 justify-end">
<button
type="button"
onClick={onClose}
className="px-4 py-2 text-gray-400 hover:text-gray-200 transition-colors duration-200 rounded-lg"
>
Cancel
</button>
<button
type="submit"
disabled={loading || !name.trim()}
className="px-6 py-2 bg-[#FFD700] text-black font-semibold rounded-lg hover:bg-yellow-400 disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200"
>
{loading ? 'Creating…' : 'Create'}
</button>
</div>
</form>
</div>
</div>
);
}
|