here’s a copy-paste admin router stub for the frontend plus tiny helpers, so your staff area lives at /admin → /admin/login → /admin/dashboard. It won’t touch your customer auth, profile, or paywall.

1) Add the admin pages

src/pages/Admin/AdminLogin.tsx

import { useState } from "react";
import { signInAdminWithGoogle } from "@/admin/firebaseClient";

export default function AdminLogin() {
  const [err, setErr] = useState<string | null>(null);

  const handleSignIn = async () => {
    try {
      const token = await signInAdminWithGoogle();       // gets Firebase ID token w/ admin claim
      sessionStorage.setItem("adminToken", token);
      window.location.href = "/admin/dashboard";
    } catch (e: any) {
      setErr(e.message || "Sign-in failed");
    }
  };

  return (
    <div className="max-w-md mx-auto py-12">
      <h1 className="text-2xl font-bold mb-2">Staff Admin</h1>
      <p className="text-sm text-gray-600 mb-6">Company-only access</p>
      <button className="rounded-xl bg-black text-white px-4 py-2" onClick={handleSignIn}>
        Sign in with Google (Staff)
      </button>
      {err && <div className="mt-4 text-red-600 text-sm">{err}</div>}
    </div>
  );
}


src/pages/Admin/Dashboard.tsx

import { useEffect, useState } from "react";
import { requireAdminOrRedirect } from "@/admin/adminGuard";

export default function Dashboard() {
  const [stats, setStats] = useState<any>(null);

  useEffect(() => {
    const token = requireAdminOrRedirect(); // redirects to /admin/login if missing
    if (!token) return;

    fetch("/api/admin/users/count", {
      headers: { Authorization: `Bearer ${token}` },
      credentials: "include",
    })
      .then(r => r.json())
      .then(setStats)
      .catch(() => setStats({ error: "Failed to load stats" }));
  }, []);

  return (
    <div className="max-w-2xl mx-auto py-10">
      <h1 className="text-2xl font-bold mb-4">Admin Dashboard</h1>
      <pre className="text-sm bg-gray-50 p-4 rounded-xl overflow-auto">
        {JSON.stringify(stats, null, 2)}
      </pre>
    </div>
  );
}


src/pages/Admin/AdminHome.tsx

import { useEffect } from "react";
import { isAdminAuthed } from "@/admin/adminGuard";

export default function AdminHome() {
  useEffect(() => {
    if (isAdminAuthed()) window.location.replace("/admin/dashboard");
    else window.location.replace("/admin/login");
  }, []);
  return null;
}

2) Add tiny admin helpers

src/admin/firebaseClient.ts

import { initializeApp } from "firebase/app";
import { getAuth, signInWithPopup, GoogleAuthProvider, getIdTokenResult } from "firebase/auth";

const app = initializeApp({
  apiKey: import.meta.env.VITE_FB_API_KEY,
  authDomain: import.meta.env.VITE_FB_AUTH_DOMAIN,
  projectId: import.meta.env.VITE_FB_PROJECT_ID,
});

export const auth = getAuth(app);

export async function signInAdminWithGoogle(): Promise<string> {
  const provider = new GoogleAuthProvider();
  const cred = await signInWithPopup(auth, provider);
  const token = await cred.user.getIdToken(true);
  const claims = (await getIdTokenResult(cred.user)).claims as any;
  if (!claims.admin) throw new Error("Not an admin account");
  return token;
}


src/admin/adminGuard.ts

export function isAdminAuthed(): boolean {
  return !!sessionStorage.getItem("adminToken");
}

export function requireAdminOrRedirect(): string | null {
  const token = sessionStorage.getItem("adminToken");
  if (!token) {
    window.location.replace("/admin/login");
    return null;
  }
  return token;
}

export function adminSignOut() {
  sessionStorage.removeItem("adminToken");
  window.location.replace("/admin/login");
}

3) Wire routes into your React Router

Where you define routes (e.g., src/App.tsx or your router file):

// import your admin pages
import AdminHome from "@/pages/Admin/AdminHome";
import AdminLogin from "@/pages/Admin/AdminLogin";
import Dashboard from "@/pages/Admin/Dashboard";

// inside <Routes> ...
<Route path="/admin" element={<AdminHome />} />
<Route path="/admin/login" element={<AdminLogin />} />
<Route path="/admin/dashboard" element={<Dashboard />} />


No public nav link is added; staff can type /admin directly.

4) Server is already ready

You’ve got the admin API guard we set up earlier:

server/admin/firebaseAdmin.ts (verifies Firebase ID token + admin claim)

server/admin/routes.ts (e.g., /api/admin/users/count)

If you haven’t yet, mount it:

// server/index.ts
import adminRoutes from "./admin/routes";
app.use(adminRoutes);

5) Env check (frontend + backend)

Frontend (.env)

VITE_FB_API_KEY=...
VITE_FB_AUTH_DOMAIN=...
VITE_FB_PROJECT_ID=...


Backend (.env)

FIREBASE_PROJECT_ID=...
FIREBASE_CLIENT_EMAIL=...
FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"


Give your staff users the admin custom claim once (script we included before). After that, they can log in via Google and access /admin/*.