- veins → shunts rename - add cfg/standalone/ and cfg/<room>/ structure - remove old data/*.json (moved to cfg/<room>/data/) - update build.py and ctrl scripts
299 lines
12 KiB
HTML
299 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Amar API (MOCK) - Configuration</title>
|
|
<style>
|
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
background: #111827;
|
|
color: #e5e7eb;
|
|
padding: 20px;
|
|
}
|
|
.container { max-width: 1200px; margin: 0 auto; }
|
|
header {
|
|
background: #f59e0b;
|
|
color: #111827;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
margin-bottom: 24px;
|
|
}
|
|
h1 { font-size: 1.5rem; font-weight: 600; margin-bottom: 8px; }
|
|
.subtitle { opacity: 0.8; font-size: 0.875rem; }
|
|
.mock-badge {
|
|
display: inline-block;
|
|
background: #111827;
|
|
color: #f59e0b;
|
|
padding: 4px 12px;
|
|
border-radius: 4px;
|
|
font-size: 0.75rem;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
margin-left: 12px;
|
|
}
|
|
.section {
|
|
background: #1f2937;
|
|
border-radius: 8px;
|
|
padding: 20px;
|
|
margin-bottom: 20px;
|
|
}
|
|
.section-header {
|
|
font-size: 1.1rem;
|
|
font-weight: 600;
|
|
margin-bottom: 16px;
|
|
color: #f9fafb;
|
|
}
|
|
.endpoint-list { display: flex; flex-direction: column; gap: 12px; }
|
|
.endpoint-card {
|
|
background: #374151;
|
|
border: 2px solid transparent;
|
|
border-radius: 6px;
|
|
padding: 16px;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
.endpoint-card:hover { border-color: #f59e0b; background: #4b5563; }
|
|
.endpoint-card.active { border-color: #f59e0b; background: #4b5563; }
|
|
.endpoint-method {
|
|
display: inline-block;
|
|
padding: 2px 8px;
|
|
border-radius: 4px;
|
|
font-size: 0.75rem;
|
|
font-weight: 600;
|
|
margin-right: 8px;
|
|
}
|
|
.method-post { background: #10b981; color: white; }
|
|
.method-get { background: #3b82f6; color: white; }
|
|
.endpoint-path { font-family: monospace; font-size: 0.875rem; }
|
|
.endpoint-desc { font-size: 0.75rem; color: #9ca3af; margin-top: 6px; }
|
|
.form-group { margin-bottom: 16px; }
|
|
.form-label {
|
|
display: block;
|
|
font-size: 0.875rem;
|
|
font-weight: 500;
|
|
margin-bottom: 6px;
|
|
color: #f9fafb;
|
|
}
|
|
.form-input, .form-textarea {
|
|
width: 100%;
|
|
padding: 10px 12px;
|
|
background: #374151;
|
|
border: 1px solid #4b5563;
|
|
border-radius: 6px;
|
|
color: #e5e7eb;
|
|
font-size: 0.875rem;
|
|
font-family: monospace;
|
|
}
|
|
.form-textarea { min-height: 200px; font-family: monospace; }
|
|
.form-input:focus, .form-textarea:focus {
|
|
outline: none;
|
|
border-color: #f59e0b;
|
|
}
|
|
.btn {
|
|
padding: 10px 20px;
|
|
border: none;
|
|
border-radius: 6px;
|
|
font-size: 0.875rem;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
.btn-primary {
|
|
background: #f59e0b;
|
|
color: #111827;
|
|
}
|
|
.btn-primary:hover { background: #d97706; }
|
|
.btn-secondary {
|
|
background: #4b5563;
|
|
color: #e5e7eb;
|
|
margin-left: 8px;
|
|
}
|
|
.btn-secondary:hover { background: #6b7280; }
|
|
.stats {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
gap: 12px;
|
|
}
|
|
.stat-card {
|
|
background: #374151;
|
|
padding: 16px;
|
|
border-radius: 6px;
|
|
}
|
|
.stat-value { font-size: 1.5rem; font-weight: 600; color: #f59e0b; }
|
|
.stat-label { font-size: 0.75rem; color: #9ca3af; margin-top: 4px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<header>
|
|
<h1>Amar API <span class="mock-badge">MOCK</span></h1>
|
|
<div class="subtitle">Configure mock responses for Amar backend endpoints</div>
|
|
</header>
|
|
|
|
<!-- Stats -->
|
|
<div class="section">
|
|
<div class="section-header">Mock Database Stats</div>
|
|
<div class="stats" id="stats">
|
|
<div class="stat-card">
|
|
<div class="stat-value">-</div>
|
|
<div class="stat-label">Pet Owners</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-value">-</div>
|
|
<div class="stat-label">Pets</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-value">-</div>
|
|
<div class="stat-label">Carts</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-value">-</div>
|
|
<div class="stat-label">Requests</div>
|
|
</div>
|
|
</div>
|
|
<div style="margin-top: 16px;">
|
|
<button class="btn btn-secondary" onclick="resetMock()">Reset Database</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Endpoint Configuration -->
|
|
<div class="section">
|
|
<div class="section-header">Configure Endpoint Responses</div>
|
|
<div class="endpoint-list">
|
|
<div class="endpoint-card" onclick="selectEndpoint('POST', '/api/v1/pet-owners/', 'petowner')">
|
|
<div>
|
|
<span class="endpoint-method method-post">POST</span>
|
|
<span class="endpoint-path">/api/v1/pet-owners/</span>
|
|
</div>
|
|
<div class="endpoint-desc">Create guest pet owner (VET-536)</div>
|
|
</div>
|
|
<div class="endpoint-card" onclick="selectEndpoint('POST', '/api/v1/pets/', 'pet')">
|
|
<div>
|
|
<span class="endpoint-method method-post">POST</span>
|
|
<span class="endpoint-path">/api/v1/pets/</span>
|
|
</div>
|
|
<div class="endpoint-desc">Create pet (VET-537)</div>
|
|
</div>
|
|
<div class="endpoint-card" onclick="selectEndpoint('POST', '/api/v1/cart/', 'cart')">
|
|
<div>
|
|
<span class="endpoint-method method-post">POST</span>
|
|
<span class="endpoint-path">/api/v1/cart/</span>
|
|
</div>
|
|
<div class="endpoint-desc">Create cart (VET-538)</div>
|
|
</div>
|
|
<div class="endpoint-card" onclick="selectEndpoint('GET', '/api/v1/services/', 'services')">
|
|
<div>
|
|
<span class="endpoint-method method-get">GET</span>
|
|
<span class="endpoint-path">/api/v1/services/</span>
|
|
</div>
|
|
<div class="endpoint-desc">List services (VET-540)</div>
|
|
</div>
|
|
<div class="endpoint-card" onclick="selectEndpoint('GET', '/api/v1/categories/', 'categories')">
|
|
<div>
|
|
<span class="endpoint-method method-get">GET</span>
|
|
<span class="endpoint-path">/api/v1/categories/</span>
|
|
</div>
|
|
<div class="endpoint-desc">List categories (VET-539)</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Response Editor -->
|
|
<div class="section" id="responseEditor" style="display: none;">
|
|
<div class="section-header">Edit Response</div>
|
|
<div class="form-group">
|
|
<label class="form-label">Endpoint</label>
|
|
<input class="form-input" id="endpointDisplay" readonly>
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="form-label">Mock Response (JSON)</label>
|
|
<textarea class="form-textarea" id="responseJson" placeholder='{"id": 123, "name": "Luna", "_mock": true}'></textarea>
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="form-label">HTTP Status Code</label>
|
|
<input type="number" class="form-input" id="statusCode" value="200">
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="form-label">Delay (ms)</label>
|
|
<input type="number" class="form-input" id="delay" value="0">
|
|
</div>
|
|
<div>
|
|
<button class="btn btn-primary" onclick="saveResponse()">Save Response</button>
|
|
<button class="btn btn-secondary" onclick="closeEditor()">Cancel</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Test -->
|
|
<div class="section">
|
|
<div class="section-header">Quick Test</div>
|
|
<p style="color: #9ca3af; margin-bottom: 12px;">Test endpoint URL to hit for configured responses:</p>
|
|
<div class="form-input" style="background: #374151; user-select: all;">
|
|
http://localhost:8005/api/v1/pet-owners/
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let selectedEndpoint = null;
|
|
|
|
async function loadStats() {
|
|
try {
|
|
const resp = await fetch('/mock/stats');
|
|
const data = await resp.json();
|
|
document.getElementById('stats').innerHTML = `
|
|
<div class="stat-card"><div class="stat-value">${data.pet_owners || 0}</div><div class="stat-label">Pet Owners</div></div>
|
|
<div class="stat-card"><div class="stat-value">${data.pets || 0}</div><div class="stat-label">Pets</div></div>
|
|
<div class="stat-card"><div class="stat-value">${data.carts || 0}</div><div class="stat-label">Carts</div></div>
|
|
<div class="stat-card"><div class="stat-value">${data.service_requests || 0}</div><div class="stat-label">Requests</div></div>
|
|
`;
|
|
} catch (e) {
|
|
console.error('Failed to load stats:', e);
|
|
}
|
|
}
|
|
|
|
async function resetMock() {
|
|
if (confirm('Reset all mock data?')) {
|
|
await fetch('/mock/reset');
|
|
loadStats();
|
|
alert('Mock database reset');
|
|
}
|
|
}
|
|
|
|
function selectEndpoint(method, path, type) {
|
|
selectedEndpoint = {method, path, type};
|
|
document.querySelectorAll('.endpoint-card').forEach(c => c.classList.remove('active'));
|
|
event.currentTarget.classList.add('active');
|
|
document.getElementById('responseEditor').style.display = 'block';
|
|
document.getElementById('endpointDisplay').value = `${method} ${path}`;
|
|
document.getElementById('responseJson').value = getDefaultResponse(type);
|
|
}
|
|
|
|
function getDefaultResponse(type) {
|
|
const defaults = {
|
|
petowner: JSON.stringify({"id": 1234, "address": "Av Santa Fe 1234", "is_guest": true, "_mock": true}, null, 2),
|
|
pet: JSON.stringify({"id": 5678, "name": "Luna", "species": "DOG", "age": 3, "_mock": true}, null, 2),
|
|
cart: JSON.stringify({"id": 9012, "items": [], "total": 0, "_mock": true}, null, 2),
|
|
services: JSON.stringify([{"id": 1, "name": "Vacunación", "price": 1500, "_mock": true}], null, 2),
|
|
categories: JSON.stringify([{"id": 1, "name": "Salud", "services": 5, "_mock": true}], null, 2)
|
|
};
|
|
return defaults[type] || '{}';
|
|
}
|
|
|
|
function saveResponse() {
|
|
alert('Mock response saved (feature pending implementation)');
|
|
}
|
|
|
|
function closeEditor() {
|
|
document.getElementById('responseEditor').style.display = 'none';
|
|
selectedEndpoint = null;
|
|
document.querySelectorAll('.endpoint-card').forEach(c => c.classList.remove('active'));
|
|
}
|
|
|
|
loadStats();
|
|
setInterval(loadStats, 5000);
|
|
</script>
|
|
</body>
|
|
</html>
|