feat(web): update frontend TypeScript modules and API types
This commit updates the web frontend TypeScript modules: Core modules: - admin.ts: Admin panel functionality and user management - analytics.ts: Analytics dashboard and data visualization - api-explorer.ts: Interactive API documentation explorer - api.ts: Core API client with request/response handling - collections.ts: Book collection management UI - conflicts.ts: Sync conflict resolution interface - custom-section-builder.ts: Dynamic section builder for UI - docs.ts: Documentation viewer and navigation - dom.ts: DOM manipulation utilities and helpers - header.ts: Application header with navigation - library.ts: Library view and book grid management - linking.ts: Device-book linking interface - password_validation.ts: Client-side password strength validation - queue.ts: Device sync queue management UI - search.ts: Full-text search with Lunr integration - storage.ts: Local storage and cache management - theme.ts: Theme management and CSS variable updates - themeDropdown.ts: Theme selector dropdown component - toast.ts: Toast notification system - woodPaneling.ts: Visual theme effects - woodPanelingInit.ts: Visual effects initialization Type definitions: - api.d.ts: Updated TypeScript definitions for API responses These updates enhance the frontend with improved functionality for book management, device synchronization, and user experience.
This commit is contained in:
+116
-98
@@ -1,70 +1,78 @@
|
||||
interface ApiExplorerRequest {
|
||||
method: string;
|
||||
endpoint: string;
|
||||
headers: Record<string, string>;
|
||||
body?: string;
|
||||
method: string;
|
||||
endpoint: string;
|
||||
headers: Record<string, string>;
|
||||
body?: string;
|
||||
}
|
||||
|
||||
const requestHistory: ApiExplorerRequest[] = [];
|
||||
|
||||
function sendApiRequest(): void {
|
||||
const method = (document.getElementById('api-method') as HTMLSelectElement)?.value || 'GET';
|
||||
const endpoint = (document.getElementById('api-endpoint') as HTMLInputElement)?.value || '';
|
||||
const bodyText = (document.getElementById('api-body') as HTMLTextAreaElement)?.value || '';
|
||||
const method =
|
||||
(document.getElementById("api-method") as HTMLSelectElement)?.value ||
|
||||
"GET";
|
||||
const endpoint =
|
||||
(document.getElementById("api-endpoint") as HTMLInputElement)?.value || "";
|
||||
const bodyText =
|
||||
(document.getElementById("api-body") as HTMLTextAreaElement)?.value || "";
|
||||
|
||||
const token = localStorage.getItem('token');
|
||||
const token = localStorage.getItem("token");
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json'
|
||||
};
|
||||
const headers: Record<string, string> = {
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
if (token) {
|
||||
headers["Authorization"] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
const request: ApiExplorerRequest = {
|
||||
method,
|
||||
endpoint,
|
||||
headers,
|
||||
body: bodyText || undefined
|
||||
};
|
||||
const request: ApiExplorerRequest = {
|
||||
method,
|
||||
endpoint,
|
||||
headers,
|
||||
body: bodyText || undefined,
|
||||
};
|
||||
|
||||
addToHistory(request);
|
||||
addToHistory(request);
|
||||
|
||||
const startTime = performance.now();
|
||||
const startTime = performance.now();
|
||||
|
||||
fetch(endpoint, {
|
||||
method,
|
||||
headers,
|
||||
body: bodyText || undefined
|
||||
fetch(endpoint, {
|
||||
method,
|
||||
headers,
|
||||
body: bodyText || undefined,
|
||||
})
|
||||
.then(async (response) => {
|
||||
const endTime = performance.now();
|
||||
const duration = Math.round(endTime - startTime);
|
||||
|
||||
const responseText = await response.text();
|
||||
let responseData: unknown;
|
||||
try {
|
||||
responseData = JSON.parse(responseText);
|
||||
} catch {
|
||||
responseData = responseText;
|
||||
}
|
||||
|
||||
displayResponse(response, responseData, duration);
|
||||
generateCurl(request);
|
||||
})
|
||||
.then(async response => {
|
||||
const endTime = performance.now();
|
||||
const duration = Math.round(endTime - startTime);
|
||||
|
||||
const responseText = await response.text();
|
||||
let responseData: unknown;
|
||||
try {
|
||||
responseData = JSON.parse(responseText);
|
||||
} catch {
|
||||
responseData = responseText;
|
||||
}
|
||||
|
||||
displayResponse(response, responseData, duration);
|
||||
generateCurl(request);
|
||||
})
|
||||
.catch(error => {
|
||||
displayError(error);
|
||||
.catch((error) => {
|
||||
displayError(error);
|
||||
});
|
||||
}
|
||||
|
||||
function displayResponse(response: Response, data: unknown, duration: number): void {
|
||||
const container = document.getElementById('api-response');
|
||||
if (!container) return;
|
||||
function displayResponse(
|
||||
response: Response,
|
||||
data: unknown,
|
||||
duration: number,
|
||||
): void {
|
||||
const container = document.getElementById("api-response");
|
||||
if (!container) return;
|
||||
|
||||
const statusColor = response.ok ? 'var(--accent)' : 'var(--error)';
|
||||
const statusColor = response.ok ? "var(--accent)" : "var(--error)";
|
||||
|
||||
container.innerHTML = `
|
||||
container.innerHTML = `
|
||||
<div class="mb-4">
|
||||
<span class="px-2 py-1 rounded text-sm" style="background-color: ${statusColor}; color: var(--bg-primary)">
|
||||
${response.status} ${response.statusText}
|
||||
@@ -76,10 +84,10 @@ function displayResponse(response: Response, data: unknown, duration: number): v
|
||||
}
|
||||
|
||||
function displayError(error: Error): void {
|
||||
const container = document.getElementById('api-response');
|
||||
if (!container) return;
|
||||
const container = document.getElementById("api-response");
|
||||
if (!container) return;
|
||||
|
||||
container.innerHTML = `
|
||||
container.innerHTML = `
|
||||
<div class="p-4 rounded" style="background-color: var(--bg-primary)">
|
||||
<p style="color: var(--error)">Error: ${error.message}</p>
|
||||
</div>
|
||||
@@ -87,84 +95,94 @@ function displayError(error: Error): void {
|
||||
}
|
||||
|
||||
function generateCurl(request: ApiExplorerRequest): void {
|
||||
const container = document.getElementById('curl-command');
|
||||
if (!container) return;
|
||||
const container = document.getElementById("curl-command");
|
||||
if (!container) return;
|
||||
|
||||
let curl = `curl -X ${request.method} '${request.endpoint}'`;
|
||||
let curl = `curl -X ${request.method} '${request.endpoint}'`;
|
||||
|
||||
Object.entries(request.headers).forEach(([key, value]) => {
|
||||
curl += ` \\\n -H '${key}: ${value}'`;
|
||||
});
|
||||
Object.entries(request.headers).forEach(([key, value]) => {
|
||||
curl += ` \\\n -H '${key}: ${value}'`;
|
||||
});
|
||||
|
||||
if (request.body) {
|
||||
curl += ` \\\n -d '${request.body}'`;
|
||||
}
|
||||
if (request.body) {
|
||||
curl += ` \\\n -d '${request.body}'`;
|
||||
}
|
||||
|
||||
container.textContent = curl;
|
||||
container.textContent = curl;
|
||||
}
|
||||
|
||||
function addToHistory(request: ApiExplorerRequest): void {
|
||||
requestHistory.unshift(request);
|
||||
if (requestHistory.length > 20) {
|
||||
requestHistory.pop();
|
||||
}
|
||||
renderHistory();
|
||||
requestHistory.unshift(request);
|
||||
if (requestHistory.length > 20) {
|
||||
requestHistory.pop();
|
||||
}
|
||||
renderHistory();
|
||||
}
|
||||
|
||||
function renderHistory(): void {
|
||||
const container = document.getElementById('request-history');
|
||||
if (!container) return;
|
||||
const container = document.getElementById("request-history");
|
||||
if (!container) return;
|
||||
|
||||
if (requestHistory.length === 0) {
|
||||
container.innerHTML = '<p class="text-sm p-2" style="color: var(--text-secondary)">No requests yet</p>';
|
||||
return;
|
||||
}
|
||||
if (requestHistory.length === 0) {
|
||||
container.innerHTML =
|
||||
'<p class="text-sm p-2" style="color: var(--text-secondary)">No requests yet</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = requestHistory.slice(0, 10).map((req, i) => `
|
||||
container.innerHTML = requestHistory
|
||||
.slice(0, 10)
|
||||
.map(
|
||||
(req, i) => `
|
||||
<div class="p-2 rounded cursor-pointer hover:bg-opacity-50 transition-colors"
|
||||
style="background-color: var(--bg-secondary)"
|
||||
onclick="window.loadFromHistory(${i})">
|
||||
<span class="text-xs font-mono" style="color: ${req.method === 'GET' ? 'var(--accent)' : req.method === 'POST' ? 'var(--success)' : req.method === 'DELETE' ? 'var(--error)' : 'var(--text-primary)'}">${req.method}</span>
|
||||
<span class="text-xs font-mono" style="color: ${req.method === "GET" ? "var(--accent)" : req.method === "POST" ? "var(--success)" : req.method === "DELETE" ? "var(--error)" : "var(--text-primary)"}">${req.method}</span>
|
||||
<span class="text-xs ml-2" style="color: var(--text-secondary)">${req.endpoint}</span>
|
||||
</div>
|
||||
`).join('');
|
||||
`,
|
||||
)
|
||||
.join("");
|
||||
}
|
||||
|
||||
function loadFromHistory(index: number): void {
|
||||
const request = requestHistory[index];
|
||||
if (!request) return;
|
||||
const request = requestHistory[index];
|
||||
if (!request) return;
|
||||
|
||||
const methodSelect = document.getElementById('api-method') as HTMLSelectElement;
|
||||
const endpointInput = document.getElementById('api-endpoint') as HTMLInputElement;
|
||||
const bodyInput = document.getElementById('api-body') as HTMLTextAreaElement;
|
||||
const methodSelect = document.getElementById(
|
||||
"api-method",
|
||||
) as HTMLSelectElement;
|
||||
const endpointInput = document.getElementById(
|
||||
"api-endpoint",
|
||||
) as HTMLInputElement;
|
||||
const bodyInput = document.getElementById("api-body") as HTMLTextAreaElement;
|
||||
|
||||
if (methodSelect) methodSelect.value = request.method;
|
||||
if (endpointInput) endpointInput.value = request.endpoint;
|
||||
if (bodyInput) bodyInput.value = request.body || '';
|
||||
if (methodSelect) methodSelect.value = request.method;
|
||||
if (endpointInput) endpointInput.value = request.endpoint;
|
||||
if (bodyInput) bodyInput.value = request.body || "";
|
||||
}
|
||||
|
||||
function copyCurl(): void {
|
||||
const curl = document.getElementById('curl-command')?.textContent;
|
||||
if (curl) {
|
||||
navigator.clipboard.writeText(curl);
|
||||
if ((window as any).showToast?.success) {
|
||||
(window as any).showToast.success('cURL copied to clipboard');
|
||||
}
|
||||
const curl = document.getElementById("curl-command")?.textContent;
|
||||
if (curl) {
|
||||
navigator.clipboard.writeText(curl);
|
||||
if ((window as any).showToast?.success) {
|
||||
(window as any).showToast.success("cURL copied to clipboard");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function formatJson(): void {
|
||||
const bodyInput = document.getElementById('api-body') as HTMLTextAreaElement;
|
||||
if (!bodyInput) return;
|
||||
const bodyInput = document.getElementById("api-body") as HTMLTextAreaElement;
|
||||
if (!bodyInput) return;
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(bodyInput.value);
|
||||
bodyInput.value = JSON.stringify(parsed, null, 2);
|
||||
} catch {
|
||||
if ((window as any).showToast?.error) {
|
||||
(window as any).showToast.error('Invalid JSON');
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(bodyInput.value);
|
||||
bodyInput.value = JSON.stringify(parsed, null, 2);
|
||||
} catch {
|
||||
if ((window as any).showToast?.error) {
|
||||
(window as any).showToast.error("Invalid JSON");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(window as any).sendApiRequest = sendApiRequest;
|
||||
|
||||
Reference in New Issue
Block a user