Files
krow_talent_app/src/pages/admin/AdminRoute.tsx
Aravind fcdaa32f4d chore(ts-migration): migrate the non-agent pages to TypeScript
Phase 9, first batch: 18 pages. Seventeen emit byte-identical JavaScript, the
eighteenth differs only by a JSDoc cast becoming a real annotation, and the
production bundle is byte-identical to be49184.

SCOPE, decided by imports. Twenty-two of the forty pages import `lib/skills`,
`lib/agents`, `ai-assistant`, `components/skills`, `ui-tree` or `ui-editor`, and
are deferred to Phase 11 with the rest of that region. `Owliver.jsx` is in THIS
batch despite the name: it imports only `krowHooks`, `krowScore`, `krowAi` and
`ui/button`, and is a profile-building conversation page rather than any part of
the agent runtime.

`DesignSystem` is the headline result. It was the single worst file in Phase 6 -
111 errors when the design system was first renamed - and it arrived here with
none, because those were never its errors: they were the primitives' inferred
props, and fixing them at the source fixed every consumer.

Three pages needed real work, and each was the author's own intent made
explicit:

  - `admin/Login` carried `useState(/** @type {{email?: string, password?: string}} */ ({}))`.
    JSDoc casts stop applying in a `.tsx` file, so that became a real type
    argument, and `validate`'s accumulator - built empty and filled per failed
    rule - needed the same shape. The login flow itself is untouched: the
    generic 401 message, the 429 branch and the `remember` field all stand.

  - `Owliver` gets `new Promise<void>`, because its `resolve()` takes no
    argument.

  - `Candidates` names the element type of a `Set` built from `any[]`, which
    otherwise infers `Set<unknown>` and makes every option a `ReactNode` error.

LINT CAUGHT A REGRESSION THE OTHER CHECKS DID NOT. The generator adds an entity
import when it sees the type NAME anywhere in the file, which for four pages
with no props at all left an import nothing used - four `unused-imports` ERRORS,
taking `npm run lint` from exit 0 to exit 1 while typecheck, tests, the bundle
and seventeen of eighteen emit comparisons all stayed green. Removed. The
generator's import rule is too eager and wants narrowing before the next batch.

Verified: tsc 21 -> 21, set-difference showing zero introduced and zero removed;
zero errors in any of the 18 pages; lint back to 0 errors and 289 warnings;
npm test 1684/1691 with the same seven failures; Owliver baseline 59/59;
production bundle byte-identical; baseline artifacts untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBG1wnuRfJKCstGB8Fekr8
2026-09-18 12:47:04 +05:30

38 lines
1.6 KiB
TypeScript

import { Navigate, Outlet, useLocation } from 'react-router-dom';
import { useAuth } from '@/lib/AuthContext';
/**
* Gate on the Admin console.
*
* It asks the auth context, which reflects `GET /me` — a real question to the
* server about a real session — rather than the `sessionStorage` flag it used
* to read. That flag could be set from the browser console; a session row keyed
* by an HttpOnly cookie cannot.
*
* This guard is deliberately kept even though it now sits inside
* `ProtectedRoute`, which checks the same thing. It is not redundant defence in
* depth for its own sake: it is the component that owns "the Admin console is
* entered through its own sign-in", and it is where a role check will go in
* Phase 3D, when being signed in stops being sufficient to be here.
*
* The attempted path travels along in location state, so signing in returns you
* to where you were going rather than dropping you on the Control Center. That
* matters for a bookmarked deep link — an operator who saved /admin/activity
* should land on Activity, not have to navigate there again.
*/
export default function AdminRoute() {
const location = useLocation();
const { isAuthenticated, isLoadingAuth, authChecked } = useAuth();
// ProtectedRoute has already waited for the check, so this is belt and
// braces: without it a direct render of this route would redirect to the
// login page during the first tick and bounce a signed-in operator out.
if (isLoadingAuth || !authChecked) return null;
if (!isAuthenticated) {
return <Navigate to="/admin/login" replace state={{ from: location.pathname }} />;
}
return <Outlet />;
}