File size: 6,954 Bytes
ba1e29d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
<!DOCTYPE html>
<html lang="sw">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="theme-color" content="#0e1116">
<meta name="robots" content="noindex">
<title>Weka Nenosiri Jipya — JAYNES MAX</title>
<style>
  :root{--bg:#0e1116;--card:#161a21;--text:#f5f6f8;--muted:#9aa3af;--accent:#e50914;--accent2:#c7b8ff;--border:#242a33;}
  *{box-sizing:border-box;margin:0;padding:0;}
  html,body{height:100%;}
  body{background:var(--bg);color:var(--text);font-family:-apple-system,Roboto,Segoe UI,Helvetica,Arial,sans-serif;
    display:flex;align-items:center;justify-content:center;padding:20px;}
  .card{width:100%;max-width:400px;background:var(--card);border:1px solid var(--border);border-radius:18px;padding:26px 22px;}
  .brand{font-weight:800;font-size:18px;text-align:center;margin-bottom:6px;}
  .brand span{color:var(--accent);}
  h1{font-size:19px;text-align:center;margin-bottom:6px;}
  .sub{font-size:13px;color:var(--muted);text-align:center;margin-bottom:20px;line-height:1.45;}
  label{display:block;font-size:12px;color:var(--muted);margin:0 0 6px 2px;}
  .field{position:relative;margin-bottom:14px;}
  input{width:100%;padding:13px 44px 13px 14px;border-radius:12px;border:1px solid var(--border);
    background:var(--bg);color:var(--text);font-size:15px;outline:none;}
  input:focus{border-color:var(--accent2);}
  .eye{position:absolute;right:6px;bottom:6px;width:36px;height:36px;border:none;background:none;color:var(--muted);font-size:17px;cursor:pointer;}
  .track{height:4px;border-radius:999px;background:rgba(255,255,255,.08);overflow:hidden;margin:-6px 0 4px;}
  .fill{height:100%;width:0;border-radius:999px;background:#ff5252;transition:width .25s,background .25s;}
  .strength{font-size:11px;color:var(--muted);text-align:right;min-height:1.3em;margin-bottom:12px;}
  .btn{width:100%;padding:13px;border:none;border-radius:999px;background:var(--accent2);color:#1b1230;
    font-weight:700;font-size:15px;cursor:pointer;display:block;text-align:center;text-decoration:none;}
  .btn:disabled{opacity:.55;cursor:default;}
  .msg{font-size:13px;text-align:center;margin-top:14px;min-height:1.3em;line-height:1.4;}
  .err{color:#ff8a80;} .ok{color:#5ee6a0;}
  .big{font-size:44px;text-align:center;margin-bottom:10px;}
  .hide{display:none;}
</style>
</head>
<body>
<div class="card">
  <div class="brand">JAYNES <span>MAX</span></div>

  <!-- Hali: kiungo kibaya / kimeisha muda -->
  <div id="bad" class="hide">
    <div class="big">⚠️</div>
    <h1>Kiungo hakifanyi kazi</h1>
    <p class="sub" id="badText">Kiungo hiki si sahihi au kimeisha muda. Omba kingine kupitia "Umesahau nenosiri?" kwenye app.</p>
    <a class="btn" href="index.html">Rudi kwenye app</a>
  </div>

  <!-- Fomu -->
  <div id="form" class="hide">
    <h1>Weka nenosiri jipya</h1>
    <p class="sub">Andika nenosiri jipya la akaunti yako.</p>
    <div class="field">
      <label for="p1">Nenosiri jipya</label>
      <input id="p1" type="password" autocomplete="new-password" placeholder="Angalau herufi 8">
      <button class="eye" type="button" data-for="p1" aria-label="Onyesha nenosiri">👁</button>
    </div>
    <div class="track"><div class="fill" id="fill"></div></div>
    <div class="strength" id="strength"></div>
    <div class="field">
      <label for="p2">Rudia nenosiri</label>
      <input id="p2" type="password" autocomplete="new-password" placeholder="Rudia nenosiri">
      <button class="eye" type="button" data-for="p2" aria-label="Onyesha nenosiri">👁</button>
    </div>
    <button class="btn" id="save">Badilisha nenosiri</button>
    <div class="msg" id="msg"></div>
  </div>

  <!-- Imefanikiwa -->
  <div id="done" class="hide">
    <div class="big">✅</div>
    <h1>Nenosiri limebadilishwa</h1>
    <p class="sub">Sasa unaweza kuingia kwa nenosiri lako jipya.</p>
    <a class="btn" href="index.html">Ingia kwenye JAYNES MAX</a>
  </div>
</div>

<script src="config.js"></script>
<script>
(function(){
  const $ = id => document.getElementById(id);
  const AUTH = JM_CONFIG.PROFILE_URL + '/auth/v1';
  const KEY = JM_CONFIG.PROFILE_KEY;
  const show = id => { ['bad','form','done'].forEach(x => $(x).classList.toggle('hide', x !== id)); };

  // Supabase huweka token kwenye hash ya kiungo: #access_token=...&type=recovery
  const params = new URLSearchParams(location.hash.replace(/^#/, ''));
  const token = params.get('access_token');
  const type = params.get('type');
  const errCode = params.get('error_code');
  try { history.replaceState(null, '', location.pathname); } catch (_) {}  // ficha token kwenye URL

  if (errCode === 'otp_expired') {
    $('badText').textContent = 'Kiungo kimeisha muda au kimeshatumika. Omba kingine kupitia "Umesahau nenosiri?" kwenye app.';
    show('bad');
  } else if (!token || type !== 'recovery') {
    show('bad');
  } else {
    show('form');
  }

  document.querySelectorAll('.eye').forEach(b => b.onclick = () => {
    const i = $(b.dataset.for); i.type = i.type === 'password' ? 'text' : 'password';
  });

  $('p1').addEventListener('input', () => {
    const v = $('p1').value;
    let s = 0;
    if (v.length >= 8) s++;
    if (v.length >= 12) s++;
    if (/[a-z]/.test(v) && /[A-Z]/.test(v)) s++;
    if (/\d/.test(v)) s++;
    if (/[^A-Za-z0-9]/.test(v)) s++;
    if (!v) s = 0;
    const lv = [
      ['0%', '#ff5252', ''], ['25%', '#ff5252', 'Dhaifu'], ['45%', '#ffb74d', 'Wastani'],
      ['70%', '#ffd54f', 'Nzuri'], ['90%', '#9ccc65', 'Imara'], ['100%', '#5ee6a0', 'Imara sana']
    ][s];
    $('fill').style.width = lv[0]; $('fill').style.background = lv[1]; $('strength').textContent = lv[2];
  });

  const msg = (t, cls) => { $('msg').textContent = t; $('msg').className = 'msg ' + (cls || ''); };

  $('save').onclick = async () => {
    const a = $('p1').value, b = $('p2').value;
    if (a.length < 8) return msg('Nenosiri liwe angalau herufi 8.', 'err');
    if (a !== b) return msg('Manenosiri hayalingani.', 'err');
    $('save').disabled = true; msg('Subiri…');
    try {
      const res = await fetch(AUTH + '/user', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json', 'apikey': KEY, 'Authorization': 'Bearer ' + token },
        body: JSON.stringify({ password: a })
      });
      const d = await res.json().catch(() => ({}));
      if (!res.ok) throw new Error(d.msg || d.error_description || d.error_code || 'Imeshindikana');
      show('done');
    } catch (e) {
      const m = String(e.message || '');
      msg(/same|different/i.test(m) ? 'Nenosiri jipya lazima lisiwe sawa na la zamani.'
        : /expired|invalid|jwt/i.test(m) ? 'Kiungo kimeisha muda. Omba kingine kwenye app.'
        : 'Imeshindikana: ' + m, 'err');
      $('save').disabled = false;
    }
  };
  [$('p1'), $('p2')].forEach(i => i.addEventListener('keydown', e => { if (e.key === 'Enter') $('save').click(); }));
})();
</script>
</body>
</html>