Extracted inline JavaScript from templates into proper TypeScript modules: - api-explorer-docs.ts: API explorer page functionality - collection-rules.ts: Collection rules management page - index.ts: Homepage theme and auth redirect - login.ts: Login page theme initialization - profile-modal.ts: Profile modal close and escape key - profile.ts: Profile page delete account - register.ts: Registration page theme init - toast-error.ts: Error toast with retry button - unlinked_books.ts: Unlinked books management page Each file: - Uses ES imports (showToast, getToken, etc.) - Has proper TypeScript types - Registers with Alpine.js via Alpine.global() - Uses async/await for API calls
143 lines
4.7 KiB
TypeScript
143 lines
4.7 KiB
TypeScript
import { Alpine } from "./alpine";
|
|
import { getToken } from "./storage";
|
|
|
|
let endpointPath = "";
|
|
let exampleResponse: unknown = null;
|
|
|
|
function initAPIExplorerDoc(path: string, request: string, response: string): void {
|
|
endpointPath = path;
|
|
exampleResponse = JSON.parse(response);
|
|
}
|
|
|
|
function showDocMode(mode: "mock" | "real"): void {
|
|
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") as HTMLTextAreaElement;
|
|
const tryItOut = document.getElementById("try-it-out");
|
|
|
|
if (!mockBtn || !realBtn || !responseBody || !responseStatus || !responseTime || !apiResponse || !requestBody || !tryItOut) return;
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
async function tryDocEndpoint(): Promise<void> {
|
|
const methodSelect = document.getElementById("http-method") as HTMLSelectElement;
|
|
const requestBody = document.getElementById("request-body") as HTMLTextAreaElement;
|
|
const responseBody = document.getElementById("response-body");
|
|
const responseStatus = document.getElementById("response-status");
|
|
const responseTime = document.getElementById("response-time");
|
|
const apiResponse = document.querySelector(".api-response");
|
|
|
|
if (!methodSelect || !requestBody || !responseBody || !responseStatus || !responseTime || !apiResponse) return;
|
|
|
|
const method = methodSelect.value;
|
|
const body = requestBody.value;
|
|
|
|
const startTime = Date.now();
|
|
try {
|
|
const token = getToken();
|
|
if (!token) {
|
|
throw new Error("No authentication token found");
|
|
}
|
|
|
|
const response = await fetch(endpointPath, {
|
|
method: method,
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Authorization: `Bearer ${token}`,
|
|
},
|
|
body: ["GET", "DELETE"].includes(method) ? undefined : body,
|
|
});
|
|
|
|
const duration = Date.now() - startTime;
|
|
const data = await response.json();
|
|
|
|
responseStatus.textContent = `${response.status} (${response.statusText})`;
|
|
responseTime.textContent = `${duration}ms`;
|
|
responseBody.textContent = JSON.stringify(data, null, 2);
|
|
apiResponse.classList.remove("hidden");
|
|
} catch (error) {
|
|
responseStatus.textContent = "Error";
|
|
responseBody.textContent = (error as Error).message;
|
|
apiResponse.classList.remove("hidden");
|
|
}
|
|
}
|
|
|
|
function copyDocRequest(): void {
|
|
const requestBody = document.getElementById("request-body") as HTMLTextAreaElement;
|
|
if (requestBody) {
|
|
navigator.clipboard.writeText(requestBody.value);
|
|
}
|
|
}
|
|
|
|
function copyDocResponse(): void {
|
|
const responseBody = document.getElementById("response-body");
|
|
if (responseBody) {
|
|
navigator.clipboard.writeText(responseBody.textContent || "");
|
|
}
|
|
}
|
|
|
|
function generateDocCURL(): void {
|
|
const methodSelect = document.getElementById("http-method") as HTMLSelectElement;
|
|
const requestBody = document.getElementById("request-body") as HTMLTextAreaElement;
|
|
|
|
if (!methodSelect || !requestBody) return;
|
|
|
|
const method = methodSelect.value;
|
|
const body = requestBody.value;
|
|
const token = getToken();
|
|
|
|
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);
|
|
}
|
|
|
|
export {
|
|
copyDocRequest,
|
|
copyDocResponse,
|
|
generateDocCURL,
|
|
initAPIExplorerDoc,
|
|
showDocMode,
|
|
tryDocEndpoint,
|
|
};
|
|
|
|
Alpine.global("apiExplorerDoc", {
|
|
copyDocRequest,
|
|
copyDocResponse,
|
|
generateDocCURL,
|
|
initAPIExplorerDoc,
|
|
showDocMode,
|
|
tryDocEndpoint,
|
|
});
|