/* global React, ReactDOM */
const { useEffect, useRef, useState, useMemo } = React;
const I = {
arrowLeft: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'m15 18-6-6 6-6'})),
arrowUp: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'m18 15-6-6-6 6'})),
arrowDown: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'m6 9 6 6 6-6'})),
ban: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('circle',{cx:12,cy:12,r:10}),React.createElement('path',{d:'m4.9 4.9 14.2 14.2'})),
bell: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9'}),React.createElement('path',{d:'M10.3 21a1.94 1.94 0 0 0 3.4 0'})),
bellOff: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M8.7 3A6 6 0 0 1 18 8a21.3 21.3 0 0 0 .6 5'}),React.createElement('path',{d:'M12 12H3'}),React.createElement('path',{d:'m1 1 2 2'}),React.createElement('path',{d:'M20.56 15H3'}),React.createElement('path',{d:'m16 19 2 2'}),React.createElement('path',{d:'m17 21 .5-1.5M17.5 4 .5 2.5M1 15h9'})),
bot: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M12 8V4H8'}),React.createElement('rect',{width:16,height:12,x:4,y:8,rx:2}),React.createElement('path',{d:'M2 14h2'}),React.createElement('path',{d:'M20 14h2'}),React.createElement('path',{d:'M15 13v2'}),React.createElement('path',{d:'M9 13v2'})),
check: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M20 6 9 17l-5-5'})),
chevronDown: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'m6 9 6 6 6-6'})),
clipboardList: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('rect',{width:8,height:4,x:8,y:2,rx:1,ry:1}),React.createElement('path',{d:'M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2'}),React.createElement('path',{d:'M12 11h4'}),React.createElement('path',{d:'M12 16h4'}),React.createElement('path',{d:'M8 11h.01'}),React.createElement('path',{d:'M8 16h.01'})),
flame: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z'})),
globe: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('circle',{cx:12,cy:12,r:10}),React.createElement('path',{d:'M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20'}),React.createElement('path',{d:'M2 12h20'})),
headphones: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M3 14h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-7a9 9 0 0 1 18 0v7a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3'})),
image: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('rect',{width:18,height:18,x:3,y:3,rx:2,ry:2}),React.createElement('circle',{cx:9,cy:9,r:2}),React.createElement('path',{d:'m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21'})),
layout: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('rect',{width:18,height:18,x:3,y:3,rx:2}),React.createElement('path',{d:'M3 9h18'}),React.createElement('path',{d:'M9 21V9'})),
listMusic: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M21 15V6'}),React.createElement('path',{d:'M18.5 18a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5Z'}),React.createElement('path',{d:'M12 12H3'}),React.createElement('path',{d:'M16 6H3'}),React.createElement('path',{d:'M12 18H3'})),
lock: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('rect',{width:18,height:11,x:3,y:11,rx:2,ry:2}),React.createElement('path',{d:'M7 11V7a5 5 0 0 1 10 0v4'})),
logOut: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4'}),React.createElement('polyline',{points:'16,17 21,12 16,7'}),React.createElement('line',{x1:21,x2:9,y1:12,y2:12})),
menu: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('line',{x1:4,x2:20,y1:12,y2:12}),React.createElement('line',{x1:4,x2:20,y1:6,y2:6}),React.createElement('line',{x1:4,x2:20,y1:18,y2:18})),
messageCircle: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M7.9 20A9 9 0 1 0 4 16.1L2 22Z'})),
mic: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z'}),React.createElement('path',{d:'M19 10v2a7 7 0 0 1-14 0v-2'}),React.createElement('line',{x1:12,x2:12,y1:19,y2:22})),
moreH: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('circle',{cx:12,cy:12,r:1}),React.createElement('circle',{cx:19,cy:12,r:1}),React.createElement('circle',{cx:5,cy:12,r:1})),
paperclip: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48'})),
phone: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.127.96.361 1.903.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0 1 22 16.92z'})),
pin: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('line',{x1:12,x2:12,y1:17,y2:22}),React.createElement('path',{d:'M5 17h14v-1.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V6h1a2 2 0 0 0 0-4H8a2 2 0 0 0 0 4h1v4.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24Z'})),
plus: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M5 12h14'}),React.createElement('path',{d:'M12 5v14'})),
radio: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M4.9 19.1C1 15.2 1 8.8 4.9 4.9'}),React.createElement('path',{d:'M7.8 16.2c-2.3-2.3-2.3-6.1 0-8.5'}),React.createElement('circle',{cx:12,cy:12,r:2}),React.createElement('path',{d:'M16.2 7.8c2.3 2.3 2.3 6.1 0 8.5'}),React.createElement('path',{d:'M19.1 4.9C23 8.8 23 15.1 19.1 19'})),
search: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('circle',{cx:11,cy:11,r:8}),React.createElement('path',{d:'m21 21-4.3-4.3'})),
send: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'m22 2-7 20-4-9-9-4Z'}),React.createElement('path',{d:'M22 2 11 13'})),
settings: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z'}),React.createElement('circle',{cx:12,cy:12,r:3})),
smile: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('circle',{cx:12,cy:12,r:10}),React.createElement('path',{d:'M8 14s1.5 2 4 2 4-2 4-2'}),React.createElement('line',{x1:9,x2:9.01,y1:9,y2:9}),React.createElement('line',{x1:15,x2:15.01,y1:9,y2:9})),
userRound: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M18 21a8 8 0 0 0-16 0'}),React.createElement('circle',{cx:10,cy:8,r:5})),
userRoundPlus: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2'}),React.createElement('circle',{cx:9,cy:7,r:4}),React.createElement('line',{x1:19,x2:19,y1:8,y2:14}),React.createElement('line',{x1:22,x2:16,y1:11,y2:11})),
users: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2'}),React.createElement('circle',{cx:9,cy:7,r:4}),React.createElement('path',{d:'M22 21v-2a4 4 0 0 0-3-3.87'}),React.createElement('path',{d:'M16 3.13a4 4 0 0 1 0 7.75'})),
volume2: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('polygon',{points:'11,5 6,9 2,9 2,15 6,15 11,19'}),React.createElement('path',{d:'M15.54 8.46a5 5 0 0 1 0 7.07'}),React.createElement('path',{d:'M19.07 4.93a10 10 0 0 1 0 14.14'})),
volumeX: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('polygon',{points:'11,5 6,9 2,9 2,15 6,15 11,19'}),React.createElement('line',{x1:22,x2:16,y1:9,y2:15}),React.createElement('line',{x1:16,x2:22,y1:9,y2:15})),
video: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5'}),React.createElement('rect',{x:2,y:6,width:14,height:12,rx:2})),
warning: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z'}),React.createElement('path',{d:'M12 9v4'}),React.createElement('path',{d:'M12 17h.01'})),
x: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M18 6 6 18'}),React.createElement('path',{d:'m6 6 12 12'})),
};
const API_URL = "/api.php";
async function apiRequest(action, options = {}) {
const { method = "GET", body, query = {} } = options;
const params = new URLSearchParams({ action, ...query });
const response = await fetch(`${API_URL}?${params.toString()}`, {
method,
headers: body instanceof FormData
? { Accept: "application/json" }
: body
? { "Content-Type": "application/json", Accept: "application/json" }
: { Accept: "application/json" },
credentials: "include",
body: body instanceof FormData ? body : body ? JSON.stringify(body) : undefined,
});
const payload = await response.json().catch(() => ({}));
if (response.status === 503 && action === "me") return { ok: true, user: null, backendUnavailable: true };
if (!response.ok || payload.ok === false) {
const error = new Error(payload.message || payload.error || "İşlem tamamlanamadı.");
error.code = payload.error;
throw error;
}
return payload;
}
function Avatar({ person, size = "md", showStatus = false }) {
const src = person.avatar_url || person.avatar;
const isGif = src && src.toLowerCase().endsWith('.gif');
return (
{src ?
: {(person.display_name || person.name || '??').slice(0, 2).toUpperCase()}}
{showStatus && }
);
}
function RoleBadge({ role }) {
const config = {
root: { label: 'ROOT', color: '#ff4444', bg: 'rgba(255,68,68,.15)' },
super_admin: { label: 'SÜPER ADMİN', color: '#ff6b00', bg: 'rgba(255,107,0,.15)' },
admin: { label: 'ADMİN', color: '#e74c3c', bg: 'rgba(231,76,60,.15)' },
moderator: { label: 'MOD', color: '#3498db', bg: 'rgba(52,152,219,.15)' },
};
const c = config[role];
if (!c) return null;
return {c.label};
}
function getRoleColor(role) {
return { root: '#ff4444', super_admin: '#ff6b00', admin: '#e74c3c', moderator: '#3498db', user: '#2ecc71' }[role] || '#95a5a6';
}
function Modal({ children, title, onClose, wide = false }) {
return (
event.stopPropagation()}>
{title}
{children}
);
}
function App() {
const [activeTab, setActiveTab] = useState("chats");
const [selectedId, setSelectedId] = useState(null);
const [search, setSearch] = useState("");
const [messages, setMessages] = useState({});
const [draft, setDraft] = useState("");
const [showSearch, setShowSearch] = useState(false);
const [showNotifications, setShowNotifications] = useState(false);
const [showAdmin, setShowAdmin] = useState(false);
const [showAccount, setShowAccount] = useState(false);
const [showSettings, setShowSettings] = useState(false);
const [liveUser, setLiveUser] = useState(null);
const [authMode, setAuthMode] = useState("login");
const [authForm, setAuthForm] = useState({ identity: "", password: "", display_name: "", username: "", email: "", gender: "other" });
const [authError, setAuthError] = useState("");
const [callType, setCallType] = useState(null);
const [mobileSidebar, setMobileSidebar] = useState(false);
const [toast, setToast] = useState("");
const [roomFilter, setRoomFilter] = useState("Tümü");
const [conversations, setConversations] = useState([]);
const [rooms, setRooms] = useState([]);
const [onlineUsers, setOnlineUsers] = useState([]);
const [adminTab, setAdminTab] = useState("dashboard");
const [adminStats, setAdminStats] = useState(null);
const [adminUsers, setAdminUsers] = useState([]);
const [adminRooms, setAdminRooms] = useState([]);
const [adminLogs, setAdminLogs] = useState([]);
const [adminReports, setAdminReports] = useState([]);
const [adminSettings, setAdminSettings] = useState({});
const [adminBots, setAdminBots] = useState([]);
const [adminRadio, setAdminRadio] = useState([]);
const [adminAnnouncements, setAdminAnnouncements] = useState([]);
const [adminIpBans, setAdminIpBans] = useState([]);
const [adminSearch, setAdminSearch] = useState("");
const [adminRoleFilter, setAdminRoleFilter] = useState("");
const [showBotModal, setShowBotModal] = useState(false);
const [showRadioModal, setShowRadioModal] = useState(false);
const [showAnnouncementModal, setShowAnnouncementModal] = useState(false);
const [showVoiceModal, setShowVoiceModal] = useState(false);
const [selectedRoom, setSelectedRoom] = useState(null);
const [voiceChannels, setVoiceChannels] = useState([]);
const [currentVoiceChannel, setCurrentVoiceChannel] = useState(null);
const [myBots, setMyBots] = useState([]);
const [radioStations, setRadioStations] = useState([]);
const [announcements, setAnnouncements] = useState([]);
const [selectedUser, setSelectedUser] = useState(null);
const [showProfileModal, setShowProfileModal] = useState(false);
const [showAvatarUpload, setShowAvatarUpload] = useState(false);
const [avatarPreview, setAvatarPreview] = useState(null);
const [chatClosed, setChatClosed] = useState(false);
const [botForm, setBotForm] = useState({ bot_name: "", username: "", description: "" });
const [autoReplyForm, setAutoReplyForm] = useState({ trigger_keyword: "", reply_text: "" });
const [selectedBot, setSelectedBot] = useState(null);
const [radioForm, setRadioForm] = useState({ name: "", stream_url: "", genre: "" });
const [announcementForm, setAnnouncementForm] = useState({ title: "", body: "", priority: "normal" });
const [voiceForm, setVoiceForm] = useState({ name: "", channel_type: "voice" });
const [userSearch, setUserSearch] = useState("");
const [muted, setMuted] = useState(false);
const [blocked, setBlocked] = useState(false);
const [followed, setFollowed] = useState(false);
const selected = conversations.find((c) => c.user_id === selectedId || c.id === selectedId);
const selectedMessages = messages[selected?.id || selectedId] || [];
const messagesEndRef = useRef(null);
useEffect(() => {
apiRequest("me").then((r) => {
if (r.user) {
setLiveUser(r.user);
loadConversations();
loadRooms();
loadOnlineUsers();
}
}).catch(() => {});
}, []);
useEffect(() => {
if (!liveUser) return;
loadConversations();
loadRooms();
loadOnlineUsers();
const interval = setInterval(() => {
apiRequest("heartbeat").catch(() => {});
loadOnlineUsers();
loadConversations();
}, 30000);
return () => clearInterval(interval);
}, [liveUser?.id]);
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
}, [selectedMessages.length]);
function notify(message) { setToast(message); window.setTimeout(() => setToast(""), 2200); }
async function loadConversations() {
try {
const r = await apiRequest("conversations");
setConversations(r.items || []);
} catch {}
}
async function loadRooms() {
try {
const r = await apiRequest("rooms");
setRooms(r.items || []);
} catch {}
}
async function loadOnlineUsers() {
try {
const r = await apiRequest("online_users");
setOnlineUsers(r.items || []);
} catch {}
}
async function loadMessages(convId) {
try {
const r = await apiRequest("messages", { query: { conversation_id: convId } });
setMessages((prev) => ({ ...prev, [convId]: r.items || [] }));
} catch {}
}
async function loadAdminData(tab) {
try {
if (tab === "dashboard") {
const r = await apiRequest("admin_stats");
setAdminStats(r.stats);
} else if (tab === "users") {
const r = await apiRequest("admin_users", { query: { search: adminSearch, role: adminRoleFilter } });
setAdminUsers(r.items || []);
} else if (tab === "rooms") {
const r = await apiRequest("admin_rooms");
setAdminRooms(r.items || []);
} else if (tab === "logs") {
const r = await apiRequest("admin_logs");
setAdminLogs(r.items || []);
} else if (tab === "reports") {
const r = await apiRequest("admin_reports");
setAdminReports(r.items || []);
} else if (tab === "settings") {
const r = await apiRequest("admin_settings");
setAdminSettings(r.settings || {});
} else if (tab === "bots") {
const r = await apiRequest("admin_bots");
setAdminBots(r.items || []);
} else if (tab === "radio") {
const r = await apiRequest("admin_radio_stations");
setAdminRadio(r.items || []);
} else if (tab === "announcements") {
const r = await apiRequest("admin_announcements");
setAdminAnnouncements(r.items || []);
} else if (tab === "ipbans") {
const r = await apiRequest("admin_ip_bans");
setAdminIpBans(r.items || []);
}
} catch (e) { notify("Hata: " + e.message); }
}
async function loadAnnouncements() {
try {
const r = await apiRequest("announcements");
setAnnouncements(r.items || []);
} catch {}
}
async function loadMyBots() {
try {
const r = await apiRequest("my_bots");
setMyBots(r.items || []);
} catch {}
}
async function loadRadioStations() {
try {
const r = await apiRequest("radio_stations");
setRadioStations(r.items || []);
} catch {}
}
function selectConversation(id) {
setSelectedId(id);
setActiveTab("chats");
setMobileSidebar(false);
const conv = conversations.find((c) => c.id === id || c.user_id === id);
if (conv) {
loadMessages(conv.id);
setChatClosed(!!conv.is_closed);
}
}
async function sendMessage(event) {
event?.preventDefault();
const text = draft.trim();
if (!text || !selected) return;
try {
const r = await apiRequest("send_message", { method: "POST", body: { conversation_id: selected.id, body: text } });
setMessages((prev) => ({
...prev,
[selected.id]: [...(prev[selected.id] || []), { id: r.id, sender_id: liveUser.id, body: text, message_type: "text", color: getRoleColor(liveUser.role), created_at: new Date().toISOString(), display_name: liveUser.display_name, username: liveUser.username }],
}));
setDraft("");
} catch (e) { notify("Mesaj gönderilemedi: " + e.message); }
}
function startCall(type) { setCallType(type); notify(`${type === "voice" ? "Sesli" : "Görüntülü"} arama başlatılıyor`); }
async function submitAuth(event) {
event.preventDefault();
setAuthError("");
try {
const result = await apiRequest(authMode, {
method: "POST",
body: authMode === "login" ? { identity: authForm.identity, password: authForm.password } : authForm,
});
setLiveUser(result.user);
setShowAccount(false);
notify(authMode === "login" ? "Giriş yapıldı" : "Hesabın oluşturuldu");
loadConversations();
loadRooms();
} catch (error) { setAuthError(error.message); }
}
async function submitGoogleLogin() {
try {
const result = await apiRequest("google_login", { method: "POST", body: { google_id: "demo_" + Date.now(), email: "demo@novatalk.app", display_name: "Google Kullanıcı", avatar_url: "" } });
setLiveUser(result.user);
setShowAccount(false);
notify("Google ile giriş yapıldı");
loadConversations();
loadRooms();
} catch (error) { setAuthError(error.message); }
}
async function handleAvatarUpload(event) {
const file = event.target.files[0];
if (!file) return;
const formData = new FormData();
formData.append("avatar", file);
try {
const response = await fetch("/api.php?action=upload_avatar", { method: "POST", body: formData, credentials: "include" });
const result = await response.json();
if (result.ok) {
setLiveUser((prev) => ({ ...prev, avatar_url: result.avatar_url }));
notify("Avatar güncellendi");
setShowAvatarUpload(false);
} else { notify("Yükleme başarısız: " + (result.error || "")); }
} catch { notify("Yükleme hatası"); }
}
async function openAdminPanel() {
setShowAdmin(true);
setAdminTab("dashboard");
loadAdminData("dashboard");
}
async function switchAdminTab(tab) {
setAdminTab(tab);
loadAdminData(tab);
}
async function updateAdminSetting(key, value) {
try {
await apiRequest("admin_settings", { method: "POST", body: { [key]: value } });
setAdminSettings((prev) => ({ ...prev, [key]: value }));
notify("Ayar güncellendi");
} catch (e) { notify("Hata: " + e.message); }
}
async function createBot() {
try {
await apiRequest("create_bot", { method: "POST", body: botForm });
notify("Bot oluşturuldu");
setShowBotModal(false);
setBotForm({ bot_name: "", username: "", description: "" });
loadAdminData("bots");
} catch (e) { notify("Hata: " + e.message); }
}
async function addAutoReply(botId) {
try {
await apiRequest("add_auto_reply", { method: "POST", body: { bot_id: botId, ...autoReplyForm } });
notify("Otomatik cevap eklendi");
setAutoReplyForm({ trigger_keyword: "", reply_text: "" });
} catch (e) { notify("Hata: " + e.message); }
}
async function addRadioStation() {
try {
await apiRequest("add_radio_station", { method: "POST", body: radioForm });
notify("Radyo istasyonu eklendi");
setShowRadioModal(false);
setRadioForm({ name: "", stream_url: "", genre: "" });
} catch (e) { notify("Hata: " + e.message); }
}
async function createAnnouncement() {
try {
await apiRequest("admin_announcements", { method: "POST", body: announcementForm });
notify("Duyuru oluşturuldu");
setShowAnnouncementModal(false);
setAnnouncementForm({ title: "", body: "", priority: "normal" });
loadAdminData("announcements");
} catch (e) { notify("Hata: " + e.message); }
}
async function createVoiceChannel() {
if (!selectedRoom) return;
try {
await apiRequest("create_voice_channel", { method: "POST", body: { room_id: selectedRoom.id, ...voiceForm } });
notify("Sesli kanal oluşturuldu");
setShowVoiceModal(false);
setVoiceForm({ name: "", channel_type: "voice" });
loadVoiceChannels(selectedRoom.id);
} catch (e) { notify("Hata: " + e.message); }
}
async function loadVoiceChannels(roomId) {
try {
const r = await apiRequest("voice_channels", { query: { room_id: roomId } });
setVoiceChannels(r.items || []);
} catch {}
}
async function joinVoiceChannel(channelId) {
try {
await apiRequest("join_voice_channel", { method: "POST", body: { channel_id: channelId } });
setCurrentVoiceChannel(channelId);
notify("Sesli kanala katıldın");
} catch (e) { notify("Hata: " + e.message); }
}
async function leaveVoiceChannel() {
try {
await apiRequest("leave_voice_channel", { method: "POST" });
setCurrentVoiceChannel(null);
notify("Sesli kanaldan ayrıldın");
} catch {}
}
async function updateUserRole(userId, role) {
try {
await apiRequest("admin_update_user", { method: "POST", body: { user_id: userId, role } });
notify("Kullanıcı rolü güncellendi");
loadAdminData("users");
} catch (e) { notify("Hata: " + e.message); }
}
async function toggleUserBan(userId, ban) {
try {
await apiRequest("admin_update_user", { method: "POST", body: { user_id: userId, is_banned: ban } });
notify(ban ? "Kullanıcı engellendi" : "Engel kaldırıldı");
loadAdminData("users");
} catch (e) { notify("Hata: " + e.message); }
}
async function toggleUserActive(userId, active) {
try {
await apiRequest("admin_update_user", { method: "POST", body: { user_id: userId, is_active: active } });
notify(active ? "Kullanıcı aktif edildi" : "Kullanıcı deaktif edildi");
loadAdminData("users");
} catch (e) { notify("Hata: " + e.message); }
}
async function warnUser(userId, reason, severity) {
try {
await apiRequest("admin_warn_user", { method: "POST", body: { user_id: userId, reason, severity } });
notify("Kullanıcı uyarıldı");
} catch (e) { notify("Hata: " + e.message); }
}
function renderAdminPanel() {
if (!liveUser || !["admin", "super_admin", "root", "moderator"].includes(liveUser.role)) return null;
const isRoot = liveUser.role === "root";
const isSuperAdmin = ["super_admin", "root"].includes(liveUser.role);
const tabs = [
{ id: "dashboard", label: "Gösterge Paneli", icon: I.layout(15) },
{ id: "users", label: "Kullanıcılar", icon: I.users(15) },
{ id: "rooms", label: "Odalar", icon: I.radio(15) },
{ id: "bots", label: "Botlar", icon: I.bot(15) },
{ id: "radio", label: "Radyo", icon: I.headphones(15) },
{ id: "reports", label: "Raporlar", icon: I.warning(15) },
{ id: "logs", label: "Loglar", icon: I.clipboardList(15) },
];
if (isSuperAdmin) tabs.push({ id: "announcements", label: "Duyurular", icon: I.bell(15) });
if (isRoot) tabs.push({ id: "settings", label: "Ayarlar", icon: I.settings(15) }, { id: "ipbans", label: "IP Ban", icon: I.ban(15) });
return (
setShowAdmin(false)}>
{tabs.map((t) => (
))}
{adminTab === "dashboard" && adminStats && (
I.users(20){adminStats.users}Toplam Üye
I.globe(20){adminStats.online}Çevrimiçi
I.radio(20){adminStats.rooms}Canlı Oda
{I.messageCircle(20)}{adminStats.messages_today}Bugünkü Mesaj
I.bot(20){adminStats.bots}Aktif Bot
I.headphones(20){adminStats.radio_stations}Radyo İstasyonu
I.warning(20){adminStats.reports_open}Açık Rapor
I.ban(20){adminStats.banned_users}Engellenen
)}
{adminTab === "users" && (
setAdminSearch(e.target.value)} onKeyDown={(e) => e.key === "Enter" && loadAdminData("users")} />
{adminUsers.map((u) => (
{u.display_name}
@{u.username} · {u.email}
{isSuperAdmin && (
)}
))}
)}
{adminTab === "rooms" && (
{adminRooms.map((r) => (
{r.name}
{r.category} · {r.member_count} üye · {r.host_name}
{r.is_live ? "🟢 Canlı" : "⚫ Kapalı"} · {r.is_hidden ? "Gizli" : "Açık"}
))}
)}
{adminTab === "bots" && (
{adminBots.map((b) => (
{I.bot(20)}
{b.bot_name}
@{b.username} · Sahibi: {b.owner_name}
{b.is_active ? "Aktif" : "Pasif"} · {b.reply_count || 0} otomatik cevap
))}
)}
{adminTab === "radio" && (
{adminRadio.map((s) => (
{I.headphones(18)}
{s.name}
{s.genre || 'Genel'} · {s.vote_count} oy · Ekleyen: {s.added_by_name}
))}
)}
{adminTab === "reports" && (
{adminReports.length === 0 &&
Açık rapor yok
}
{adminReports.map((r) => (
{r.reason}
Raporlayan: {r.reporter_name} · Durum: {r.status}
{r.created_at}
))}
)}
{adminTab === "logs" && (
{adminLogs.map((l) => (
{l.action}
{l.entity_type} #{l.entity_id || '—'}
{l.actor_name} · {l.ip_address || '—'}
{l.created_at}
))}
)}
{adminTab === "announcements" && (
{adminAnnouncements.map((a) => (
{a.title}
{a.body}
Öncelik: {a.priority} · {a.created_by_name} · {a.created_at}
))}
)}
{adminTab === "settings" && isRoot && (
setAdminSettings({ ...adminSettings, site_name: e.target.value })} />
setAdminSettings({ ...adminSettings, max_upload_mb: e.target.value })} />
setAdminSettings({ ...adminSettings, welcome_message: e.target.value })} />
)}
{adminTab === "ipbans" && isRoot && (
{adminIpBans.map((b) => (
{b.ip_address}
{b.reason || '—'} · {b.banned_by_name}
))}
)}
);
}
return (
{selected ? (
<>
{selected.display_name}
{selected.is_online ? "şimdi çevrimiçi" : "çevrimdışı"} · @{selected.username}
I.lock(12) Mesajların uçtan uca korunuyor
{chatClosed &&
I.lock(16) Bu sohbet yönetici tarafından kapatıldı
}
{selectedMessages.map((message) => (
{message.sender_id !== liveUser?.id &&
}
{message.sender_id !== liveUser?.id &&
{message.display_name}}
{message.message_type === "gif" ?

: message.body}
{message.created_at ? new Date(message.created_at).toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' }) : ''} {message.is_pinned && I.pin(11)}
))}
>
) : (
I.messageCircle(48)
Nova Talk'a Hoş Geldin
Sohbet etmeye başlamak için sol panelden bir konuşma seç veya yeni biriyle tanış.
{announcements.length > 0 && (
I.bell(16)
{announcements[0].title}
{announcements[0].body}
)}
)}
{showSearch && (
setShowSearch(false)}>
I.search(18) setSearch(e.target.value)} />
{search ? "Sonuçlar" : "Son aramalar"}
{conversations.filter((c) => !search || `${c.display_name} ${c.username}`.toLowerCase().includes(search.toLowerCase())).slice(0, 5).map((c) => (
))}
)}
{showNotifications && (
setShowNotifications(false)}>
{announcements.length === 0 &&
Bildirim yok
}
{announcements.map((a) => (
I.bell(16)
{a.title}{a.body}
))}
)}
{showAccount && (
setShowAccount(false)}>
{liveUser ? (
<>
{liveUser.display_name}
@{liveUser.username}
Çevrimiçi
>
) : (
)}
)}
{showSettings && (
setShowSettings(false)}>
)}
{showAdmin && renderAdminPanel()}
{showBotModal && (
setShowBotModal(false)}>
)}
{showRadioModal && (
setShowRadioModal(false)}>
)}
{showAnnouncementModal && (
setShowAnnouncementModal(false)}>
)}
{showVoiceModal && (
setShowVoiceModal(false)}>
)}
{showAvatarUpload && (
setShowAvatarUpload(false)}>
)}
{callType && (
{callType === "voice" ? "Sesli arama" : "Görüntülü arama"}
{selected?.display_name || "Bilinmeyen"}
Bağlanıyor...
)}
{toast &&
{I.check(16)} {toast}
}
);
}
createRoot(document.getElementById("root")).render();