2
0
Files
Royal-Pop-Accessory/Store/src/pages/client/reseed/index.astro
T
2026-06-23 18:50:37 +01:00

274 lines
10 KiB
Plaintext

---
import SiteFooter from "../../../components/royal-pop/SiteFooter.astro";
import BaseLayout from "../../../layouts/BaseLayout.astro";
import styles from "../client.module.scss";
const apiBaseUrl = process.env.API_BASE_URL || (import.meta.env.PROD ? "" : "http://localhost:8081");
const dangerListClass = styles.dangerList;
const dangerPanelClass = styles.dangerPanel;
const dangerActionClass = styles.dangerAction;
const dangerNoteClass = styles.dangerNote;
---
<BaseLayout
title="Royal Pop Client Reseed"
description="Private Royal Pop reseed tool for backup and reset operations."
noindex={true}
>
<main class={styles.clientPage} data-client-reseed-page>
<header class={styles.topbar}>
<div class={styles.topbarInner}>
<a href="/client/" class={styles.backLink}>Royal Pop</a>
<div class={styles.progress} aria-label="Client tools">
<span class={styles.progressActive}>Client reseed</span>
</div>
<div class={styles.topbarActions}>
<p class={styles.helpText}>Local only</p>
<button type="button" class={styles.logoutButton} data-client-logout hidden>Log out</button>
</div>
</div>
</header>
<section class={styles.hero}>
<div class={styles.heroInner}>
<div>
<p class={styles.eyebrow}>Client reseed</p>
<h1 class={styles.heroTitle}>Back up the local database, then clear it back to empty.</h1>
<p class={styles.heroBody} data-reseed-hero-body>
This private tool creates a timestamped SQL backup first, then wipes orders, order items, and inventory rows while keeping the schema intact.
</p>
</div>
<div class={styles.heroMeta}>
<div>
<strong>Backup first</strong>
<span>A SQL snapshot is written before anything is truncated so you have a recovery point.</span>
</div>
<div>
<strong>App data only</strong>
<span>Orders, order items, and inventory rows are cleared. Tables, indexes, and routes remain in place.</span>
</div>
<div>
<strong>No shortcuts</strong>
<span>Use this only when you truly want a clean local state for demos, testing, or re-seeding.</span>
</div>
</div>
</div>
</section>
<section class={styles.dashboardSection}>
<div class={styles.dashboardShell}>
<section class={styles.loginPanel} data-client-login-panel>
<div class={styles.panelHeader}>
<p class={styles.sectionKicker}>Sign in</p>
<h2>Unlock the reseed tool.</h2>
<p>Use the same private client credentials as the other dashboard pages.</p>
</div>
<form class={styles.loginForm} data-client-login-form>
<label class={styles.field}>
<span>Username</span>
<input type="text" name="username" autocomplete="username" required />
</label>
<label class={styles.field}>
<span>Password</span>
<input type="password" name="password" autocomplete="current-password" required />
</label>
<div class={styles.loginActions}>
<button type="submit" class={styles.primaryCta} data-client-login-submit>Unlock reseed</button>
</div>
<p class={styles.formStatus} data-client-auth-feedback data-state="info">
Sign in to create a backup and reset the local database.
</p>
</form>
</section>
<section class={dangerPanelClass} data-client-reseed-panel hidden>
<div class={styles.panelHeader}>
<p class={styles.sectionKicker}>Danger zone</p>
<h2>Backup and reseed the local database</h2>
<p>This action writes a SQL backup file first, then empties the app tables so the local client pages come back up with a clean state.</p>
</div>
<ul class={dangerListClass}>
<li>Creates a timestamped SQL backup file under <strong>Backend/Backups/reseed/</strong>.</li>
<li>Clears <strong>orders</strong>, <strong>order_items</strong>, and <strong>inventory_levels</strong>.</li>
<li>Keeps the schema, migrations, and app code intact.</li>
</ul>
<div class={dangerActionClass}>
<button type="button" class={styles.primaryCta} data-reseed-trigger>Backup and reseed database</button>
<p class={dangerNoteClass}>There is no second step on this page. Once you press the button, the local reset runs immediately after the backup file is written.</p>
</div>
<p class={styles.dashboardStatus} data-client-reseed-feedback data-state="info">Reseed tool ready.</p>
</section>
</div>
</section>
<SiteFooter />
<script define:vars={{ apiBaseUrl }}>
const normalizedApiBaseUrl = String(apiBaseUrl || "").replace(/\/$/, "");
const initClientReseedPage = () => {
const root = document.querySelector("[data-client-reseed-page]");
if (!root || root.dataset.initialized === "true") return;
root.dataset.initialized = "true";
const loginPanel = root.querySelector("[data-client-login-panel]");
const reseedPanel = root.querySelector("[data-client-reseed-panel]");
const loginForm = root.querySelector("[data-client-login-form]");
const loginSubmit = root.querySelector("[data-client-login-submit]");
const authFeedback = root.querySelector("[data-client-auth-feedback]");
const reseedFeedback = root.querySelector("[data-client-reseed-feedback]");
const reseedTrigger = root.querySelector("[data-reseed-trigger]");
const logoutButton = root.querySelector("[data-client-logout]");
const heroBody = root.querySelector("[data-reseed-hero-body]");
const setFeedback = (node, message, state = "info") => {
if (!node) return;
node.textContent = message;
node.dataset.state = state;
};
const setAuthenticatedUi = (authenticated, username = "") => {
if (loginPanel) loginPanel.hidden = authenticated;
if (reseedPanel) reseedPanel.hidden = !authenticated;
if (logoutButton) logoutButton.hidden = !authenticated;
if (heroBody) {
heroBody.textContent = authenticated
? `Signed in as ${username || "client user"}. This tool will create a backup file first and then reset the local database to an empty state.`
: "Sign in to create a backup and reset the local database to an empty state.";
}
};
const syncSession = async () => {
try {
const response = await fetch(`${normalizedApiBaseUrl}/v1/client/session`, { credentials: "include" });
const payload = await response.json().catch(() => null);
const session = payload?.data;
if (!response.ok || !session?.configured) {
setAuthenticatedUi(false);
setFeedback(authFeedback, session?.configured === false ? "Client dashboard credentials are not configured for this environment yet." : "Unable to verify dashboard access.", session?.configured === false ? "error" : "info");
return;
}
if (!session.authenticated) {
setAuthenticatedUi(false);
setFeedback(authFeedback, "Sign in to create a backup and reset the local database.", "info");
return;
}
setAuthenticatedUi(true, session.username);
setFeedback(reseedFeedback, "Reseed tool ready.", "success");
} catch (error) {
setAuthenticatedUi(false);
setFeedback(authFeedback, error instanceof Error ? error.message : "Unable to verify dashboard access.", "error");
}
};
loginForm?.addEventListener("submit", async (event) => {
event.preventDefault();
const formData = new FormData(loginForm);
const username = String(formData.get("username") || "").trim();
const password = String(formData.get("password") || "");
if (!username || !password) {
setFeedback(authFeedback, "Enter both the username and password.", "error");
return;
}
if (loginSubmit) {
loginSubmit.disabled = true;
loginSubmit.textContent = "Unlocking…";
}
setFeedback(authFeedback, "Checking credentials…", "info");
try {
const response = await fetch(`${normalizedApiBaseUrl}/v1/client/login`, {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username, password }),
});
const payload = await response.json().catch(() => null);
if (!response.ok) {
throw new Error(payload?.error?.message || "The client dashboard username or password is incorrect.");
}
loginForm.reset();
setAuthenticatedUi(true, payload?.data?.username || username);
setFeedback(reseedFeedback, "Reseed tool ready.", "success");
} catch (error) {
setAuthenticatedUi(false);
setFeedback(authFeedback, error instanceof Error ? error.message : "Unable to unlock the reseed tool.", "error");
} finally {
if (loginSubmit) {
loginSubmit.disabled = false;
loginSubmit.textContent = "Unlock reseed";
}
}
});
logoutButton?.addEventListener("click", async () => {
logoutButton.disabled = true;
try {
await fetch(`${normalizedApiBaseUrl}/v1/client/logout`, { method: "POST", credentials: "include" });
} finally {
logoutButton.disabled = false;
setAuthenticatedUi(false);
setFeedback(authFeedback, "Signed out. Sign in again to use the reseed tool.", "info");
}
});
reseedTrigger?.addEventListener("click", async () => {
if (!window.confirm("This will back up the local database and then clear orders, order items, and inventory. Continue?")) {
return;
}
reseedTrigger.disabled = true;
reseedTrigger.textContent = "Backing up and reseeding…";
setFeedback(reseedFeedback, "Creating backup and clearing the local database…", "info");
try {
const response = await fetch(`${normalizedApiBaseUrl}/v1/client/reseed`, {
method: "POST",
credentials: "include",
});
const payload = await response.json().catch(() => null);
if (!response.ok) {
throw new Error(payload?.error?.message || "The database backup or reset could not be completed.");
}
const data = payload?.data || {};
setFeedback(
reseedFeedback,
`Backup saved to ${data.backupPath || "Backups/reseed"}. Reset cleared ${data.ordersCount || 0} orders, ${data.orderItemsCount || 0} order items, and ${data.inventoryCount || 0} inventory rows.`,
"success",
);
} catch (error) {
setFeedback(reseedFeedback, error instanceof Error ? error.message : "The database backup or reset could not be completed.", "error");
} finally {
reseedTrigger.disabled = false;
reseedTrigger.textContent = "Backup and reseed database";
}
});
syncSession();
};
initClientReseedPage();
document.addEventListener("astro:page-load", initClientReseedPage);
</script>
</main>
</BaseLayout>