mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat: add "Hello from the otter side" easter eggs
Custom 404 pages for landing and docs sites, otter greeting on the first-login sidebar, and a one-time welcome toast after password change.
This commit is contained in:
@@ -16,5 +16,59 @@ const { Layout } = DefaultTheme;
|
||||
<GitHubStars />
|
||||
</div>
|
||||
</template>
|
||||
<template #not-found>
|
||||
<div class="not-found-page">
|
||||
<span class="not-found-otter">🦦</span>
|
||||
<h1 class="not-found-heading">Hello from the otter side!</h1>
|
||||
<p class="not-found-text">This page swam away. Let's get you back on track.</p>
|
||||
<a href="/" class="not-found-link">Back to docs</a>
|
||||
</div>
|
||||
</template>
|
||||
</Layout>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.not-found-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 60vh;
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.not-found-otter {
|
||||
font-size: 4.5rem;
|
||||
}
|
||||
|
||||
.not-found-heading {
|
||||
margin-top: 1.5rem;
|
||||
font-size: 2.25rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.not-found-text {
|
||||
margin-top: 1rem;
|
||||
font-size: 1.125rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.not-found-link {
|
||||
margin-top: 2rem;
|
||||
display: inline-block;
|
||||
padding: 0.625rem 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
background: var(--vp-c-brand-1);
|
||||
color: var(--vp-c-white);
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.not-found-link:hover {
|
||||
background: var(--vp-c-brand-2);
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Footer } from "@/components/footer";
|
||||
import { Navbar } from "@/components/navbar";
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<main className="flex min-h-screen flex-col items-center justify-center px-6 pt-16">
|
||||
<span className="text-7xl">🦦</span>
|
||||
<h1 className="mt-6 text-4xl font-bold tracking-tight sm:text-5xl">
|
||||
Hello from the otter side!
|
||||
</h1>
|
||||
<p className="mt-4 text-lg text-muted">
|
||||
This page swam away. Let's get you back on track.
|
||||
</p>
|
||||
<a
|
||||
href="/"
|
||||
className="mt-8 rounded-lg bg-accent px-6 py-3 text-sm font-medium text-accent-foreground transition-colors hover:bg-accent-hover"
|
||||
>
|
||||
Back to homepage
|
||||
</a>
|
||||
</main>
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
+10
-1
@@ -1,7 +1,7 @@
|
||||
import { APP_VERSION, en, shouldShowConsent } from "@snapotter/shared";
|
||||
import { Component, type ErrorInfo, lazy, type ReactNode, Suspense, useEffect } from "react";
|
||||
import { BrowserRouter, Navigate, Route, Routes, useLocation } from "react-router-dom";
|
||||
import { Toaster } from "sonner";
|
||||
import { Toaster, toast } from "sonner";
|
||||
import { ConnectionMonitor } from "./components/common/connection-monitor";
|
||||
import { KeyboardShortcutProvider } from "./components/common/keyboard-shortcut-provider";
|
||||
import { I18nProvider } from "./contexts/i18n-context";
|
||||
@@ -186,6 +186,15 @@ export function App() {
|
||||
fetchAnalyticsConfig();
|
||||
}, [fetchAnalyticsConfig]);
|
||||
|
||||
useEffect(() => {
|
||||
if (localStorage.getItem("snapotter-welcome") === "1") {
|
||||
localStorage.removeItem("snapotter-welcome");
|
||||
toast("Hello from the otter side! 🦦", {
|
||||
duration: 5000,
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!analyticsConfigLoaded ||
|
||||
|
||||
@@ -100,6 +100,7 @@ export function ChangePasswordPage() {
|
||||
return;
|
||||
}
|
||||
|
||||
localStorage.setItem("snapotter-welcome", "1");
|
||||
// Trigger browser password save prompt via real form submission + navigation
|
||||
const username = localStorage.getItem("snapotter-username") || "admin";
|
||||
triggerBrowserPasswordSave(username, newPassword);
|
||||
@@ -218,6 +219,7 @@ export function ChangePasswordPage() {
|
||||
</div>
|
||||
<div className="hidden lg:flex flex-1 bg-primary/90 items-center justify-center p-12 text-white rounded-s-3xl">
|
||||
<div className="max-w-lg space-y-6 text-center">
|
||||
<span className="text-7xl">🦦</span>
|
||||
<h2 className="text-3xl font-bold">{t.changePassword.sidebarTitle}</h2>
|
||||
<p className="text-lg text-white/80">{t.changePassword.sidebarDescription}</p>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user