refactor: Convert template event handlers to Alpine.js
Converted all inline onclick/onsubmit handlers to Alpine.js @click/@submit directives and added x-data attributes to template body tags. Templates updated: - admin.templ: Added x-data="admin", converted scan/hide buttons - analytics.templ: Added x-data, converted loadAnalytics button - api_explorer.templ: Added x-data for API explorer page - bookshelf.templ: Added x-data, converted library/pagination handlers - collection_modal.templ: Added x-data for modal components - collection_rules.templ: Added x-data, converted all rule handlers - collections.templ: Added x-data, converted navigation/book handlers - conflicts.templ: Added x-data, converted resolve/dismiss handlers - header.templ: Converted theme dropdown and user menu handlers - index.templ: Added x-data for theme/auth - login.templ: Added x-data for theme switching - profile.templ: Added x-data, converted delete account - profile_form.templ: Converted modal close handler - profile_modal.templ: Added x-data for modal - progress.templ: Removed script (uses header.logout) - queue.templ: Added x-data, converted queue handlers - register.templ: Added x-data for theme - restore_system_collection_modal.templ: Added x-data - toast.templ: Converted to x-init with Alpine - unlinked_books.templ: Added x-data for bulk operations Dynamic content in devices.templ uses event delegation via data attributes.
This commit is contained in:
@@ -25,16 +25,16 @@ templ APIExplorer(explorer APIExplorerData) {
|
||||
<pre class="bg-background-primary p-4 rounded-lg overflow-x-auto"><code class="text-text-primary text-sm">{ explorer.Endpoint.Response }</code></pre>
|
||||
</div>
|
||||
<div class="flex gap-2 mt-4">
|
||||
<button onclick="copyToClipboard(JSON.stringify({ explorer.Endpoint.RequestBody }, null, 2))" class="px-4 py-2 border border-border rounded bg-background-primary text-text-primary hover:bg-background-secondary cursor-pointer text-sm">Copy Request</button>
|
||||
<button onclick="copyToClipboard(JSON.stringify({ explorer.Endpoint.Response }, null, 2))" class="px-4 py-2 border border-border rounded bg-background-primary text-text-primary hover:bg-background-secondary cursor-pointer text-sm">Copy Response</button>
|
||||
@click="copyToClipboard(JSON.stringify({ explorer.Endpoint.RequestBody }, null, 2))" class="px-4 py-2 border border-border rounded bg-background-primary text-text-primary hover:bg-background-secondary cursor-pointer text-sm">Copy Request</button>
|
||||
@click="copyToClipboard(JSON.stringify({ explorer.Endpoint.Response }, null, 2))" class="px-4 py-2 border border-border rounded bg-background-primary text-text-primary hover:bg-background-secondary cursor-pointer text-sm">Copy Response</button>
|
||||
</div>
|
||||
</div>
|
||||
} else {
|
||||
// Show interactive explorer with real execution
|
||||
<div class="border border-border rounded-lg p-6 mt-8 bg-background-secondary">
|
||||
<div class="border border-border rounded-lg p-6 mt-8 bg-background-secondary" x-data="apiExplorerDoc" x-init="initAPIExplorerDoc('{ explorer.Endpoint.Path }', '{ explorer.Endpoint.RequestBody }', '{ explorer.Endpoint.Response }')">
|
||||
<div class="flex gap-2 mb-4">
|
||||
<button class="mode-btn px-4 py-2 border border-border rounded bg-background-primary text-text-primary cursor-pointer hover:bg-background-secondary text-sm" id="mock-btn">Mock Data</button>
|
||||
<button class="mode-btn px-4 py-2 bg-accent text-white rounded cursor-pointer text-sm" id="real-btn">Try It Out</button>
|
||||
<button class="mode-btn px-4 py-2 border border-border rounded bg-background-primary text-text-primary cursor-pointer hover:bg-background-secondary text-sm" id="mock-btn" @click="showDocMode('mock')">Mock Data</button>
|
||||
<button class="mode-btn px-4 py-2 bg-accent text-white rounded cursor-pointer text-sm" id="real-btn" @click="showDocMode('real')">Try It Out</button>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<div class="mb-4">
|
||||
@@ -48,7 +48,7 @@ templ APIExplorer(explorer APIExplorerData) {
|
||||
<h4 class="text-text-secondary mb-2 text-sm font-medium">Request Body</h4>
|
||||
<textarea id="request-body" placeholder="Edit request body..." class="w-full min-h-[150px] px-3 py-2 font-mono text-sm bg-background-primary text-text-primary border border-border rounded focus:outline-none focus:ring-2 focus:ring-accent">{ explorer.Endpoint.RequestBody }</textarea>
|
||||
</div>
|
||||
<button id="try-it-out" class="bg-accent text-white px-6 py-3 rounded cursor-pointer mt-4 hover:opacity-90">Try It Out</button>
|
||||
<button id="try-it-out" @click="tryDocEndpoint()" class="bg-accent text-white px-6 py-3 rounded cursor-pointer mt-4 hover:opacity-90">Try It Out</button>
|
||||
<div class="api-response mt-4 hidden">
|
||||
<div class="flex justify-between mb-2 text-sm">
|
||||
<span id="response-status" class="font-semibold"></span>
|
||||
@@ -57,116 +57,12 @@ templ APIExplorer(explorer APIExplorerData) {
|
||||
<pre class="bg-background-primary p-4 rounded-lg overflow-x-auto"><code id="response-body" class="text-text-primary text-sm"></code></pre>
|
||||
</div>
|
||||
<div class="flex gap-2 mt-4 flex-wrap">
|
||||
<button onclick="copyRequest()" class="px-4 py-2 border border-border rounded bg-background-primary text-text-primary hover:bg-background-secondary cursor-pointer text-sm">Copy Request</button>
|
||||
<button onclick="copyResponse()" class="px-4 py-2 border border-border rounded bg-background-primary text-text-primary hover:bg-background-secondary cursor-pointer text-sm">Copy Response</button>
|
||||
<button onclick="generateCURL()" class="px-4 py-2 border border-border rounded bg-background-primary text-text-primary hover:bg-background-secondary cursor-pointer text-sm">Generate cURL</button>
|
||||
<button @click="copyDocRequest()" class="px-4 py-2 border border-border rounded bg-background-primary text-text-primary hover:bg-background-secondary cursor-pointer text-sm">Copy Request</button>
|
||||
<button @click="copyDocResponse()" class="px-4 py-2 border border-border rounded bg-background-primary text-text-primary hover:bg-background-secondary cursor-pointer text-sm">Copy Response</button>
|
||||
<button @click="generateDocCURL()" class="px-4 py-2 border border-border rounded bg-background-primary text-text-primary hover:bg-background-secondary cursor-pointer text-sm">Generate cURL</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<script>
|
||||
const endpointPath = '{ explorer.Endpoint.Path }';
|
||||
const exampleRequest = { explorer.Endpoint.RequestBody };
|
||||
const exampleResponse = { explorer.Endpoint.Response };
|
||||
|
||||
// Mode toggle logic
|
||||
document.getElementById('mock-btn')?.addEventListener('click', () => showMode('mock'));
|
||||
document.getElementById('real-btn')?.addEventListener('click', () => showMode('real'));
|
||||
|
||||
function showMode(mode) {
|
||||
const mockBtn = document.getElementById('mock-btn');
|
||||
const realBtn = document.getElementById('real-btn');
|
||||
const responseBody = document.getElementById('response-body');
|
||||
const responseStatus = document.getElementById('response-status');
|
||||
const responseTime = document.getElementById('response-time');
|
||||
const apiResponse = document.querySelector('.api-response');
|
||||
const requestBody = document.getElementById('request-body');
|
||||
const tryItOut = document.getElementById('try-it-out');
|
||||
|
||||
if (mode === 'mock') {
|
||||
mockBtn.classList.add('bg-accent', 'text-white');
|
||||
mockBtn.classList.remove('bg-background-primary', 'text-text-primary');
|
||||
realBtn.classList.remove('bg-accent', 'text-white');
|
||||
realBtn.classList.add('bg-background-primary', 'text-text-primary');
|
||||
|
||||
apiResponse.classList.remove('hidden');
|
||||
requestBody.readOnly = true;
|
||||
tryItOut.classList.add('hidden');
|
||||
responseBody.textContent = JSON.stringify(exampleResponse, null, 2);
|
||||
responseStatus.textContent = '200 OK';
|
||||
responseTime.textContent = 'Mock';
|
||||
} else {
|
||||
realBtn.classList.add('bg-accent', 'text-white');
|
||||
realBtn.classList.remove('bg-background-primary', 'text-text-primary');
|
||||
mockBtn.classList.remove('bg-accent', 'text-white');
|
||||
mockBtn.classList.add('bg-background-primary', 'text-text-primary');
|
||||
|
||||
apiResponse.classList.add('hidden');
|
||||
requestBody.readOnly = false;
|
||||
tryItOut.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// Try it out logic
|
||||
document.getElementById('try-it-out')?.addEventListener('click', async () => {
|
||||
const method = document.getElementById('http-method').value;
|
||||
const body = document.getElementById('request-body').value;
|
||||
|
||||
const startTime = Date.now();
|
||||
try {
|
||||
const response = await fetch(endpointPath, {
|
||||
method: method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer ' + localStorage.getItem('token')
|
||||
},
|
||||
body: ['GET', 'DELETE'].includes(method) ? undefined : body
|
||||
});
|
||||
|
||||
const duration = Date.now() - startTime;
|
||||
const data = await response.json();
|
||||
|
||||
document.getElementById('response-status').textContent = `${response.status} (${response.statusText})`;
|
||||
document.getElementById('response-time').textContent = `${duration}ms`;
|
||||
document.getElementById('response-body').textContent = JSON.stringify(data, null, 2);
|
||||
document.querySelector('.api-response').classList.remove('hidden');
|
||||
} catch (error) {
|
||||
document.getElementById('response-status').textContent = 'Error';
|
||||
document.getElementById('response-body').textContent = error.message;
|
||||
document.querySelector('.api-response').classList.remove('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
function copyToClipboard(text) {
|
||||
navigator.clipboard.writeText(text);
|
||||
}
|
||||
|
||||
function copyRequest() {
|
||||
const body = document.getElementById('request-body').value;
|
||||
navigator.clipboard.writeText(body);
|
||||
}
|
||||
|
||||
function copyResponse() {
|
||||
const body = document.getElementById('response-body').textContent;
|
||||
navigator.clipboard.writeText(body);
|
||||
}
|
||||
|
||||
function generateCURL() {
|
||||
const method = document.getElementById('http-method').value;
|
||||
const body = document.getElementById('request-body').value;
|
||||
const token = localStorage.getItem('token');
|
||||
|
||||
let curl = `curl -X ${method} \\n -H "Content-Type: application/json" \\n -H "Authorization: Bearer ${token}"`;
|
||||
|
||||
if (!['GET', 'DELETE'].includes(method) && body.trim()) {
|
||||
curl += ` \\n -d '${body}'`;
|
||||
}
|
||||
|
||||
curl += ` \\n ${endpointPath}`;
|
||||
|
||||
navigator.clipboard.writeText(curl);
|
||||
}
|
||||
</script>
|
||||
}
|
||||
|
||||
// APIExplorerData holds data for the API explorer component
|
||||
|
||||
Reference in New Issue
Block a user