123
This commit is contained in:
176
frontend/src/main.js
Normal file
176
frontend/src/main.js
Normal file
@@ -0,0 +1,176 @@
|
||||
import { api } from "./api/client.js";
|
||||
import { renderAppShell } from "./components/shell.js";
|
||||
import { renderLoginPage } from "./pages/login.js";
|
||||
import "./styles/main.css";
|
||||
|
||||
const state = {
|
||||
user: null,
|
||||
error: "",
|
||||
tables: [],
|
||||
activeTable: "",
|
||||
activeTab: "data",
|
||||
page: "overview",
|
||||
rows: { rows: [], total: 0 },
|
||||
tableDetails: { columns: [], foreignKeys: [] },
|
||||
users: [],
|
||||
roles: [],
|
||||
auditLogs: [],
|
||||
postgresLogs: [],
|
||||
sqlDraft: "",
|
||||
sqlResult: null,
|
||||
search: ""
|
||||
};
|
||||
|
||||
const app = document.querySelector("#app");
|
||||
|
||||
bootstrap();
|
||||
|
||||
async function bootstrap() {
|
||||
try {
|
||||
const { user } = await api.me();
|
||||
state.user = user;
|
||||
|
||||
if (user) {
|
||||
await hydrateDashboard();
|
||||
}
|
||||
} catch {
|
||||
state.user = null;
|
||||
}
|
||||
|
||||
render();
|
||||
}
|
||||
|
||||
async function hydrateDashboard() {
|
||||
const [tablesPayload, usersPayload, rolesPayload, auditPayload, logsPayload] = await Promise.all([
|
||||
api.tables(),
|
||||
api.users(),
|
||||
api.roles(),
|
||||
api.audit(),
|
||||
api.postgresLogs()
|
||||
]);
|
||||
|
||||
state.tables = tablesPayload.tables;
|
||||
state.users = usersPayload.users;
|
||||
state.roles = rolesPayload.roles;
|
||||
state.auditLogs = auditPayload.logs;
|
||||
state.postgresLogs = logsPayload.logs;
|
||||
|
||||
if (!state.activeTable && state.tables[0]) {
|
||||
state.activeTable = state.tables[0].table_name;
|
||||
}
|
||||
|
||||
if (state.activeTable) {
|
||||
await Promise.all([loadRows(), loadTableDetails()]);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadRows() {
|
||||
if (!state.activeTable) {
|
||||
return;
|
||||
}
|
||||
|
||||
state.rows = await api.rows(state.activeTable, {
|
||||
page: 1,
|
||||
pageSize: 25,
|
||||
search: state.search
|
||||
});
|
||||
}
|
||||
|
||||
async function loadTableDetails() {
|
||||
if (!state.activeTable) {
|
||||
return;
|
||||
}
|
||||
|
||||
state.tableDetails = await api.tableDetails(state.activeTable);
|
||||
}
|
||||
|
||||
function render() {
|
||||
app.innerHTML = state.user ? renderAppShell(state) : renderLoginPage(state);
|
||||
bindEvents();
|
||||
}
|
||||
|
||||
function bindEvents() {
|
||||
const loginForm = document.querySelector("#loginForm");
|
||||
if (loginForm) {
|
||||
loginForm.addEventListener("submit", handleLogin);
|
||||
}
|
||||
|
||||
const logoutButton = document.querySelector("#logoutButton");
|
||||
if (logoutButton) {
|
||||
logoutButton.addEventListener("click", handleLogout);
|
||||
}
|
||||
|
||||
document.querySelectorAll("[data-page]").forEach((element) => {
|
||||
element.addEventListener("click", async (event) => {
|
||||
state.page = event.currentTarget.dataset.page;
|
||||
render();
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll("[data-tab]").forEach((element) => {
|
||||
element.addEventListener("click", async (event) => {
|
||||
state.activeTab = event.currentTarget.dataset.tab;
|
||||
render();
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll("[data-table]").forEach((element) => {
|
||||
element.addEventListener("click", async (event) => {
|
||||
state.activeTable = event.currentTarget.dataset.table;
|
||||
state.page = "overview";
|
||||
state.sqlDraft = `select * from ${state.activeTable} limit 20;`;
|
||||
await Promise.all([loadRows(), loadTableDetails()]);
|
||||
render();
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll("[data-action]").forEach((element) => {
|
||||
element.addEventListener("click", handleAction);
|
||||
});
|
||||
}
|
||||
|
||||
async function handleLogin(event) {
|
||||
event.preventDefault();
|
||||
const formData = new FormData(event.currentTarget);
|
||||
state.error = "";
|
||||
|
||||
try {
|
||||
const payload = await api.login({
|
||||
username: formData.get("username"),
|
||||
password: formData.get("password")
|
||||
});
|
||||
state.user = payload.user;
|
||||
await hydrateDashboard();
|
||||
} catch (error) {
|
||||
state.error = error.message;
|
||||
}
|
||||
|
||||
render();
|
||||
}
|
||||
|
||||
async function handleLogout() {
|
||||
await api.logout();
|
||||
state.user = null;
|
||||
state.error = "";
|
||||
render();
|
||||
}
|
||||
|
||||
async function handleAction(event) {
|
||||
const action = event.currentTarget.dataset.action;
|
||||
|
||||
if (action === "refresh") {
|
||||
await hydrateDashboard();
|
||||
}
|
||||
|
||||
if (action === "search") {
|
||||
state.search = document.querySelector("#tableSearchInput")?.value || "";
|
||||
await loadRows();
|
||||
}
|
||||
|
||||
if (action === "run-sql") {
|
||||
state.sqlDraft = document.querySelector("#sqlEditor")?.value || "";
|
||||
state.sqlResult = await api.executeSql(state.sqlDraft);
|
||||
}
|
||||
|
||||
render();
|
||||
}
|
||||
Reference in New Issue
Block a user