Repository navigation
Expand file tree
/
Copy pathflags-page.js
More file actions
96 lines (84 loc) · 3.32 KB
/
Copy pathflags-page.js
File metadata and controls
96 lines (84 loc) · 3.32 KB
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
// ── Feature flags admin page ──────────────────────────────────
//
// Change this passphrase to whatever you like.
const PASSPHRASE = 'luke';
const lockScreen = document.getElementById('lock-screen');
const adminPanel = document.getElementById('admin-panel');
const lockForm = document.getElementById('lock-form');
const lockInput = document.getElementById('lock-input');
const lockError = document.getElementById('lock-error');
const flagsList = document.getElementById('flags-list');
const resetBtn = document.getElementById('reset-btn');
// ── Auth ──────────────────────────────────────────────────────
// Session-only: unlocked state is not persisted between visits.
let unlocked = false;
lockForm.addEventListener('submit', (e) => {
e.preventDefault();
if (lockInput.value === PASSPHRASE) {
unlock();
} else {
lockError.hidden = false;
lockInput.value = '';
lockInput.focus();
lockInput.classList.add('shake');
lockInput.addEventListener('animationend', () => {
lockInput.classList.remove('shake');
}, { once: true });
}
});
function unlock() {
unlocked = true;
lockScreen.hidden = true;
adminPanel.hidden = false;
renderFlags();
}
// ── Render ────────────────────────────────────────────────────
function renderFlags() {
flagsList.innerHTML = '';
const flags = Flags.getAll();
if (flags.length === 0) {
flagsList.innerHTML = '<li class="flags-empty">No flags defined yet.</li>';
return;
}
flags.forEach(flag => {
const li = document.createElement('li');
li.className = 'flag-row';
const id = `flag-${flag.key}`;
li.innerHTML = `
<div class="flag-info">
<label class="flag-label" for="${id}">${flag.label}</label>
<p class="flag-desc">${flag.description}</p>
</div>
<div class="flag-control">
<span class="flag-status ${flag.value ? 'on' : 'off'}">${flag.value ? 'On' : 'Off'}</span>
<button
id="${id}"
class="flag-toggle ${flag.value ? 'enabled' : ''}"
role="switch"
aria-checked="${flag.value}"
aria-label="Toggle ${flag.label}"
>
<span class="flag-toggle-thumb"></span>
</button>
</div>
`;
li.querySelector('.flag-toggle').addEventListener('click', (e) => {
const btn = e.currentTarget;
const newValue = btn.getAttribute('aria-checked') !== 'true';
Flags.set(flag.key, newValue);
btn.setAttribute('aria-checked', newValue);
btn.classList.toggle('enabled', newValue);
const status = li.querySelector('.flag-status');
status.textContent = newValue ? 'On' : 'Off';
status.className = `flag-status ${newValue ? 'on' : 'off'}`;
});
flagsList.appendChild(li);
});
}
// ── Reset ─────────────────────────────────────────────────────
resetBtn.addEventListener('click', () => {
if (confirm('Reset all flags to their default values?')) {
Flags.resetAll();
renderFlags();
}
});