Barkmaster commited on
Commit
2c7fd0d
Β·
verified Β·
1 Parent(s): acb6fce
Files changed (1) hide show
  1. index.html +47 -156
index.html CHANGED
@@ -188,18 +188,6 @@
188
  .modal-corner.bl { bottom: -1px; left: -1px; border-width: 0 0 2px 2px; }
189
  .modal-corner.br { bottom: -1px; right: -1px; border-width: 0 2px 2px 0; }
190
 
191
- .modal-close {
192
- position: absolute;
193
- top: 15px; right: 15px;
194
- background: transparent;
195
- border: none;
196
- color: var(--text-dim);
197
- font-size: 16px;
198
- cursor: pointer;
199
- transition: color .2s;
200
- }
201
- .modal-close:hover { color: var(--accent2); }
202
-
203
  .modal-title {
204
  font-family: 'Orbitron', sans-serif;
205
  font-size: 18px;
@@ -269,22 +257,16 @@
269
  display: none;
270
  }
271
 
272
- .auth-toggle {
273
- text-align: center;
274
- margin-top: 20px;
275
- font-size: 11px;
276
- color: var(--text-dim);
277
- }
278
- .auth-toggle button {
279
  background: none; border: none;
280
- color: var(--accent);
 
281
  cursor: pointer;
282
- font-family: 'Share Tech Mono', monospace;
283
- letter-spacing: 1px;
284
- text-decoration: underline;
285
- margin-left: 5px;
286
  }
