Loading gists…
A Text snippet, saved for reference.
| 1 | <!DOCTYPE html> |
| 2 | <html lang="en"> |
| 3 | <head> |
| 4 | <meta charset="UTF-8"> |
| 5 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 6 | <title>StreamControl Pro - Live Streaming Management</title> |
| 7 | <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> |
| 8 | <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"> |
| 9 | <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet"> |
| 10 | <style> |
| 11 | /* Global CSS Styles */ |
| 12 | :root { |
| 13 | --primary-color: #ff0000; |
| 14 | --secondary-color: #282828; |
| 15 | --dark-bg: #0f0f0f; |
| 16 | --card-bg: #1a1a1a; |
| 17 | --text-primary: #ffffff; |
| 18 | --text-secondary: #aaaaaa; |
| 19 | --border-color: #303030; |
| 20 | --success-color: #00d4aa; |
| 21 | --warning-color: #ff6b35; |
| 22 | } |
| 23 | |
| 24 | * { |
| 25 | margin: 0; |
| 26 | padding: 0; |
| 27 | box-sizing: border-box; |
| 28 | } |
| 29 | |
| 30 | body { |
| 31 | font-family: 'Roboto', -apple-system, BlinkMacSystemFont, sans-serif; |
| 32 | background: linear-gradient(135deg, var(--dark-bg) 0%, var(--secondary-color) 100%); |
| 33 | color: var(--text-primary); |
| 34 | min-height: 100vh; |
| 35 | } |
| 36 | |
| 37 | /* Auth Page Styles */ |
| 38 | .auth-container { |
| 39 | min-height: 100vh; |
| 40 | display: flex; |
| 41 | align-items: center; |
| 42 | justify-content: center; |
| 43 | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| 44 | position: relative; |
| 45 | overflow: hidden; |
| 46 | } |
| 47 | |
| 48 | .auth-container::before { |
| 49 | content: ''; |
| 50 | position: absolute; |
| 51 | top: 0; |
| 52 | left: 0; |
| 53 | right: 0; |
| 54 | bottom: 0; |
| 55 | background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grid" width="10" height="10" patternUnits="userSpaceOnUse"><path d="M 10 0 L 0 0 0 10" fill="none" stroke="rgba(255,255,255,0.1)" stroke-width="0.5"/></pattern></defs><rect width="100" height="100" fill="url(%23grid)"/></svg>'); |
| 56 | opacity: 0.3; |
| 57 | } |
| 58 | |
| 59 | .auth-card { |
| 60 | background: rgba(255, 255, 255, 0.95); |
| 61 | backdrop-filter: blur(20px); |
| 62 | border-radius: 20px; |
| 63 | box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); |
| 64 | border: 1px solid rgba(255, 255, 255, 0.2); |
| 65 | color: #333; |
| 66 | position: relative; |
| 67 | z-index: 1; |
| 68 | overflow: hidden; |
| 69 | } |
| 70 | |
| 71 | .auth-card::before { |
| 72 | content: ''; |
| 73 | position: absolute; |
| 74 | top: 0; |
| 75 | left: 0; |
| 76 | right: 0; |
| 77 | height: 4px; |
| 78 | background: linear-gradient(90deg, var(--primary-color), #ff6b35, #00d4aa); |
| 79 | } |
| 80 | |
| 81 | .logo-section { |
| 82 | text-align: center; |
| 83 | margin-bottom: 2rem; |
| 84 | } |
| 85 | |
| 86 | .logo { |
| 87 | width: 80px; |
| 88 | height: 80px; |
| 89 | background: linear-gradient(135deg, var(--primary-color), #ff6b35); |
| 90 | border-radius: 20px; |
| 91 | display: inline-flex; |
| 92 | align-items: center; |
| 93 | justify-content: center; |
| 94 | margin-bottom: 1rem; |
| 95 | box-shadow: 0 10px 30px rgba(255, 0, 0, 0.3); |
| 96 | } |
| 97 | |
| 98 | .form-control { |
| 99 | background: rgba(255, 255, 255, 0.9); |
| 100 | border: 2px solid #e1e5e9; |
| 101 | border-radius: 12px; |
| 102 | padding: 12px 16px; |
| 103 | transition: all 0.3s ease; |
| 104 | } |
| 105 | |
| 106 | .form-control:focus { |
| 107 | background: white; |
| 108 | border-color: var(--primary-color); |
| 109 | box-shadow: 0 0 0 3px rgba(255, 0, 0, 0.1); |
| 110 | } |
| 111 | |
| 112 | .btn-primary { |
| 113 | background: linear-gradient(135deg, var(--primary-color), #ff6b35); |
| 114 | border: none; |
| 115 | border-radius: 12px; |
| 116 | padding: 12px; |
| 117 | font-weight: 600; |
| 118 | transition: all 0.3s ease; |
| 119 | } |
| 120 | |
| 121 | .btn-primary:hover { |
| 122 | transform: translateY(-2px); |
| 123 | box-shadow: 0 10px 30px rgba(255, 0, 0, 0.3); |
| 124 | } |
| 125 | |
| 126 | /* Admin Panel Styles */ |
| 127 | .admin-container { |
| 128 | display: flex; |
| 129 | min-height: 100vh; |
| 130 | background: var(--dark-bg); |
| 131 | } |
| 132 | |
| 133 | .sidebar { |
| 134 | width: 280px; |
| 135 | background: var(--card-bg); |
| 136 | border-right: 1px solid var(--border-color); |
| 137 | position: fixed; |
| 138 | height: 100vh; |
| 139 | overflow-y: auto; |
| 140 | z-index: 1000; |
| 141 | } |
| 142 | |
| 143 | .sidebar-header { |
| 144 | padding: 24px; |
| 145 | border-bottom: 1px solid var(--border-color); |
| 146 | text-align: center; |
| 147 | } |
| 148 | |
| 149 | .sidebar-nav { |
| 150 | padding: 16px 0; |
| 151 | } |
| 152 | |
| 153 | .nav-item { |
| 154 | margin: 8px 16px; |
| 155 | } |
| 156 | |
| 157 | .nav-link { |
| 158 | display: flex; |
| 159 | align-items: center; |
| 160 | padding: 12px 16px; |
| 161 | color: var(--text-secondary); |
| 162 | text-decoration: none; |
| 163 | border-radius: 12px; |
| 164 | transition: all 0.3s ease; |
| 165 | } |
| 166 | |
| 167 | .nav-link:hover, .nav-link.active { |
| 168 | background: rgba(255, 0, 0, 0.1); |
| 169 | color: var(--primary-color); |
| 170 | } |
| 171 | |
| 172 | .nav-link i { |
| 173 | width: 24px; |
| 174 | margin-right: 12px; |
| 175 | } |
| 176 | |
| 177 | .main-content { |
| 178 | flex: 1; |
| 179 | margin-left: 280px; |
| 180 | background: var(--dark-bg); |
| 181 | } |
| 182 | |
| 183 | .top-bar { |
| 184 | background: var(--card-bg); |
| 185 | border-bottom: 1px solid var(--border-color); |
| 186 | padding: 16px 32px; |
| 187 | display: flex; |
| 188 | justify-content: between; |
| 189 | align-items: center; |
| 190 | position: sticky; |
| 191 | top: 0; |
| 192 | z-index: 999; |
| 193 | } |
| 194 | |
| 195 | .admin-info { |
| 196 | display: flex; |
| 197 | align-items: center; |
| 198 | margin-left: auto; |
| 199 | } |
| 200 | |
| 201 | .admin-avatar { |
| 202 | width: 40px; |
| 203 | height: 40px; |
| 204 | background: linear-gradient(135deg, var(--primary-color), #ff6b35); |
| 205 | border-radius: 50%; |
| 206 | display: flex; |
| 207 | align-items: center; |
| 208 | justify-content: center; |
| 209 | margin-right: 12px; |
| 210 | font-weight: bold; |
| 211 | } |
| 212 | |
| 213 | .content-area { |
| 214 | padding: 32px; |
| 215 | } |
| 216 | |
| 217 | .stats-grid { |
| 218 | display: grid; |
| 219 | grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); |
| 220 | gap: 24px; |
| 221 | margin-bottom: 32px; |
| 222 | } |
| 223 | |
| 224 | .stat-card { |
| 225 | background: var(--card-bg); |
| 226 | border: 1px solid var(--border-color); |
| 227 | border-radius: 16px; |
| 228 | padding: 24px; |
| 229 | transition: all 0.3s ease; |
| 230 | } |
| 231 | |
| 232 | .stat-card:hover { |
| 233 | transform: translateY(-4px); |
| 234 | box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); |
| 235 | } |
| 236 | |
| 237 | .stat-icon { |
| 238 | width: 60px; |
| 239 | height: 60px; |
| 240 | border-radius: 12px; |
| 241 | display: flex; |
| 242 | align-items: center; |
| 243 | justify-content: center; |
| 244 | margin-bottom: 16px; |
| 245 | font-size: 24px; |
| 246 | } |
| 247 | |
| 248 | .section-card { |
| 249 | background: var(--card-bg); |
| 250 | border: 1px solid var(--border-color); |
| 251 | border-radius: 16px; |
| 252 | padding: 32px; |
| 253 | margin-bottom: 24px; |
| 254 | } |
| 255 | |
| 256 | .section-title { |
| 257 | display: flex; |
| 258 | align-items: center; |
| 259 | justify-content: between; |
| 260 | margin-bottom: 24px; |
| 261 | } |
| 262 | |
| 263 | .btn-custom { |
| 264 | background: linear-gradient(135deg, var(--primary-color), #ff6b35); |
| 265 | border: none; |
| 266 | border-radius: 12px; |
| 267 | padding: 10px 20px; |
| 268 | color: white; |
| 269 | font-weight: 600; |
| 270 | transition: all 0.3s ease; |
| 271 | } |
| 272 | |
| 273 | .btn-custom:hover { |
| 274 | transform: translateY(-2px); |
| 275 | box-shadow: 0 10px 30px rgba(255, 0, 0, 0.3); |
| 276 | color: white; |
| 277 | } |
| 278 | |
| 279 | .form-control-dark { |
| 280 | background: var(--secondary-color); |
| 281 | border: 1px solid var(--border-color); |
| 282 | color: var(--text-primary); |
| 283 | border-radius: 8px; |
| 284 | } |
| 285 | |
| 286 | .form-control-dark:focus { |
| 287 | border-color: var(--primary-color); |
| 288 | box-shadow: 0 0 0 2px rgba(255, 0, 0, 0.2); |
| 289 | background: var(--secondary-color); |
| 290 | color: var(--text-primary); |
| 291 | } |
| 292 | |
| 293 | .table-dark { |
| 294 | background: var(--card-bg); |
| 295 | border-radius: 12px; |
| 296 | overflow: hidden; |
| 297 | } |
| 298 | |
| 299 | .table-dark th { |
| 300 | background: var(--secondary-color); |
| 301 | border-color: var(--border-color); |
| 302 | padding: 16px; |
| 303 | } |
| 304 | |
| 305 | .table-dark td { |
| 306 | border-color: var(--border-color); |
| 307 | padding: 16px; |
| 308 | } |
| 309 | |
| 310 | .btn-sm { |
| 311 | padding: 6px 12px; |
| 312 | font-size: 0.875rem; |
| 313 | border-radius: 8px; |
| 314 | } |
| 315 | |
| 316 | .btn-success { |
| 317 | background: var(--success-color); |
| 318 | border-color: var(--success-color); |
| 319 | } |
| 320 | |
| 321 | .btn-warning { |
| 322 | background: var(--warning-color); |
| 323 | border-color: var(--warning-color); |
| 324 | } |
| 325 | |
| 326 | .btn-danger { |
| 327 | background: #dc3545; |
| 328 | border-color: #dc3545; |
| 329 | } |
| 330 | |
| 331 | .modal-content { |
| 332 | background: var(--card-bg); |
| 333 | border: 1px solid var(--border-color); |
| 334 | border-radius: 16px; |
| 335 | } |
| 336 | |
| 337 | .modal-header { |
| 338 | border-bottom: 1px solid var(--border-color); |
| 339 | } |
| 340 | |
| 341 | .modal-footer { |
| 342 | border-top: 1px solid var(--border-color); |
| 343 | } |
| 344 | |
| 345 | .alert { |
| 346 | border-radius: 12px; |
| 347 | padding: 16px; |
| 348 | margin-bottom: 16px; |
| 349 | } |
| 350 | |
| 351 | .channel-item { |
| 352 | background: var(--secondary-color); |
| 353 | border: 1px solid var(--border-color); |
| 354 | border-radius: 8px; |
| 355 | padding: 12px; |
| 356 | margin-bottom: 8px; |
| 357 | display: flex; |
| 358 | justify-content: between; |
| 359 | align-items: center; |
| 360 | } |
| 361 | |
| 362 | .channel-url { |
| 363 | font-family: monospace; |
| 364 | font-size: 0.9rem; |
| 365 | color: var(--text-secondary); |
| 366 | } |
| 367 | |
| 368 | /* Responsive Design */ |
| 369 | @media (max-width: 768px) { |
| 370 | .sidebar { |
| 371 | transform: translateX(-100%); |
| 372 | transition: transform 0.3s ease; |
| 373 | } |
| 374 | |
| 375 | .sidebar.show { |
| 376 | transform: translateX(0); |
| 377 | } |
| 378 | |
| 379 | .main-content { |
| 380 | margin-left: 0; |
| 381 | } |
| 382 | |
| 383 | .stats-grid { |
| 384 | grid-template-columns: 1fr; |
| 385 | } |
| 386 | |
| 387 | .content-area { |
| 388 | padding: 16px; |
| 389 | } |
| 390 | |
| 391 | .top-bar { |
| 392 | padding: 16px; |
| 393 | } |
| 394 | } |
| 395 | |
| 396 | /* Loading Animation */ |
| 397 | .loading { |
| 398 | display: inline-block; |
| 399 | width: 20px; |
| 400 | height: 20px; |
| 401 | border: 3px solid rgba(255, 255, 255, 0.3); |
| 402 | border-radius: 50%; |
| 403 | border-top-color: #fff; |
| 404 | animation: spin 1s ease-in-out infinite; |
| 405 | } |
| 406 | |
| 407 | @keyframes spin { |
| 408 | to { transform: rotate(360deg); } |
| 409 | } |
| 410 | |
| 411 | /* Hide elements */ |
| 412 | .d-none { |
| 413 | display: none !important; |
| 414 | } |
| 415 | |
| 416 | .show { |
| 417 | display: block !important; |
| 418 | } |
| 419 | |
| 420 | .hide { |
| 421 | display: none !important; |
| 422 | } |
| 423 | </style> |
| 424 | </head> |
| 425 | <body> |
| 426 | <!-- Home/Landing Page --> |
| 427 | <div id="homePage" class="container-fluid p-0"> |
| 428 | <div class="auth-container"> |
| 429 | <div class="auth-card p-5" style="max-width: 400px; width: 90%;"> |
| 430 | <div class="logo-section"> |
| 431 | <div class="logo"> |
| 432 | <i class="fas fa-broadcast-tower text-white fs-1"></i> |
| 433 | </div> |
| 434 | <h2 class="fw-bold mb-1">StreamControl Pro</h2> |
| 435 | <p class="text-muted">Professional Live Streaming Management</p> |
| 436 | </div> |
| 437 | |
| 438 | <div class="text-center"> |
| 439 | <button id="goToAuth" class="btn btn-primary btn-lg px-5 mb-3"> |
| 440 | <i class="fas fa-sign-in-alt me-2"></i>Get Started |
| 441 | </button> |
| 442 | <p class="small text-muted">Manage your live streams like a pro</p> |
| 443 | </div> |
| 444 | </div> |
| 445 | </div> |
| 446 | </div> |
| 447 | |
| 448 | <!-- Authentication Page --> |
| 449 | <div id="authPage" class="d-none"> |
| 450 | <div class="auth-container"> |
| 451 | <div class="auth-card p-5" style="max-width: 450px; width: 90%;"> |
| 452 | <div class="logo-section"> |
| 453 | <div class="logo"> |
| 454 | <i class="fas fa-broadcast-tower text-white fs-1"></i> |
| 455 | </div> |
| 456 | <h2 class="fw-bold mb-3">Welcome Back</h2> |
| 457 | </div> |
| 458 | |
| 459 | <!-- Login Form --> |
| 460 | <div id="loginForm"> |
| 461 | <form id="loginFormElement"> |
| 462 | <div class="mb-3"> |
| 463 | <label class="form-label fw-semibold">Email Address</label> |
| 464 | <input type="email" id="loginEmail" class="form-control" placeholder="Enter your email" required> |
| 465 | </div> |
| 466 | <div class="mb-4"> |
| 467 | <label class="form-label fw-semibold">Password</label> |
| 468 | <input type="password" id="loginPassword" class="form-control" placeholder="Enter your password" required> |
| 469 | </div> |
| 470 | <button type="submit" class="btn btn-primary w-100 mb-3"> |
| 471 | <span id="loginLoader" class="loading d-none"></span> |
| 472 | <span id="loginText">Sign In</span> |
| 473 | </button> |
| 474 | </form> |
| 475 | <div class="text-center"> |
| 476 | <span class="text-muted">Don't have an account? </span> |
| 477 | <a href="#" id="showSignup" class="text-decoration-none fw-semibold">Sign Up</a> |
| 478 | </div> |
| 479 | </div> |
| 480 | |
| 481 | <!-- Signup Form --> |
| 482 | <div id="signupForm" class="d-none"> |
| 483 | <form id="signupFormElement"> |
| 484 | <div class="mb-3"> |
| 485 | <label class="form-label fw-semibold">Full Name</label> |
| 486 | <input type="text" id="signupName" class="form-control" placeholder="Enter your full name" required> |
| 487 | </div> |
| 488 | <div class="mb-3"> |
| 489 | <label class="form-label fw-semibold">Email Address</label> |
| 490 | <input type="email" id="signupEmail" class="form-control" placeholder="Enter your email" required> |
| 491 | </div> |
| 492 | <div class="mb-4"> |
| 493 | <label class="form-label fw-semibold">Password</label> |
| 494 | <input type="password" id="signupPassword" class="form-control" placeholder="Create a password" required> |
| 495 | </div> |
| 496 | <button type="submit" class="btn btn-primary w-100 mb-3"> |
| 497 | <span id="signupLoader" class="loading d-none"></span> |
| 498 | <span id="signupText">Create Account</span> |
| 499 | </button> |
| 500 | </form> |
| 501 | <div class="text-center"> |
| 502 | <span class="text-muted">Already have an account? </span> |
| 503 | <a href="#" id="showLogin" class="text-decoration-none fw-semibold">Sign In</a> |
| 504 | </div> |
| 505 | </div> |
| 506 | |
| 507 | <div id="authAlert" class="alert d-none"></div> |
| 508 | </div> |
| 509 | </div> |
| 510 | </div> |
| 511 | |
| 512 | <!-- Admin Panel --> |
| 513 | <div id="adminPanel" class="d-none"> |
| 514 | <div class="admin-container"> |
| 515 | <!-- Sidebar --> |
| 516 | <nav class="sidebar"> |
| 517 | <div class="sidebar-header"> |
| 518 | <div class="logo mb-3"> |
| 519 | <i class="fas fa-broadcast-tower text-white fs-2"></i> |
| 520 | </div> |
| 521 | <h5 class="fw-bold">StreamControl Pro</h5> |
| 522 | <p class="small text-muted mb-0">Admin Dashboard</p> |
| 523 | </div> |
| 524 | |
| 525 | <div class="sidebar-nav"> |
| 526 | <div class="nav-item"> |
| 527 | <a href="#" class="nav-link active" data-section="dashboard"> |
| 528 | <i class="fas fa-tachometer-alt"></i> |
| 529 | Dashboard |
| 530 | </a> |
| 531 | </div> |
| 532 | <div class="nav-item"> |
| 533 | <a href="#" class="nav-link" data-section="streams"> |
| 534 | <i class="fas fa-video"></i> |
| 535 | Live Streams |
| 536 | </a> |
| 537 | </div> |
| 538 | <div class="nav-item"> |
| 539 | <a href="#" class="nav-link" data-section="categories"> |
| 540 | <i class="fas fa-tags"></i> |
| 541 | Categories |
| 542 | </a> |
| 543 | </div> |
| 544 | <div class="nav-item"> |
| 545 | <a href="#" class="nav-link" data-section="analytics"> |
| 546 | <i class="fas fa-chart-bar"></i> |
| 547 | Analytics |
| 548 | </a> |
| 549 | </div> |
| 550 | <div class="nav-item"> |
| 551 | <a href="#" class="nav-link" data-section="settings"> |
| 552 | <i class="fas fa-cog"></i> |
| 553 | Settings |
| 554 | </a> |
| 555 | </div> |
| 556 | </div> |
| 557 | </nav> |
| 558 | |
| 559 | <!-- Main Content --> |
| 560 | <main class="main-content"> |
| 561 | <!-- Top Bar --> |
| 562 | <div class="top-bar"> |
| 563 | <div class="d-flex align-items-center"> |
| 564 | <button class="btn btn-outline-secondary d-lg-none me-3" id="toggleSidebar"> |
| 565 | <i class="fas fa-bars"></i> |
| 566 | </button> |
| 567 | <h4 class="mb-0 fw-bold" id="pageTitle">Dashboard</h4> |
| 568 | </div> |
| 569 | |
| 570 | <div class="admin-info"> |
| 571 | <div class="admin-avatar" id="adminAvatar">A</div> |
| 572 | <div> |
| 573 | <div class="fw-semibold" id="adminName">Admin User</div> |
| 574 | <div class="small text-muted">Administrator</div> |
| 575 | </div> |
| 576 | <div class="dropdown ms-3"> |
| 577 | <button class="btn btn-outline-secondary" type="button" data-bs-toggle="dropdown"> |
| 578 | <i class="fas fa-ellipsis-v"></i> |
| 579 | </button> |
| 580 | <ul class="dropdown-menu"> |
| 581 | <li><a class="dropdown-item" href="#"><i class="fas fa-user me-2"></i>Profile</a></li> |
| 582 | <li><a class="dropdown-item" href="#"><i class="fas fa-cog me-2"></i>Settings</a></li> |
| 583 | <li><hr class="dropdown-divider"></li> |
| 584 | <li><a class="dropdown-item" href="#" id="logoutBtn"><i class="fas fa-sign-out-alt me-2"></i>Logout</a></li> |
| 585 | </ul> |
| 586 | </div> |
| 587 | </div> |
| 588 | </div> |
| 589 | |
| 590 | <!-- Content Area --> |
| 591 | <div class="content-area"> |
| 592 | <!-- Dashboard Section --> |
| 593 | <div id="dashboardSection" class="content-section"> |
| 594 | <div class="stats-grid"> |
| 595 | <div class="stat-card"> |
| 596 | <div class="stat-icon" style="background: rgba(255, 0, 0, 0.1); color: var(--primary-color);"> |
| 597 | <i class="fas fa-video"></i> |
| 598 | </div> |
| 599 | <h3 class="fw-bold mb-1" id="totalStreams">0</h3> |
| 600 | <p class="text-muted mb-0">Total Streams</p> |
| 601 | </div> |
| 602 | <div class="stat-card"> |
| 603 | <div class="stat-icon" style="background: rgba(0, 212, 170, 0.1); color: var(--success-color);"> |
| 604 | <i class="fas fa-play-circle"></i> |
| 605 | </div> |
| 606 | <h3 class="fw-bold mb-1" id="activeStreams">0</h3> |
| 607 | <p class="text-muted mb-0">Active Streams</p> |
| 608 | </div> |
| 609 | <div class="stat-card"> |
| 610 | <div class="stat-icon" style="background: rgba(255, 107, 53, 0.1); color: var(--warning-color);"> |
| 611 | <i class="fas fa-tags"></i> |
| 612 | </div> |
| 613 | <h3 class="fw-bold mb-1" id="totalCategories">0</h3> |
| 614 | <p class="text-muted mb-0">Categories</p> |
| 615 | </div> |
| 616 | <div class="stat-card"> |
| 617 | <div class="stat-icon" style="background: rgba(106, 90, 205, 0.1); color: #6a5acd;"> |
| 618 | <i class="fas fa-server"></i> |
| 619 | </div> |
| 620 | <h3 class="fw-bold mb-1" id="totalChannels">0</h3> |
| 621 | <p class="text-muted mb-0">Channel Sources</p> |
| 622 | </div> |
| 623 | </div> |
| 624 | </div> |
| 625 | |
| 626 | <!-- Streams Section --> |
| 627 | <div id="streamsSection" class="content-section d-none"> |
| 628 | <div class="section-card"> |
| 629 | <div class="section-title"> |
| 630 | <h3 class="fw-bold mb-0">Live Streams Management</h3> |
| 631 | <button class="btn btn-custom" data-bs-toggle="modal" data-bs-target="#addStreamModal"> |
| 632 | <i class="fas fa-plus me-2"></i>Add New Stream |
| 633 | </button> |
| 634 | </div> |
| 635 | |
| 636 | <div class="table-responsive"> |
| 637 | <table class="table table-dark table-hover"> |
| 638 | <thead> |
| 639 | <tr> |
| 640 | <th>Sport</th> |
| 641 | <th>Tournament</th> |
| 642 | <th>Match</th> |
| 643 | <th>Channels</th> |
| 644 | <th>Actions</th> |
| 645 | </tr> |
| 646 | </thead> |
| 647 | <tbody id="streamsTableBody"> |
| 648 | <!-- Dynamic content will be inserted here --> |
| 649 | </tbody> |
| 650 | </table> |
| 651 | </div> |
| 652 | </div> |
| 653 | </div> |
| 654 | |
| 655 | <!-- Other sections will be added here --> |
| 656 | <div id="categoriesSection" class="content-section d-none"> |
| 657 | <div class="section-card"> |
| 658 | <h3 class="fw-bold">Categories</h3> |
| 659 | <p class="text-muted">Manage stream categories and sports.</p> |
| 660 | </div> |
| 661 | </div> |
| 662 | |
| 663 | <div id="analyticsSection" class="content-section d-none"> |
| 664 | <div class="section-card"> |
| 665 | <h3 class="fw-bold">Analytics</h3> |
| 666 | <p class="text-muted">View streaming analytics and performance metrics.</p> |
| 667 | </div> |
| 668 | </div> |
| 669 | |
| 670 | <div id="settingsSection" class="content-section d-none"> |
| 671 | <div class="section-card"> |
| 672 | <h3 class="fw-bold">Settings</h3> |
| 673 | <p class="text-muted">Configure system settings and preferences.</p> |
| 674 | </div> |
| 675 | </div> |
| 676 | </div> |
| 677 | </main> |
| 678 | </div> |
| 679 | </div> |
| 680 | |
| 681 | <!-- Add Stream Modal --> |
| 682 | <div class="modal fade" id="addStreamModal" tabindex="-1"> |
| 683 | <div class="modal-dialog modal-lg"> |
| 684 | <div class="modal-content"> |
| 685 | <div class="modal-header"> |
| 686 | <h5 class="modal-title">Add New Live Stream</h5> |
| 687 | <button type="button" class="btn-close" data-bs-dismiss="modal"></button> |
| 688 | </div> |
| 689 | <div class="modal-body"> |
| 690 | <form id="addStreamForm"> |
| 691 | <div class="row"> |
| 692 | <div class="col-md-6 mb-3"> |
| 693 | <label class="form-label">Sport</label> |
| 694 | <input type="text" id="streamSport" class="form-control form-control-dark" placeholder="e.g., Football" required> |
| 695 | </div> |
| 696 | <div class="col-md-6 mb-3"> |
| 697 | <label class="form-label">Tournament</label> |
| 698 | <input type="text" id="streamTournament" class="form-control form-control-dark" placeholder="e.g., Premier League" required> |
| 699 | </div> |
| 700 | </div> |
| 701 | <div class="mb-3"> |
| 702 | <label class="form-label">Match</label> |
| 703 | <input type="text" id="streamMatch" class="form-control form-control-dark" placeholder="e.g., Team A vs Team B" required> |
| 704 | </div> |
| 705 | <div class="mb-3"> |
| 706 | <label class="form-label">Channel URLs</label> |
| 707 | <div id="channelInputs"> |
| 708 | <div class="input-group mb-2"> |
| 709 | <input type="url" class="form-control form-control-dark channel-input" placeholder="https://example.com/channel/1" required> |
| 710 | <button type="button" class="btn btn-outline-success" onclick="addChannelInput()"> |
| 711 | <i class="fas fa-plus"></i> |
| 712 | </button> |
| 713 | </div> |
| 714 | </div> |
| 715 | </div> |
| 716 | </form> |
| 717 | </div> |
| 718 | <div class="modal-footer"> |
| 719 | <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> |
| 720 | <button type="button" class="btn btn-custom" id="saveStreamBtn"> |
| 721 | <i class="fas fa-save me-2"></i>Save Stream |
| 722 | </button> |
| 723 | </div> |
| 724 | </div> |
| 725 | </div> |
| 726 | </div> |
| 727 | |
| 728 | <!-- Edit Stream Modal --> |
| 729 | <div class="modal fade" id="editStreamModal" tabindex="-1"> |
| 730 | <div class="modal-dialog modal-lg"> |
| 731 | <div class="modal-content"> |
| 732 | <div class="modal-header"> |
| 733 | <h5 class="modal-title">Edit Live Stream</h5> |
| 734 | <button type="button" class="btn-close" data-bs-dismiss="modal"></button> |
| 735 | </div> |
| 736 | <div class="modal-body"> |
| 737 | <form id="editStreamForm"> |
| 738 | <input type="hidden" id="editStreamId"> |
| 739 | <div class="row"> |
| 740 | <div class="col-md-6 mb-3"> |
| 741 | <label class="form-label">Sport</label> |
| 742 | <input type="text" id="editStreamSport" class="form-control form-control-dark" required> |
| 743 | </div> |
| 744 | <div class="col-md-6 mb-3"> |
| 745 | <label class="form-label">Tournament</label> |
| 746 | <input type="text" id="editStreamTournament" class="form-control form-control-dark" required> |
| 747 | </div> |
| 748 | </div> |
| 749 | <div class="mb-3"> |
| 750 | <label class="form-label">Match</label> |
| 751 | <input type="text" id="editStreamMatch" class="form-control form-control-dark" required> |
| 752 | </div> |
| 753 | <div class="mb-3"> |
| 754 | <label class="form-label">Channel URLs</label> |
| 755 | <div id="editChannelInputs"> |
| 756 | <!-- Dynamic channel inputs will be inserted here --> |
| 757 | </div> |
| 758 | </div> |
| 759 | </form> |
| 760 | </div> |
| 761 | <div class="modal-footer"> |
| 762 | <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> |
| 763 | <button type="button" class="btn btn-custom" id="updateStreamBtn"> |
| 764 | <i class="fas fa-save me-2"></i>Update Stream |
| 765 | </button> |
| 766 | </div> |
| 767 | </div> |
| 768 | </div> |
| 769 | </div> |
| 770 | |
| 771 | <!-- Bootstrap JS --> |
| 772 | <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> |
| 773 | |
| 774 | <!-- Firebase Configuration --> |
| 775 | <script type="module"> |
| 776 | // Import Firebase modules |
| 777 | import { initializeApp } from "https://www.gstatic.com/firebasejs/11.8.1/firebase-app.js"; |
| 778 | import { getAuth, createUserWithEmailAndPassword, signInWithEmailAndPassword, signOut, onAuthStateChanged } from "https://www.gstatic.com/firebasejs/11.8.1/firebase-auth.js"; |
| 779 | import { getFirestore, collection, addDoc, getDocs, deleteDoc, doc, updateDoc, getDoc } from "https://www.gstatic.com/firebasejs/11.8.1/firebase-firestore.js"; |
| 780 | |
| 781 | // Firebase configuration |
| 782 | const firebaseConfig = { |
| 783 | apiKey: "AIzaSyA3gwO6KEBUg0jEaYpd-IYlbEbjtlXNBtg", |
| 784 | authDomain: "live-stream-3df4b.firebaseapp.com", |
| 785 | projectId: "live-stream-3df4b", |
| 786 | storageBucket: "live-stream-3df4b.firebasestorage.app", |
| 787 | messagingSenderId: "41369839856", |
| 788 | appId: "1:41369839856:web:e59112ad3c7352e920ba35", |
| 789 | measurementId: "G-8K3RF8KQD1" |
| 790 | }; |
| 791 | |
| 792 | // Initialize Firebase |
| 793 | const app = initializeApp(firebaseConfig); |
| 794 | const auth = getAuth(app); |
| 795 | const db = getFirestore(app); |
| 796 | |
| 797 | // Global variables |
| 798 | let currentUser = null; |
| 799 | let streams = []; |
| 800 | |
| 801 | // DOM Elements |
| 802 | const homePage = document.getElementById('homePage'); |
| 803 | const authPage = document.getElementById('authPage'); |
| 804 | const adminPanel = document.getElementById('adminPanel'); |
| 805 | const loginForm = document.getElementById('loginForm'); |
| 806 | const signupForm = document.getElementById('signupForm'); |
| 807 | |
| 808 | // Navigation |
| 809 | document.getElementById('goToAuth').addEventListener('click', () => { |
| 810 | homePage.classList.add('d-none'); |
| 811 | authPage.classList.remove('d-none'); |
| 812 | }); |
| 813 | |
| 814 | document.getElementById('showSignup').addEventListener('click', (e) => { |
| 815 | e.preventDefault(); |
| 816 | loginForm.classList.add('d-none'); |
| 817 | signupForm.classList.remove('d-none'); |
| 818 | }); |
| 819 | |
| 820 | document.getElementById('showLogin').addEventListener('click', (e) => { |
| 821 | e.preventDefault(); |
| 822 | signupForm.classList.add('d-none'); |
| 823 | loginForm.classList.remove('d-none'); |
| 824 | }); |
| 825 | |
| 826 | // Authentication |
| 827 | onAuthStateChanged(auth, (user) => { |
| 828 | if (user) { |
| 829 | currentUser = user; |
| 830 | showAdminPanel(); |
| 831 | loadStreams(); |
| 832 | updateStats(); |
| 833 | } else { |
| 834 | currentUser = null; |
| 835 | showAuthPage(); |
| 836 | } |
| 837 | }); |
| 838 | |
| 839 | // Login |
| 840 | document.getElementById('loginFormElement').addEventListener('submit', async (e) => { |
| 841 | e.preventDefault(); |
| 842 | const email = document.getElementById('loginEmail').value; |
| 843 | const password = document.getElementById('loginPassword').value; |
| 844 | |
| 845 | showLoading('login', true); |
| 846 | try { |
| 847 | await signInWithEmailAndPassword(auth, email, password); |
| 848 | showAlert('Login successful!', 'success'); |
| 849 | } catch (error) { |
| 850 | showAlert(error.message, 'danger'); |
| 851 | } |
| 852 | showLoading('login', false); |
| 853 | }); |
| 854 | |
| 855 | // Signup |
| 856 | document.getElementById('signupFormElement').addEventListener('submit', async (e) => { |
| 857 | e.preventDefault(); |
| 858 | const name = document.getElementById('signupName').value; |
| 859 | const email = document.getElementById('signupEmail').value; |
| 860 | const password = document.getElementById('signupPassword').value; |
| 861 | |
| 862 | showLoading('signup', true); |
| 863 | try { |
| 864 | const userCredential = await createUserWithEmailAndPassword(auth, email, password); |
| 865 | // Store user info in Firestore |
| 866 | await addDoc(collection(db, 'users'), { |
| 867 | uid: userCredential.user.uid, |
| 868 | name: name, |
| 869 | email: email, |
| 870 | role: 'admin', |
| 871 | createdAt: new Date() |
| 872 | }); |
| 873 | showAlert('Account created successfully!', 'success'); |
| 874 | } catch (error) { |
| 875 | showAlert(error.message, 'danger'); |
| 876 | } |
| 877 | showLoading('signup', false); |
| 878 | }); |
| 879 | |
| 880 | // Logout |
| 881 | document.getElementById('logoutBtn').addEventListener('click', async () => { |
| 882 | try { |
| 883 | await signOut(auth); |
| 884 | showAlert('Logged out successfully!', 'success'); |
| 885 | } catch (error) { |
| 886 | showAlert(error.message, 'danger'); |
| 887 | } |
| 888 | }); |
| 889 | |
| 890 | // Show/Hide functions |
| 891 | function showAuthPage() { |
| 892 | homePage.classList.add('d-none'); |
| 893 | authPage.classList.remove('d-none'); |
| 894 | adminPanel.classList.add('d-none'); |
| 895 | } |
| 896 | |
| 897 | function showAdminPanel() { |
| 898 | homePage.classList.add('d-none'); |
| 899 | authPage.classList.add('d-none'); |
| 900 | adminPanel.classList.remove('d-none'); |
| 901 | |
| 902 | // Set admin name |
| 903 | if (currentUser) { |
| 904 | const adminName = currentUser.email.split('@')[0]; |
| 905 | document.getElementById('adminName').textContent = adminName; |
| 906 | document.getElementById('adminAvatar').textContent = adminName.charAt(0).toUpperCase(); |
| 907 | } |
| 908 | } |
| 909 | |
| 910 | function showLoading(type, show) { |
| 911 | const loader = document.getElementById(`${type}Loader`); |
| 912 | const text = document.getElementById(`${type}Text`); |
| 913 | |
| 914 | if (show) { |
| 915 | loader.classList.remove('d-none'); |
| 916 | text.textContent = 'Please wait...'; |
| 917 | } else { |
| 918 | loader.classList.add('d-none'); |
| 919 | text.textContent = type === 'login' ? 'Sign In' : 'Create Account'; |
| 920 | } |
| 921 | } |
| 922 | |
| 923 | function showAlert(message, type) { |
| 924 | const alert = document.getElementById('authAlert'); |
| 925 | alert.className = `alert alert-${type}`; |
| 926 | alert.textContent = message; |
| 927 | alert.classList.remove('d-none'); |
| 928 | |
| 929 | setTimeout(() => { |
| 930 | alert.classList.add('d-none'); |
| 931 | }, 5000); |
| 932 | } |
| 933 | |
| 934 | // Navigation between sections |
| 935 | document.querySelectorAll('.nav-link').forEach(link => { |
| 936 | link.addEventListener('click', (e) => { |
| 937 | e.preventDefault(); |
| 938 | const section = link.getAttribute('data-section'); |
| 939 | |
| 940 | // Update active nav |
| 941 | document.querySelectorAll('.nav-link').forEach(l => l.classList.remove('active')); |
| 942 | link.classList.add('active'); |
| 943 | |
| 944 | // Show section |
| 945 | document.querySelectorAll('.content-section').forEach(s => s.classList.add('d-none')); |
| 946 | document.getElementById(`${section}Section`).classList.remove('d-none'); |
| 947 | |
| 948 | // Update page title |
| 949 | const titles = { |
| 950 | dashboard: 'Dashboard', |
| 951 | streams: 'Live Streams', |
| 952 | categories: 'Categories', |
| 953 | analytics: 'Analytics', |
| 954 | settings: 'Settings' |
| 955 | }; |
| 956 | document.getElementById('pageTitle').textContent = titles[section]; |
| 957 | }); |
| 958 | }); |
| 959 | |
| 960 | // Stream Management Functions |
| 961 | async function loadStreams() { |
| 962 | try { |
| 963 | const querySnapshot = await getDocs(collection(db, 'streams')); |
| 964 | streams = []; |
| 965 | querySnapshot.forEach((doc) => { |
| 966 | streams.push({ id: doc.id, ...doc.data() }); |
| 967 | }); |
| 968 | renderStreamsTable(); |
| 969 | updateStats(); |
| 970 | } catch (error) { |
| 971 | console.error('Error loading streams:', error); |
| 972 | } |
| 973 | } |
| 974 | |
| 975 | function renderStreamsTable() { |
| 976 | const tbody = document.getElementById('streamsTableBody'); |
| 977 | tbody.innerHTML = ''; |
| 978 | |
| 979 | streams.forEach(stream => { |
| 980 | const row = document.createElement('tr'); |
| 981 | row.innerHTML = ` |
| 982 | <td class="fw-semibold">${stream.sport}</td> |
| 983 | <td>${stream.tournament}</td> |
| 984 | <td>${stream.match}</td> |
| 985 | <td> |
| 986 | <span class="badge bg-primary">${stream.channels.length} channels</span> |
| 987 | </td> |
| 988 | <td> |
| 989 | <button class="btn btn-sm btn-success me-2" onclick="editStream('${stream.id}')"> |
| 990 | <i class="fas fa-edit"></i> |
| 991 | </button> |
| 992 | <button class="btn btn-sm btn-danger" onclick="deleteStream('${stream.id}')"> |
| 993 | <i class="fas fa-trash"></i> |
| 994 | </button> |
| 995 | </td> |
| 996 | `; |
| 997 | tbody.appendChild(row); |
| 998 | }); |
| 999 | } |
| 1000 | |
| 1001 | function updateStats() { |
| 1002 | document.getElementById('totalStreams').textContent = streams.length; |
| 1003 | document.getElementById('activeStreams').textContent = streams.length; |
| 1004 | |
| 1005 | const categories = [...new Set(streams.map(s => s.sport))]; |
| 1006 | document.getElementById('totalCategories').textContent = categories.length; |
| 1007 | |
| 1008 | const totalChannels = streams.reduce((total, stream) => total + stream.channels.length, 0); |
| 1009 | document.getElementById('totalChannels').textContent = totalChannels; |
| 1010 | } |
| 1011 | |
| 1012 | // Add Stream |
| 1013 | document.getElementById('saveStreamBtn').addEventListener('click', async () => { |
| 1014 | const sport = document.getElementById('streamSport').value; |
| 1015 | const tournament = document.getElementById('streamTournament').value; |
| 1016 | const match = document.getElementById('streamMatch').value; |
| 1017 | const channelInputs = document.querySelectorAll('.channel-input'); |
| 1018 | const channels = Array.from(channelInputs).map(input => input.value).filter(url => url.trim() !== ''); |
| 1019 | |
| 1020 | if (!sport || !tournament || !match || channels.length === 0) { |
| 1021 | alert('Please fill all fields and add at least one channel URL'); |
| 1022 | return; |
| 1023 | } |
| 1024 | |
| 1025 | try { |
| 1026 | if (!currentUser) { |
| 1027 | alert('User not authenticated. Please log in again.'); |
| 1028 | return; |
| 1029 | } |
| 1030 | await addDoc(collection(db, 'streams'), { |
| 1031 | sport, |
| 1032 | tournament, |
| 1033 | match, |
| 1034 | channels, |
| 1035 | createdAt: new Date(), |
| 1036 | createdBy: currentUser.uid |
| 1037 | }); |
| 1038 | |
| 1039 | // Close modal and refresh |
| 1040 | const modal = bootstrap.Modal.getInstance(document.getElementById('addStreamModal')); |
| 1041 | modal.hide(); |
| 1042 | |
| 1043 | // Reset form |
| 1044 | document.getElementById('addStreamForm').reset(); |
| 1045 | resetChannelInputs(); |
| 1046 | |
| 1047 | loadStreams(); |
| 1048 | alert('Stream added successfully!'); |
| 1049 | } catch (error) { |
| 1050 | console.error('Error adding stream:', error); |
| 1051 | alert('Error adding stream. Please try again.'); |
| 1052 | } |
| 1053 | }); |
| 1054 | |
| 1055 | // Edit Stream |
| 1056 | window.editStream = async (streamId) => { |
| 1057 | const stream = streams.find(s => s.id === streamId); |
| 1058 | if (!stream) return; |
| 1059 | |
| 1060 | document.getElementById('editStreamId').value = streamId; |
| 1061 | document.getElementById('editStreamSport').value = stream.sport; |
| 1062 | document.getElementById('editStreamTournament').value = stream.tournament; |
| 1063 | document.getElementById('editStreamMatch').value = stream.match; |
| 1064 | |
| 1065 | // Populate channels |
| 1066 | const editChannelInputs = document.getElementById('editChannelInputs'); |
| 1067 | editChannelInputs.innerHTML = ''; |
| 1068 | |
| 1069 | stream.channels.forEach((channel, index) => { |
| 1070 | const inputGroup = document.createElement('div'); |
| 1071 | inputGroup.className = 'input-group mb-2'; |
| 1072 | inputGroup.innerHTML = ` |
| 1073 | <input type="url" class="form-control form-control-dark edit-channel-input" value="${channel}" required> |
| 1074 | <button type="button" class="btn btn-outline-danger" onclick="removeEditChannelInput(this)"> |
| 1075 | <i class="fas fa-times"></i> |
| 1076 | </button> |
| 1077 | `; |
| 1078 | editChannelInputs.appendChild(inputGroup); |
| 1079 | }); |
| 1080 | |
| 1081 | // Add empty input for new channels |
| 1082 | const inputGroup = document.createElement('div'); |
| 1083 | inputGroup.className = 'input-group mb-2'; |
| 1084 | inputGroup.innerHTML = ` |
| 1085 | <input type="url" class="form-control form-control-dark edit-channel-input" placeholder="Add new channel URL"> |
| 1086 | <button type="button" class="btn btn-outline-success" onclick="addEditChannelInput()"> |
| 1087 | <i class="fas fa-plus"></i> |
| 1088 | </button> |
| 1089 | `; |
| 1090 | editChannelInputs.appendChild(inputGroup); |
| 1091 | |
| 1092 | const modal = new bootstrap.Modal(document.getElementById('editStreamModal')); |
| 1093 | modal.show(); |
| 1094 | }; |
| 1095 | |
| 1096 | // Update Stream |
| 1097 | document.getElementById('updateStreamBtn').addEventListener('click', async () => { |
| 1098 | const streamId = document.getElementById('editStreamId').value; |
| 1099 | const sport = document.getElementById('editStreamSport').value; |
| 1100 | const tournament = document.getElementById('editStreamTournament').value; |
| 1101 | const match = document.getElementById('editStreamMatch').value; |
| 1102 | const channelInputs = document.querySelectorAll('.edit-channel-input'); |
| 1103 | const channels = Array.from(channelInputs).map(input => input.value).filter(url => url.trim() !== ''); |
| 1104 | |
| 1105 | if (!sport || !tournament || !match || channels.length === 0) { |
| 1106 | alert('Please fill all fields and add at least one channel URL'); |
| 1107 | return; |
| 1108 | } |
| 1109 | |
| 1110 | try { |
| 1111 | await updateDoc(doc(db, 'streams', streamId), { |
| 1112 | sport, |
| 1113 | tournament, |
| 1114 | match, |
| 1115 | channels, |
| 1116 | updatedAt: new Date() |
| 1117 | }); |
| 1118 | |
| 1119 | const modal = bootstrap.Modal.getInstance(document.getElementById('editStreamModal')); |
| 1120 | modal.hide(); |
| 1121 | |
| 1122 | loadStreams(); |
| 1123 | alert('Stream updated successfully!'); |
| 1124 | } catch (error) { |
| 1125 | console.error('Error updating stream:', error); |
| 1126 | alert('Error updating stream. Please try again.'); |
| 1127 | } |
| 1128 | }); |
| 1129 | |
| 1130 | // Delete Stream |
| 1131 | window.deleteStream = async (streamId) => { |
| 1132 | if (!confirm('Are you sure you want to delete this stream?')) return; |
| 1133 | |
| 1134 | try { |
| 1135 | await deleteDoc(doc(db, 'streams', streamId)); |
| 1136 | loadStreams(); |
| 1137 | alert('Stream deleted successfully!'); |
| 1138 | } catch (error) { |
| 1139 | console.error('Error deleting stream:', error); |
| 1140 | alert('Error deleting stream. Please try again.'); |
| 1141 | } |
| 1142 | }; |
| 1143 | |
| 1144 | // Channel Input Management |
| 1145 | window.addChannelInput = () => { |
| 1146 | const channelInputs = document.getElementById('channelInputs'); |
| 1147 | const inputGroup = document.createElement('div'); |
| 1148 | inputGroup.className = 'input-group mb-2'; |
| 1149 | inputGroup.innerHTML = ` |
| 1150 | <input type="url" class="form-control form-control-dark channel-input" placeholder="https://example.com/channel/"> |
| 1151 | <button type="button" class="btn btn-outline-danger" onclick="removeChannelInput(this)"> |
| 1152 | <i class="fas fa-times"></i> |
| 1153 | </button> |
| 1154 | `; |
| 1155 | channelInputs.appendChild(inputGroup); |
| 1156 | }; |
| 1157 | |
| 1158 | window.removeChannelInput = (button) => { |
| 1159 | button.parentElement.remove(); |
| 1160 | }; |
| 1161 | |
| 1162 | window.addEditChannelInput = () => { |
| 1163 | const editChannelInputs = document.getElementById('editChannelInputs'); |
| 1164 | const inputGroup = document.createElement('div'); |
| 1165 | inputGroup.className = 'input-group mb-2'; |
| 1166 | inputGroup.innerHTML = ` |
| 1167 | <input type="url" class="form-control form-control-dark edit-channel-input" placeholder="https://example.com/channel/"> |
| 1168 | <button type="button" class="btn btn-outline-danger" onclick="removeEditChannelInput(this)"> |
| 1169 | <i class="fas fa-times"></i> |
| 1170 | </button> |
| 1171 | `; |
| 1172 | editChannelInputs.appendChild(inputGroup); |
| 1173 | }; |
| 1174 | |
| 1175 | window.removeEditChannelInput = (button) => { |
| 1176 | button.parentElement.remove(); |
| 1177 | }; |
| 1178 | |
| 1179 | function resetChannelInputs() { |
| 1180 | const channelInputs = document.getElementById('channelInputs'); |
| 1181 | channelInputs.innerHTML = ` |
| 1182 | <div class="input-group mb-2"> |
| 1183 | <input type="url" class="form-control form-control-dark channel-input" placeholder="https://example.com/channel/1" required> |
| 1184 | <button type="button" class="btn btn-outline-success" onclick="addChannelInput()"> |
| 1185 | <i class="fas fa-plus"></i> |
| 1186 | </button> |
| 1187 | </div> |
| 1188 | `; |
| 1189 | } |
| 1190 | |
| 1191 | // Sidebar toggle for mobile |
| 1192 | document.getElementById('toggleSidebar').addEventListener('click', () => { |
| 1193 | const sidebar = document.querySelector('.sidebar'); |
| 1194 | sidebar.classList.toggle('show'); |
| 1195 | }); |
| 1196 | |
| 1197 | // Initialize app |
| 1198 | document.addEventListener('DOMContentLoaded', () => { |
| 1199 | // Check authentication state |
| 1200 | onAuthStateChanged(auth, (user) => { |
| 1201 | if (user) { |
| 1202 | currentUser = user; |
| 1203 | showAdminPanel(); |
| 1204 | loadStreams(); |
| 1205 | } else { |
| 1206 | currentUser = null; |
| 1207 | showAuthPage(); |
| 1208 | } |
| 1209 | }); |
| 1210 | }); |
| 1211 | </script> |
| 1212 | </body> |
| 1213 | </html> |