mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix(demo): boot to dashboard, sample-data notice, robust mobile editor icon (#464)
The demo signs in as an admin on load (no login/change-password screen; /login and /change-password bounce to the dashboard), authEnabled stays true so the People/Teams/Roles/Security settings tabs remain available, the banner notes the admin data is sample data, and the mobile editor icon is an inline SVG so it always renders.
This commit is contained in:
@@ -8,7 +8,8 @@ export function DemoBanner() {
|
||||
return (
|
||||
<div className="relative z-[9999] flex items-center justify-center gap-3 bg-primary px-4 py-2 text-sm text-primary-foreground">
|
||||
<span>
|
||||
This is a live demo. Processing is disabled.{" "}
|
||||
This is a live demo. All users, teams, and activity shown are sample data, and file
|
||||
processing is disabled.{" "}
|
||||
<a
|
||||
href="https://github.com/snapotter-hq/SnapOtter"
|
||||
target="_blank"
|
||||
|
||||
@@ -736,9 +736,11 @@ export function matchDemoRoute(url: string, method: string, body?: unknown): Res
|
||||
}
|
||||
|
||||
if (path === "/api/auth/session" && method === "GET") {
|
||||
const token = localStorage.getItem("snapotter-token");
|
||||
if (!token) return json({ error: "Unauthorized" }, 401);
|
||||
const state = loadState();
|
||||
// The demo is always signed in as an admin, so it boots straight into the
|
||||
// dashboard with no login or change-password screen, and every settings
|
||||
// tab (including the auth-gated People/Teams/Roles/Security ones) stays
|
||||
// available. mustChangePassword is always false; the session is returned
|
||||
// regardless of token so a stray logout can't drop you onto /login.
|
||||
return json({
|
||||
user: {
|
||||
id: 1,
|
||||
@@ -746,7 +748,8 @@ export function matchDemoRoute(url: string, method: string, body?: unknown): Res
|
||||
displayName: "Demo User",
|
||||
role: "admin",
|
||||
permissions: PERMISSIONS,
|
||||
mustChangePassword: !state.passwordChanged,
|
||||
mustChangePassword: false,
|
||||
mfaRequired: false,
|
||||
loginMethod: "local",
|
||||
hasLocalPassword: true,
|
||||
},
|
||||
@@ -1036,7 +1039,26 @@ export function matchDemoRoute(url: string, method: string, body?: unknown): Res
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Boot the demo as an already-signed-in admin so there is no login screen.
|
||||
* Runs before the app mounts: seeds an auth token and, if the URL is a login
|
||||
* or change-password route (manual navigation, or the logout redirect),
|
||||
* rewrites it to the dashboard before React Router reads the location.
|
||||
*/
|
||||
function primeDemoAuth() {
|
||||
try {
|
||||
localStorage.setItem("snapotter-token", "demo-token");
|
||||
localStorage.setItem("snapotter-username", "demo");
|
||||
} catch {}
|
||||
const p = window.location.pathname;
|
||||
if (p === "/login" || p === "/change-password") {
|
||||
window.history.replaceState(null, "", "/");
|
||||
}
|
||||
}
|
||||
|
||||
export function installMocks() {
|
||||
primeDemoAuth();
|
||||
|
||||
const originalFetch = window.fetch.bind(window);
|
||||
|
||||
window.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
|
||||
|
||||
@@ -1,19 +1,32 @@
|
||||
/**
|
||||
* Image-editor glyph for the mobile bottom nav: a framed photo with an editing
|
||||
* pencil. Rendered as an inline SVG (like the sibling Lucide nav icons) so it
|
||||
* has no external-asset dependency. It previously used a CSS mask over
|
||||
* /edit-image.png, which rendered as nothing whenever that asset failed to load
|
||||
* (e.g. it was missing from the demo build), leaving the icon invisible.
|
||||
*/
|
||||
export function ImageEditIcon({ className }: { className?: string }) {
|
||||
return (
|
||||
<span
|
||||
<svg
|
||||
className={className}
|
||||
style={{
|
||||
display: "inline-block",
|
||||
backgroundColor: "currentColor",
|
||||
maskImage: "url(/edit-image.png)",
|
||||
maskSize: "contain",
|
||||
maskRepeat: "no-repeat",
|
||||
maskPosition: "center",
|
||||
WebkitMaskImage: "url(/edit-image.png)",
|
||||
WebkitMaskSize: "contain",
|
||||
WebkitMaskRepeat: "no-repeat",
|
||||
WebkitMaskPosition: "center",
|
||||
}}
|
||||
/>
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M19 11V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h5" />
|
||||
<circle cx="7.5" cy="8" r="1.6" />
|
||||
<path d="M3.5 15.5 6.5 12.5 9 15 12 12" />
|
||||
<g transform="translate(10.8 8.2) scale(0.62)">
|
||||
<path
|
||||
vectorEffect="non-scaling-stroke"
|
||||
d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"
|
||||
/>
|
||||
<path vectorEffect="non-scaling-stroke" d="m15 5 4 4" />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user