287
- .auth-toggle button:hover { color: #fff; }
288
 
289
  /* ── ANON BANNER ── */
290
  .anon-banner {
@@ -486,6 +468,7 @@
486
  .data-table tbody tr:hover { background: rgba(0,212,255,0.04); }
487
  .data-table tbody tr:last-child td { border-bottom: none; }
488
 
 
489
  .data-table tbody tr.own-row {
490
  background: rgba(0,212,255,0.06);
491
  border-left: 3px solid var(--accent);
@@ -636,32 +619,18 @@
636
  <div class="modal-corner bl"></div>
637
  <div class="modal-corner br"></div>
638
  <button class="modal-close" onclick="closeLoginModal()">βœ•</button>
639
-
640
- <div class="modal-title" id="modal-title-text">SIGN IN</div>
641
- <div class="modal-sub" id="modal-sub-text">ACCESS FULL COMBAT ANALYTICS</div>
642
-
643
- <div class="form-group" id="group-username" style="display:none;">
644
- <label>Operator Handle (Username)</label>
645
- <input type="text" id="login-username" placeholder="ShadowSniper99" />
646
- </div>
647
-
648
  <div class="form-group">
649
  <label>Email Address</label>
650
  <input type="email" id="login-email" placeholder="operator@warzone.com" />
651
  </div>
652
-
653
  <div class="form-group">
654
  <label>Password</label>
655
  <input type="password" id="login-password" placeholder="β€’β€’β€’β€’β€’β€’β€’β€’" />
656
  </div>
657
-
658
- <button class="modal-submit" id="login-submit" onclick="submitAuth()">AUTHENTICATE</button>
659
  <div class="modal-error" id="login-error"></div>
660
-
661
- <div class="auth-toggle">
662
- <span id="auth-toggle-text">Don't have clearance?</span>
663
- <button id="auth-toggle-btn" onclick="toggleAuthMode()">REGISTER NEW OPERATOR</button>
664
- </div>
665
  </div>
666
  </div>
667
 
@@ -830,8 +799,7 @@ const SUPABASE_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZ
830
 
831
  let players = [];
832
  let charts = {};
833
- let authUser = null;
834
- let isLoginMode = true;
835
 
836
  // ── AUTH STATE ─────────────────────────────────────────────────────
837
  function isLoggedIn() { return authUser !== null; }
@@ -863,156 +831,77 @@ function applyAuthState() {
863
  if (loggedIn) renderMyStats();
864
  }
865
 
866
- // ── LOGIN / REGISTER MODAL ─────────────────────────────────────────
867
  function openLoginModal() {
868
  document.getElementById('login-modal').classList.add('active');
869
- setTimeout(() => {
870
- if(isLoginMode) document.getElementById('login-email').focus();
871
- else document.getElementById('login-username').focus();
872
- }, 300);
873
  }
874
-
875
  function closeLoginModal() {
876
  document.getElementById('login-modal').classList.remove('active');
877
  }
878
-
879
  document.getElementById('login-modal').addEventListener('click', e => {
880
  if (e.target === e.currentTarget) closeLoginModal();
881
  });
882
  document.addEventListener('keydown', e => {
883
  if (e.key === 'Escape') closeLoginModal();
884
- if (e.key === 'Enter' && document.getElementById('login-modal').classList.contains('active')) submitAuth();
885
  });
886
 
887
- function toggleAuthMode() {
888
- isLoginMode = !isLoginMode;
889
- const title = document.getElementById('modal-title-text');
890
- const sub = document.getElementById('modal-sub-text');
891
- const userGroup = document.getElementById('group-username');
892
- const submitBtn = document.getElementById('login-submit');
893
- const toggleText = document.getElementById('auth-toggle-text');
894
- const toggleBtn = document.getElementById('auth-toggle-btn');
895
- const errEl = document.getElementById('login-error');
896
-
897
- errEl.style.display = 'none';
898
-
899
- if (isLoginMode) {
900
- title.textContent = 'SIGN IN';
901
- sub.textContent = 'ACCESS FULL COMBAT ANALYTICS';
902
- userGroup.style.display = 'none';
903
- submitBtn.textContent = 'AUTHENTICATE';
904
- toggleText.textContent = "Don't have clearance?";
905
- toggleBtn.textContent = "REGISTER NEW OPERATOR";
906
- } else {
907
- title.textContent = 'ENLIST';
908
- sub.textContent = 'CREATE YOUR OPERATOR PROFILE';
909
- userGroup.style.display = 'block';
910
- submitBtn.textContent = 'INITIALIZE ACCOUNT';
911
- toggleText.textContent = "Already enlisted?";
912
- toggleBtn.textContent = "SIGN IN HERE";
913
- }
914
- }
915
-
916
- async function submitAuth() {
917
  const email = document.getElementById('login-email').value.trim();
918
  const password = document.getElementById('login-password').value;
919
- const username = document.getElementById('login-username').value.trim();
920
  const errEl = document.getElementById('login-error');
921
  const btn = document.getElementById('login-submit');
922
 
923
  errEl.style.display = 'none';
924
-
925
  if (!email || !password) {
926
  errEl.textContent = '⚠ EMAIL AND PASSWORD REQUIRED';
927
  errEl.style.display = 'block';
928
  return;
929
  }
930
- if (!isLoginMode && !username) {
931
- errEl.textContent = '⚠ OPERATOR HANDLE REQUIRED';
932
- errEl.style.display = 'block';
933
- return;
934
- }
935
 
936
  btn.disabled = true;
937
- btn.textContent = 'PROCESSING...';
938
 
939
  try {
940
- let res, data;
941
-
942
- if (isLoginMode) {
943
- res = await fetch(`${SUPABASE_URL}/auth/v1/token?grant_type=password`, {
944
- method: 'POST',
945
- headers: { 'apikey': SUPABASE_KEY, 'Content-Type': 'application/json' },
946
- body: JSON.stringify({ email, password })
947
- });
948
- data = await res.json();
949
- if (!res.ok) throw new Error(data.error_description || data.msg || 'Login failed');
950
-
951
- const userId = data.user?.id;
952
- let fetchedUsername = data.user?.email;
953
- try {
954
- const statsRes = await fetch(
955
- `${SUPABASE_URL}/rest/v1/player_stats?id=eq.${userId}&select=username`,
956
- { headers: { 'apikey': SUPABASE_KEY, 'Authorization': `Bearer ${data.access_token}` } }
957
- );
958
- const statsData = await statsRes.json();
959
- if (statsData?.[0]?.username) fetchedUsername = statsData[0].username;
960
- } catch(_) {}
961
-
962
- authUser = { id: userId, email: data.user?.email, username: fetchedUsername, token: data.access_token };
963
-
964
- } else {
965
- res = await fetch(`${SUPABASE_URL}/auth/v1/signup`, {
966
  method: 'POST',
967
- headers: { 'apikey': SUPABASE_KEY, 'Content-Type': 'application/json' },
 
 
 
968
  body: JSON.stringify({ email, password })
969
- });
970
- data = await res.json();
971
- if (!res.ok) throw new Error(data.error_description || data.msg || 'Registration failed');
972
-
973
- const userId = data.user?.id;
974
- const token = data.session?.access_token || SUPABASE_KEY;
975
-
976
- const createRes = await fetch(`${SUPABASE_URL}/rest/v1/player_stats`, {
977
- method: 'POST',
978
- headers: {
979
- 'apikey': SUPABASE_KEY,
980
- 'Authorization': `Bearer ${token}`,
981
- 'Content-Type': 'application/json',
982
- 'Prefer': 'return=minimal'
983
- },
984
- body: JSON.stringify({
985
- id: userId,
986
- username: username,
987
- rank_points: 1000,
988
- kills: 0,
989
- deaths: 0,
990
- gold: 0,
991
- headshots: 0,
992
- level: 1,
993
- equipped_weapon: 'Pistol',
994
- equipped_armor: 'None'
995
- })
996
- });
997
-
998
- if (!createRes.ok) {
999
- console.warn("Failed to create player_stats row. Check RLS policies.");
1000
  }
 
1001
 
1002
- authUser = { id: userId, email: data.user?.email, username: username, token: token };
1003
- toggleAuthMode();
1004
- }
1005
-
 
 
 
 
 
 
 
 
 
 
 
 
1006
  closeLoginModal();
1007
  applyAuthState();
1008
- loadData();
1009
 
1010
  } catch (e) {
1011
  errEl.textContent = `⚠ ${e.message.toUpperCase()}`;
1012
  errEl.style.display = 'block';
1013
  } finally {
1014
  btn.disabled = false;
1015
- btn.textContent = isLoginMode ? 'AUTHENTICATE' : 'INITIALIZE ACCOUNT';
1016
  }
1017
  }
1018
 
@@ -1022,7 +911,6 @@ function doLogout() {
1022
  renderCharts();
1023
  document.getElementById('login-email').value = '';
1024
  document.getElementById('login-password').value = '';
1025
- document.getElementById('login-username').value = '';
1026
  }
1027
 
1028
  // ── FETCH DATA ─────────────────────────────────────────────────────
@@ -1137,7 +1025,7 @@ function destroyAll() { Object.values(charts).forEach(c => c.destroy()); charts
1137
 
1138
  function renderCharts() {
1139
  destroyAll();
1140
- if (!isLoggedIn()) return;
1141
 
1142
  Chart.defaults.color = '#4a7a9b';
1143
  Chart.defaults.font.family = "'Share Tech Mono', monospace";
@@ -1222,6 +1110,7 @@ function renderTable() {
1222
  const loggedIn = isLoggedIn();
1223
  const head = document.getElementById('leaderboard-head');
1224
 
 
1225
  if (loggedIn) {
1226
  head.innerHTML = `<tr>
1227
  <th>#</th><th>PLAYER</th><th>TIER</th><th>RANK PTS</th>
@@ -1242,6 +1131,7 @@ function renderTable() {
1242
  if (weaponFilter) data = data.filter(p => p.equipped_weapon === weaponFilter);
1243
  data.sort((a,b) => sortKey==='kd' ? b.kd-a.kd : (b[sortKey]||0)-(a[sortKey]||0));
1244
 
 
1245
  if (!loggedIn) data = data.slice(0, 10);
1246
 
1247
  const tbody = document.getElementById('leaderboard-body');
@@ -1287,6 +1177,7 @@ function renderTable() {
1287
  tbody.appendChild(tr);
1288
  });
1289
 
 
1290
  if (!loggedIn && players.length > 10) {
1291
  const tr = document.createElement('tr');
1292
  tr.innerHTML = `<td colspan="6" style="text-align:center;color:var(--text-dim);padding:16px;border-top:1px solid var(--border)">
 
188
  .modal-corner.bl { bottom: -1px; left: -1px; border-width: 0 0 2px 2px; }
189
  .modal-corner.br { bottom: -1px; right: -1px; border-width: 0 2px 2px 0; }
190
 
 
 
 
 
 
 
 
 
 
 
 
 
191
  .modal-title {
192
  font-family: 'Orbitron', sans-serif;
193
  font-size: 18px;
 
257
  display: none;
258
  }
259
 
260
+ .modal-close {
261
+ position: absolute;
262
+ top: 14px; right: 14px;
 
 
 
 
263
  background: none; border: none;
264
+ color: var(--text-dim);
265
+ font-size: 18px;
266
  cursor: pointer;
267
+ transition: color .2s;
 
 
 
268
  }
269
+ .modal-close:hover { color: var(--accent2); }
270
 
271
  /* ── ANON BANNER ── */
272
  .anon-banner {
 
468
  .data-table tbody tr:hover { background: rgba(0,212,255,0.04); }
469
  .data-table tbody tr:last-child td { border-bottom: none; }
470
 
471
+ /* Own player row highlight */
472
  .data-table tbody tr.own-row {
473
  background: rgba(0,212,255,0.06);
474
  border-left: 3px solid var(--accent);
 
619
  <div class="modal-corner bl"></div>
620
  <div class="modal-corner br"></div>
621
  <button class="modal-close" onclick="closeLoginModal()">βœ•</button>
622
+ <div class="modal-title">SIGN IN</div>
623
+ <div class="modal-sub">ACCESS FULL COMBAT ANALYTICS</div>
 
 
 
 
 
 
 
624
  <div class="form-group">
625
  <label>Email Address</label>
626
  <input type="email" id="login-email" placeholder="operator@warzone.com" />
627
  </div>
 
628
  <div class="form-group">
629
  <label>Password</label>
630
  <input type="password" id="login-password" placeholder="β€’β€’β€’β€’β€’β€’β€’β€’" />
631
  </div>
632
+ <button class="modal-submit" id="login-submit" onclick="doLogin()">AUTHENTICATE</button>
 
633
  <div class="modal-error" id="login-error"></div>
 
 
 
 
 
634
  </div>
635
  </div>
636
 
 
799
 
800
  let players = [];
801
  let charts = {};
802
+ let authUser = null; // { id, email, username, token }
 
803
 
804
  // ── AUTH STATE ─────────────────────────────────────────────────────
805
  function isLoggedIn() { return authUser !== null; }
 
831
  if (loggedIn) renderMyStats();
832
  }
833
 
834
+ // ── LOGIN MODAL ────────────────────────────────────────────────────
835
  function openLoginModal() {
836
  document.getElementById('login-modal').classList.add('active');
837
+ setTimeout(() => document.getElementById('login-email').focus(), 300);
 
 
 
838
  }
 
839
  function closeLoginModal() {
840
  document.getElementById('login-modal').classList.remove('active');
841
  }
 
842
  document.getElementById('login-modal').addEventListener('click', e => {
843
  if (e.target === e.currentTarget) closeLoginModal();
844
  });
845
  document.addEventListener('keydown', e => {
846
  if (e.key === 'Escape') closeLoginModal();
847
+ if (e.key === 'Enter' && document.getElementById('login-modal').classList.contains('active')) doLogin();
848
  });
849
 
850
+ async function doLogin() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
851
  const email = document.getElementById('login-email').value.trim();
852
  const password = document.getElementById('login-password').value;
 
853
  const errEl = document.getElementById('login-error');
854
  const btn = document.getElementById('login-submit');
855
 
856
  errEl.style.display = 'none';
 
857
  if (!email || !password) {
858
  errEl.textContent = '⚠ EMAIL AND PASSWORD REQUIRED';
859
  errEl.style.display = 'block';
860
  return;
861
  }
 
 
 
 
 
862
 
863
  btn.disabled = true;
864
+ btn.textContent = 'AUTHENTICATING...';
865
 
866
  try {
867
+ const res = await fetch(
868
+ `${SUPABASE_URL}/auth/v1/token?grant_type=password`,
869
+ {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
870
  method: 'POST',
871
+ headers: {
872
+ 'apikey': SUPABASE_KEY,
873
+ 'Content-Type': 'application/json'
874
+ },
875
  body: JSON.stringify({ email, password })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
876
  }
877
+ );
878
 
879
+ const data = await res.json();
880
+ if (!res.ok) throw new Error(data.error_description || data.msg || 'Login failed');
881
+
882
+ // Fetch username from player_stats
883
+ const userId = data.user?.id;
884
+ let username = data.user?.email;
885
+ try {
886
+ const statsRes = await fetch(
887
+ `${SUPABASE_URL}/rest/v1/player_stats?id=eq.${userId}&select=username`,
888
+ { headers: { 'apikey': SUPABASE_KEY, 'Authorization': `Bearer ${data.access_token}` } }
889
+ );
890
+ const statsData = await statsRes.json();
891
+ if (statsData?.[0]?.username) username = statsData[0].username;
892
+ } catch(_) {}
893
+
894
+ authUser = { id: userId, email: data.user?.email, username, token: data.access_token };
895
  closeLoginModal();
896
  applyAuthState();
897
+ renderCharts();
898
 
899
  } catch (e) {
900
  errEl.textContent = `⚠ ${e.message.toUpperCase()}`;
901
  errEl.style.display = 'block';
902
  } finally {
903
  btn.disabled = false;
904
+ btn.textContent = 'AUTHENTICATE';
905
  }
906
  }
907
 
 
911
  renderCharts();
912
  document.getElementById('login-email').value = '';
913
  document.getElementById('login-password').value = '';
 
914
  }
915
 
916
  // ── FETCH DATA ─────────────────────────────────────────────────────
 
1025
 
1026
  function renderCharts() {
1027
  destroyAll();
1028
+ if (!isLoggedIn()) return; // charts only render when logged in
1029
 
1030
  Chart.defaults.color = '#4a7a9b';
1031
  Chart.defaults.font.family = "'Share Tech Mono', monospace";
 
1110
  const loggedIn = isLoggedIn();
1111
  const head = document.getElementById('leaderboard-head');
1112
 
1113
+ // Anon: limited columns. Logged in: full columns
1114
  if (loggedIn) {
1115
  head.innerHTML = `<tr>
1116
  <th>#</th><th>PLAYER</th><th>TIER</th><th>RANK PTS</th>
 
1131
  if (weaponFilter) data = data.filter(p => p.equipped_weapon === weaponFilter);
1132
  data.sort((a,b) => sortKey==='kd' ? b.kd-a.kd : (b[sortKey]||0)-(a[sortKey]||0));
1133
 
1134
+ // Anonymous: top 10 only
1135
  if (!loggedIn) data = data.slice(0, 10);
1136
 
1137
  const tbody = document.getElementById('leaderboard-body');
 
1177
  tbody.appendChild(tr);
1178
  });
1179
 
1180
+ // If anon: add teaser row
1181
  if (!loggedIn && players.length > 10) {
1182
  const tr = document.createElement('tr');
1183
  tr.innerHTML = `<td colspan="6" style="text-align:center;color:var(--text-dim);padding:16px;border-top:1px solid var(--border)">