web / aura.html
everydaycats's picture
Update aura.html
a2f19dd verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Aura Admin Dashboard</title>
<style>
:root {
--bg: #0d0d0d;
--surface: #1a1a1a;
--border: #333;
--primary: #CC75FF;
--text: #fff;
--text-sec: #aaa;
--danger: #FF3B30;
--success: #12D8C3;
}
body {
margin: 0;
padding: 40px 20px;
background-color: var(--bg);
color: var(--text);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
.container {
max-width: 800px;
margin: 0 auto;
}
.header {
display: flex;
align-items: center;
margin-bottom: 30px;
}
.header h1 {
color: var(--primary);
margin: 0;
font-size: 28px;
}
.card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 16px;
padding: 24px;
margin-bottom: 30px;
}
.card h2 {
margin-top: 0;
margin-bottom: 20px;
font-size: 20px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
color: var(--text-sec);
font-weight: 500;
font-size: 14px;
}
input[type="text"], textarea, input[type="file"] {
width: 100%;
padding: 14px;
background: var(--bg);
color: var(--text);
border: 1px solid var(--border);
border-radius: 10px;
box-sizing: border-box;
font-size: 15px;
}
textarea {
resize: vertical;
min-height: 100px;
font-family: inherit;
}
input[type="file"] {
padding: 10px;
color: var(--text-sec);
}
button {
background: var(--primary);
color: #000;
border: none;
padding: 14px 24px;
border-radius: 10px;
font-weight: bold;
cursor: pointer;
font-size: 16px;
transition: opacity 0.2s;
width: 100%;
}
button:hover {
opacity: 0.9;
}
button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
button.danger {
background: rgba(255, 59, 48, 0.15);
color: var(--danger);
width: auto;
padding: 10px 16px;
font-size: 14px;
}
button.danger:hover {
background: rgba(255, 59, 48, 0.25);
}
button.success {
background: rgba(18, 216, 195, 0.15);
color: var(--success);
width: auto;
padding: 10px 16px;
font-size: 14px;
}
button.success:hover {
background: rgba(18, 216, 195, 0.25);
}
.challenge-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px;
border: 1px solid var(--border);
border-radius: 12px;
margin-bottom: 12px;
background: var(--bg);
}
.challenge-info {
flex: 1;
margin: 0 20px;
}
.challenge-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 6px;
display: block;
}
.challenge-img {
width: 70px;
height: 70px;
border-radius: 8px;
object-fit: cover;
background: #222;
}
.meta-text {
font-size: 13px;
color: var(--text-sec);
}
.badge {
padding: 4px 8px;
border-radius: 6px;
font-size: 11px;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-right: 10px;
}
.badge.active {
background: rgba(18, 216, 195, 0.2);
color: var(--success);
border: 1px solid rgba(18, 216, 195, 0.3);
}
.badge.archived {
background: rgba(255, 255, 255, 0.05);
color: var(--text-sec);
border: 1px solid var(--border);
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Aura Grade Admin</h1>
</div>
<div class="card">
<h2>Create New Challenge</h2>
<form id="createForm">
<div class="form-group">
<label>Challenge Title</label>
<input type="text" id="title" placeholder="e.g. Rate the Fit" required>
</div>
<div class="form-group">
<label>Description</label>
<textarea id="description" placeholder="Explain what the sorcerers need to match..." required></textarea>
</div>
<div class="form-group">
<label>Reference Image</label>
<input type="file" id="imageFile" accept="image/*" required>
</div>
<button type="submit" id="submitBtn">Publish Challenge</button>
</form>
</div>
<div class="card">
<h2>Existing Challenges</h2>
<div id="challengesList">
<p style="color: var(--text-sec);">Loading challenges...</p>
</div>
</div>
</div>
<script>
// Update this to your deployed backend URL in production
// Example: const API_BASE = 'https://api.yourdomain.com/api/aurameasure';
const API_BASE = 'https://everydaycats-everything.hf.space/api/aurameasure';
document.getElementById('createForm').addEventListener('submit', async (e) => {
e.preventDefault();
const btn = document.getElementById('submitBtn');
btn.disabled = true;
btn.innerText = 'Deliberating with Spirits...';
try {
const title = document.getElementById('title').value;
const description = document.getElementById('description').value;
const fileInput = document.getElementById('imageFile');
if (!fileInput.files[0]) throw new Error('Please select an image.');
// Convert file to raw base64 (backend expects it without the data uri prefix)
const base64String = await new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
const base64 = reader.result.split(',')[1];
resolve(base64);
};
reader.onerror = error => reject(error);
reader.readAsDataURL(fileInput.files[0]);
});
const res = await fetch(`${API_BASE}/admin/challenges`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title, description, image_base64: base64String })
});
const json = await res.json();
if (!json.success) throw new Error(json.error || 'Failed to create challenge');
alert('Challenge created successfully!');
document.getElementById('createForm').reset();
fetchChallenges();
} catch (err) {
alert('Error: ' + err.message);
} finally {
btn.disabled = false;
btn.innerText = 'Publish Challenge';
}
});
async function fetchChallenges() {
const list = document.getElementById('challengesList');
try {
const res = await fetch(`${API_BASE}/admin/challenges`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const json = await res.json();
if (!json.success) throw new Error(json.error);
list.innerHTML = '';
if (json.data.length === 0) {
list.innerHTML = '<p style="color: var(--text-sec);">No challenges found.</p>';
return;
}
json.data.forEach(c => {
const isActive = !!c.session_id;
const row = document.createElement('div');
row.className = 'challenge-row';
row.innerHTML = `
<img src="${c.reference_image_url}" class="challenge-img" alt="thumb">
<div class="challenge-info">
<span class="challenge-title">${c.title}</span>
<span class="badge ${isActive ? 'active' : 'archived'}">${isActive ? 'Active' : 'Archived'}</span>
<span class="meta-text">Entries: ${c.participant_count || 0}</span>
</div>
<button class="${isActive ? 'danger' : 'success'}" onclick="toggleStatus('${c.id}', '${isActive ? 'archive' : 'activate'}')">
${isActive ? 'Archive' : 'Make Active'}
</button>
`;
list.appendChild(row);
});
} catch (err) {
list.innerHTML = `<p style="color: var(--danger);">Error loading challenges: ${err.message}</p>`;
}
}
async function toggleStatus(id, action) {
if (!confirm(`Are you sure you want to ${action} this challenge?`)) return;
try {
const res = await fetch(`${API_BASE}/admin/challenges/${id}/toggle`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action })
});
const json = await res.json();
if (!json.success) throw new Error(json.error);
fetchChallenges();
} catch (err) {
alert('Error: ' + err.message);
}
}
// Initialize dashboard
fetchChallenges();
</script>
</body>
</html>