- commit
- 501a586
- parent
- d26c63f
- author
- Neale Pickett
- date
- 2025-07-01 13:02:49 -0600 MDT
Add status checker web page
1 files changed,
+208,
-0
+208,
-0
1@@ -0,0 +1,208 @@
2+<!DOCTYPE html>
3+<html lang="en">
4+<head>
5+ <meta charset="UTF-8">
6+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7+ <title>Betsy Button Status Checker</title>
8+ <style>
9+ /* Custom font for a clean look */
10+ body {
11+ font-family: "Inter", sans-serif;
12+ background-color: #f0f4f8; /* Light gray background */
13+ display: flex;
14+ flex-direction: column; /* Arrange content vertically */
15+ justify-content: center;
16+ align-items: center;
17+ min-height: 100vh; /* Full viewport height */
18+ margin: 0;
19+ padding: 1rem; /* Add some padding for small screens */
20+ box-sizing: border-box;
21+ color: #333; /* Default text color */
22+ }
23+
24+ h1 {
25+ font-size: 2rem; /* Larger heading */
26+ font-weight: bold;
27+ margin-bottom: 1.5rem;
28+ text-align: center;
29+ color: #2c3e50; /* Darker heading color */
30+ }
31+
32+ /* Input container styling */
33+ div.w-full { /* Re-using a class name from previous version, adjust if needed */
34+ width: 100%;
35+ max-width: 300px; /* Limit width for input */
36+ margin-bottom: 1.5rem;
37+ text-align: center;
38+ }
39+
40+ label {
41+ display: block;
42+ font-size: 0.875rem; /* text-sm */
43+ font-weight: 500; /* font-medium */
44+ margin-bottom: 0.25rem;
45+ color: #555; /* gray-700 */
46+ }
47+
48+ input[type="text"] {
49+ display: block;
50+ width: 100%;
51+ padding: 0.5rem 1rem; /* px-4 py-2 */
52+ border: 1px solid #ccc; /* border-gray-300 */
53+ border-radius: 0.375rem; /* rounded-md */
54+ box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); /* shadow-sm */
55+ font-size: 0.875rem; /* sm:text-sm */
56+ transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
57+ }
58+
59+ input[type="text"]:focus {
60+ outline: none;
61+ border-color: #3b82f6; /* focus:border-blue-500 */
62+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.5); /* focus:ring-blue-500 focus:ring-offset-2 */
63+ }
64+
65+ /* Keyframe animations for blinking and pulsing */
66+ @keyframes expired {
67+ 0%, 12.5%, 50%, 67.5%, 75% { opacity: 1.00; } /* On states */
68+ 62.5%, 87.5%, 100% { opacity: 0.20; } /* Off states */
69+ }
70+
71+ @keyframes pulse {
72+ 0%, 50%, 100% { opacity: 0.8; }
73+ 25%, 75% { opacity: 1.0; }
74+ 12.5%, 37.5%, 62.5%, 87.5% { opacity: 0.9; }
75+ }
76+
77+ /* Container for the light */
78+ .no-light {
79+ background-color: black;
80+ border-radius: 50%; /* Make it a circle */
81+ display: flex;
82+ justify-content: center;
83+ align-items: center;
84+ height: 45vh; /* Responsive height */
85+ width: 45vh; /* Responsive width, maintains aspect ratio */
86+ max-height: 200px; /* Max size for larger screens */
87+ max-width: 200px;
88+ margin: 2rem auto; /* Center with margin */
89+ box-shadow: inset 0 0 15px rgba(0, 0, 0, 0.5); /* Inner shadow for depth */
90+ }
91+
92+ /* The actual light element */
93+ .light {
94+ height: 80%; /* Takes up 80% of its parent (.no-light) */
95+ width: 80%; /* Takes up 80% of its parent (.no-light) */
96+ border-radius: 50%; /* Make it a circle */
97+ background-color: red; /* Default color for 'expired' */
98+ animation: expired 2s infinite; /* Default animation */
99+ }
100+
101+ /* Style for 'OK' status */
102+ .light.ok {
103+ background-color: green; /* Green color for 'OK' */
104+ animation: pulse 2s infinite; /* Pulse animation for 'OK' */
105+ }
106+
107+ /* Hidden utility class */
108+ .hidden {
109+ display: none;
110+ }
111+
112+ /* Error message styling */
113+ p.error {
114+ color: #ef4444; /* text-red-500 */
115+ font-size: 0.875rem; /* text-sm */
116+ margin-top: 1rem;
117+ text-align: center;
118+ }
119+ </style>
120+</head>
121+<body>
122+ <div>
123+ <h1>Betsy Button Status Checker</h1>
124+
125+ <!-- Input for Button ID -->
126+ <div>
127+ <label for="id">
128+ Button ID:
129+ </label>
130+ <input name="id" placeholder="identifier" maxlength="40" required>
131+ </div>
132+
133+ <div class="no-light">
134+ <div class="light"></div>
135+ </div>
136+ <p class="error hidden">Error fetching status.</p>
137+ </div>
138+
139+ <script>
140+ // Get references to DOM elements
141+ const buttonIdInput = document.querySelector('[name=id]');
142+ const statusLight = document.querySelector('.light');
143+ const errorMessageDisplay = document.querySelector(".error");
144+
145+ // Key for local storage
146+ const LOCAL_STORAGE_KEY = 'buttonStatusCheckerId';
147+ // Interval for fetching status (e.g., every 10 seconds as per your code)
148+ const FETCH_INTERVAL = 10 * 1000; // milliseconds
149+
150+ // Function to fetch the button status from the API
151+ async function fetchButtonStatus() {
152+ try {
153+ // Hide any previous error messages
154+ errorMessageDisplay.classList.add('hidden');
155+
156+ let id = buttonIdInput.value;
157+ // Construct the API URL using the button ID
158+ // Note: This assumes 'state/' is a relative path from where this HTML is served.
159+ // If it's an absolute path, you'd need to add the full domain.
160+ let resp = await fetch(`state/${id}`);
161+
162+ switch (resp.status) {
163+ case 200:
164+ // If status is 200 (OK), add 'ok' class to apply green color and pulse animation
165+ statusLight.classList.add("ok");
166+ break;
167+ case 404:
168+ // If status is 404 (Not Found), remove 'ok' class to apply red color and expired animation
169+ statusLight.classList.remove("ok");
170+ break;
171+ default:
172+ // For any other status, remove 'ok' class and throw an error
173+ statusLight.classList.remove("ok");
174+ throw new Error(`HTTP error! status: ${resp.status}`);
175+ }
176+ } catch (error) {
177+ console.error('Error fetching button status:', error);
178+ // Show error message to the user
179+ errorMessageDisplay.classList.remove('hidden');
180+ // Ensure the light stops pulsing/blinking and shows error state
181+ statusLight.classList.remove('ok'); // Ensure it's red/expired on error
182+ }
183+ }
184+
185+ // --- Initialize on page load ---
186+ document.addEventListener('DOMContentLoaded', () => {
187+ // Load saved button ID from local storage
188+ const savedButtonId = localStorage.getItem(LOCAL_STORAGE_KEY);
189+ if (savedButtonId) {
190+ buttonIdInput.value = savedButtonId;
191+ }
192+
193+ // Immediately fetch the status when the page loads
194+ fetchButtonStatus();
195+
196+ // Set up an interval to fetch the status periodically
197+ setInterval(fetchButtonStatus, FETCH_INTERVAL);
198+ });
199+
200+ // --- Event Listener for Button ID Input ---
201+ // Save button ID to local storage when input changes
202+ buttonIdInput.addEventListener('input', () => {
203+ localStorage.setItem(LOCAL_STORAGE_KEY, buttonIdInput.value);
204+ // Re-fetch status immediately when the ID changes to reflect the new button's state
205+ fetchButtonStatus();
206+ });
207+ </script>
208+</body>
209+</html>