Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
a0267da
feat(*): Admin flow initiate
Ayush8923 Mar 30, 2026
feea8c5
fix(*): added the note of after creating the key
Ayush8923 Mar 30, 2026
39604a2
fix(*): added the index.ts file for easy to import from the index
Ayush8923 Mar 30, 2026
fc7837f
fix(*): off the exhaustive deps
Ayush8923 Mar 30, 2026
e355d4a
fix(*): added the skeleton loader
Ayush8923 Mar 30, 2026
d4d4f1c
fix(*): some cleanups
Ayush8923 Mar 30, 2026
a4da445
fix(*): few clenaups
Ayush8923 Mar 31, 2026
713e9ad
feat(*): google integration flow
Ayush8923 Mar 31, 2026
06acfdc
feat(*): added the logout api endpoint
Ayush8923 Mar 31, 2026
b2ef783
fix(*): align the bottom action items tab
Ayush8923 Mar 31, 2026
3fbc5d3
fix(*): UI updates and users flow
Ayush8923 Apr 1, 2026
a236f2d
fix(*): small updates
Ayush8923 Apr 1, 2026
7e45d41
fix(*): some cases handling & UI updates
Ayush8923 Apr 2, 2026
e57c2e4
fix(*): some edge cases handled
Ayush8923 Apr 2, 2026
0e4d1dc
fix(*): authentication handle in the knowledge base
Ayush8923 Apr 2, 2026
8c4e3cc
fix(*): implement the edit projects
Ayush8923 Apr 2, 2026
bd1e94f
Merge branch 'feat/admin-flow' of https://github.com/ProjectTech4DevA…
Ayush8923 Apr 2, 2026
1b6598b
Merge branch 'feat/admin-flow' of https://github.com/ProjectTech4DevA…
Ayush8923 Apr 2, 2026
525f850
fix(*): remove the js comment
Ayush8923 Apr 2, 2026
2e3c91b
Merge branch 'main' of https://github.com/ProjectTech4DevAI/kaapi-fro…
Ayush8923 Apr 3, 2026
8a98eeb
Merge branch 'feat/admin-flow' of https://github.com/ProjectTech4DevA…
Ayush8923 Apr 3, 2026
32826e2
fix(*): some apikey intilization
Ayush8923 Apr 3, 2026
b6d59f8
fix(*): some of the UI updates
Ayush8923 Apr 3, 2026
3301f9d
fix(*): update the api response data
Ayush8923 Apr 4, 2026
9e14e58
fix(*): cleanups & handling refresh token
Ayush8923 Apr 5, 2026
be27625
fix(*): update the default setting route
Ayush8923 Apr 5, 2026
28513c8
Merge branch 'main' into feat/admin-flow
Ayush8923 Apr 6, 2026
9959073
Merge branch 'feat/admin-flow' into feat/google-integration
Ayush8923 Apr 6, 2026
1726212
Merge branch 'main' into feat/admin-flow
Ayush8923 Apr 7, 2026
4a0e744
Merge branch 'feat/admin-flow' into feat/google-integration
Ayush8923 Apr 7, 2026
275fcfc
fix(*): centralize the local storage key and clear the local storage …
Ayush8923 Apr 7, 2026
376e838
Merge branch 'main' of https://github.com/ProjectTech4DevAI/kaapi-fro…
Ayush8923 Apr 8, 2026
e223533
Merge branch 'feat/admin-flow' of https://github.com/ProjectTech4DevA…
Ayush8923 Apr 8, 2026
adb54ec
Merge branch 'feat/admin-flow' of https://github.com/ProjectTech4DevA…
Ayush8923 Apr 8, 2026
05bd0d3
fix(*): UI updates
Ayush8923 Apr 8, 2026
4c32ea0
Merge branch 'main' into feat/admin-flow
Ayush8923 Apr 8, 2026
e0c4783
Merge branch 'feat/admin-flow' into feat/google-integration
Ayush8923 Apr 8, 2026
06b672b
cleanups(*): stt and tts flow
Ayush8923 Apr 8, 2026
f985a1e
fix(*): few edge cases handles
Ayush8923 Apr 8, 2026
c206f3a
feat(*): User invitation flow
Ayush8923 Apr 8, 2026
fa0490f
fix(*): move invite page inside the auth group
Ayush8923 Apr 8, 2026
a20bb4d
fix(*): few updates on invite page
Ayush8923 Apr 9, 2026
16f139e
Feat: Google Integration (#102)
Ayush8923 Apr 9, 2026
44cd484
Merge branch 'feat/admin-flow' of https://github.com/ProjectTech4DevA…
Ayush8923 Apr 9, 2026
bed2d71
fix(*): update the invite page
Ayush8923 Apr 9, 2026
250769d
Merge branch 'main' of https://github.com/ProjectTech4DevAI/kaapi-fro…
Ayush8923 Apr 10, 2026
797d5ec
Merge branch 'main' into feat/invitation-flow
Ayush8923 Apr 14, 2026
f8358e8
Merge branch 'main' into feat/invitation-flow
Ayush8923 Apr 15, 2026
ba64472
Merge branch 'main' into feat/invitation-flow
Ayush8923 Apr 17, 2026
b8bc2cf
Auth: Email Login Flow (#114)
Ayush8923 Apr 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 144 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Commands

```bash
npm run dev # Start dev server at http://localhost:3000
npm run lint # Run ESLint
npm run build # Production build
npm run format # Format TS/tsx with Prettier
```

## Architecture Overview

### Tech Stack

- **Framework**: Next.js 16.0.7 (App Router)
- **React 19.2.0** + **TypeScript** (strict mode)
- **Routing**: Next.js App Router (react-router-dom is listed as a dependency but currently unused)
- **Styling**: Tailwind CSS 4.x + custom color system and styles defined in `/app/globals.css` for cases not supported by Tailwind
- **Data Fetching**: Native Fetch API + SWR 2.3.6 (used selectively where required)
- **Date/Time**: date-fns 4.1.0, date-fns-tz 3.2.0
- **ESLint**: Used for maintaining code quality, enforcing consistent coding standards, and catching potential issues during development and build time

### Directory Structure

| Path | Purpose |
| ------------------------------ | --------------------------------------------------------------------------------------------- |
| `app/(auth)/` | Authentication-related routes (e.g., invite, verify flows) |
| `app/(main)/` | Main application routes (dashboard-level features like datasets, evaluations, settings, etc.) |
| `app/api/` | Backend API route handlers (Next.js route handlers acting as BFF layer) |
| `app/components/` | App-scoped components used within routes/Pages |
| `app/components/icons/` | Hand-authored React icon components |
| `app/hooks/` | Custom React hooks specific to app features |
| `app/lib/` | Core shared logic and utilities across the application |
| `app/lib/context/` | React context providers (global state handling) |
| `app/lib/store/` | State management logic (custom/global store) |
| `app/lib/types/` | TypeScript type definitions (shared across modules) |
| `app/lib/utils/` | Domain-specific utility modules (e.g., evaluation, guardrails) |
| `app/lib/data/` | Static data and validators (e.g., guardrails validators) |
| `app/lib/apiClient.ts` | Centralized API client for forwarding requests to the backend |
| `app/lib/authCookie.ts` | Authentication cookie utilities (get/set/remove tokens) |
| `app/lib/configFetchers.ts` | API fetchers related to configuration modules |
| `app/lib/constants.ts` | Global constants used across the app |
| `app/lib/guardrailsClient.ts` | Client-side API helpers for guardrails features |
| `app/lib/models.ts` | Data models/interfaces for structured data handling |
| `app/lib/navConfig.ts` | Navigation configuration (sidebar/menu structure) |
| `app/lib/promptEditorUtils.ts` | Utility functions for prompt editor logic |
| `app/lib/utils.ts` | General utility/helper functions |
| `public/favicon.ico` | Application favicon |

## Import Aliases

[tsconfig.json](./tsconfig.json) sets paths: `{ "@/*": ["./*"] }`, so imports are resolved from the project root using the `@/` prefix. Use:

```
import { apiClient } from '@/app/lib/apiClient';
import { Providers } from '@/app/components/providers';
import { APP_NAME } from '@/app/lib/constants';
```

SVGs follow Next.js defaults (imported as static assets via next/image or referenced from /public).

## Routing & Role-Based Access

Routing uses the **Next.js App Router** exclusively. Routes are organized via route groups:

- `app/(auth)/` - unauthenticated flows (`/invite`, `/verify`)
- `app/(main)/` — authenticated app surface (`/evaluations`, `/datasets`, `/configurations`, `/guardrails`, `/knowledge-base`, `/settings`, etc.)

Role gating lives in middleware.ts and reads a kaapi_role cookie with two values:

- `user` - standard authenticated user
- `superuser` - admin; required for `/settings/*`

The cookie is issued server-side by [authCookie.ts](app/lib/authCookie.ts) after login/verify based on user.is_superuser. Middleware classifies each request into one of:

- `PUBLIC_ROUTES` — open to everyone (`/evaluations`, `/invite`, `/verify`, `/coming-soon/*`)
- `GUEST_ONLY_ROUTES` — unauthenticated only (`/keystore`); authenticated users are redirected to `/evaluations`
- `/settings/*` — superuser only
- Everything else — any authenticated user

There is no dynamic/custom role system; only the two static roles above.

## Toast Notifications

Toasts are managed via a React Context provider ([Toast.tsx](app/components/Toast.tsx)), mounted once in [Providers.tsx](app/components/providers/Providers.tsx). Consume them from any client component:

```
import { useToast } from '@/app/components/Toast';
// or the re-export: import { useToast } from '@/app/hooks/useToast';

function MyComponent() {
const toast = useToast();

toast.success('Saved successfully'); // success toast
toast.error('Something went wrong'); // error toast
toast.warning('Heads up'); // warning toast
toast.info('FYI'); // info toast

// Optional: override the default 5000ms auto-dismiss
toast.success('Saved', 3000);

// Low-level API (type + duration)
toast.addToast('Custom message', 'success', 4000);
}
```

## Authentication [AuthContext.tsx](app/lib/context/AuthContext.tsx)

There is no `AuthService` class. Auth state is owned by a React Context provider (`AuthProvider`) mounted in [Providers.tsx](app/components/providers/Providers.tsx), and consumed via the `useAuth()` hook:

```
import { useAuth } from '@/app/lib/context/AuthContext';

function MyComponent() {
const {
isAuthenticated, isHydrated,
session, currentUser, googleProfile,
apiKeys, activeKey, addKey, removeKey, setKeys,
loginWithToken, logout,
} = useAuth();
}
```

## App Context [AppContext.tsx](app/lib/context/AppContext.tsx)

Sidebar state is managed via `AppProvider`, consumed with `useApp()`:

```
import { useApp } from '@/app/lib/context/AppContext';

const { sidebarCollapsed, setSidebarCollapsed, toggleSidebar } = useApp();
```

## API Client & Error Handling

The BFF layer uses [apiClient.ts](app/lib/apiClient.ts) which forwards requests from Next.js route handlers to the backend at `BACKEND_URL` (defaults to `http://localhost:8000`). Key patterns:

- **Server-side (route handlers)**: Use `apiClient(request, endpoint, options)` — it relays `X-API-KEY` and `Cookie` headers automatically and returns `{ status, data, headers }`.
- **Client-side**: Use `clientFetch(endpoint, options)` — handles token refresh on 401, dispatches `AUTH_EXPIRED_EVENT` when refresh fails, and throws with a message extracted from `error`, `message`, or `detail` fields in the response body.
- **Error extraction**: `extractErrorMessage(body, fallback)` reads `body.error || body.message || body.detail` — follow this pattern when adding new API routes.
- **Auth expiry**: On 401 with failed refresh, a `CustomEvent(AUTH_EXPIRED_EVENT)` is dispatched on `window`, which `AuthContext` listens to for automatic logout.
42 changes: 42 additions & 0 deletions app/(auth)/invite/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"use client";

import { Suspense } from "react";
import { useSearchParams } from "next/navigation";
import { SpinnerIcon } from "@/app/components/icons";
import TokenVerifyPage from "@/app/components/auth/TokenVerifyPage";

function InviteContent() {
const searchParams = useSearchParams();

return (
<TokenVerifyPage
token={searchParams.get("token")}
apiUrl="/api/auth/invite"
title={{
verifying: "Verifying invitation",
success: "Welcome aboard!",
error: "Something went wrong",
}}
description={{
verifying: "Hang tight — we're setting up your account.",
success: "Your account has been activated. Redirecting to dashboard...",
}}
errorFallback="Invitation link is invalid or has expired."
helpText="If this keeps happening, please contact your organization administrator for a new invitation link."
/>
);
}

export default function InvitePage() {
return (
<Suspense
fallback={
<div className="min-h-screen bg-bg-secondary flex items-center justify-center">
<SpinnerIcon className="w-8 h-8 text-text-secondary animate-spin" />
</div>
}
>
<InviteContent />
</Suspense>
);
}
42 changes: 42 additions & 0 deletions app/(auth)/verify/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"use client";

import { Suspense } from "react";
import { useSearchParams } from "next/navigation";
import { SpinnerIcon } from "@/app/components/icons";
import TokenVerifyPage from "@/app/components/auth/TokenVerifyPage";

function VerifyContent() {
const searchParams = useSearchParams();

return (
<TokenVerifyPage
token={searchParams.get("token")}
apiUrl="/api/auth/magic-link/verify"
title={{
verifying: "Signing you in",
success: "You're in!",
error: "Login link failed",
}}
description={{
verifying: "Verifying your login link...",
success: "Redirecting to dashboard...",
}}
errorFallback="Login link is invalid or has expired."
helpText="Login links expire after 15 minutes. Please request a new one from the login page."
/>
);
}

export default function VerifyPage() {
return (
<Suspense
fallback={
<div className="min-h-screen bg-bg-secondary flex items-center justify-center">
<SpinnerIcon className="w-8 h-8 text-text-secondary animate-spin" />
</div>
}
>
<VerifyContent />
</Suspense>
);
}
15 changes: 10 additions & 5 deletions app/(main)/configurations/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { colors } from "@/app/lib/colors";
import { usePaginatedList, useInfiniteScroll } from "@/app/hooks";
import ConfigCard from "@/app/components/ConfigCard";
import Loader, { LoaderBox } from "@/app/components/Loader";
import { EvalJob } from "@/app/components/types";
import { EvalJob } from "@/app/lib/types/evaluation";
import {
ConfigPublic,
ConfigVersionItems,
Expand Down Expand Up @@ -271,7 +271,10 @@ export default function ConfigLibraryPage() {
</button>
</div>

<div ref={scrollRef} className="flex-1 overflow-auto p-6">
<div
ref={scrollRef}
className="flex-1 overflow-y-auto overflow-x-hidden p-6"
>
{isLoading ? (
<LoaderBox message="Loading configurations..." size="md" />
) : error ? (
Expand Down Expand Up @@ -341,11 +344,13 @@ export default function ConfigLibraryPage() {
) : (
<>
<div
className="grid gap-4 items-start"
style={{ gridTemplateColumns: `repeat(${columnCount}, 1fr)` }}
className="grid gap-4 items-start w-full"
style={{
gridTemplateColumns: `repeat(${columnCount}, minmax(0, 1fr))`,
}}
>
{columns.map((col, colIdx) => (
<div key={colIdx} className="flex flex-col gap-4">
<div key={colIdx} className="flex flex-col gap-4 min-w-0">
{col.map((config) => (
<ConfigCard
key={config.id}
Expand Down
6 changes: 1 addition & 5 deletions app/(main)/configurations/prompt-editor/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -380,10 +380,7 @@ function PromptEditorContent() {
};

return (
<div
className="w-full h-screen flex flex-col"
style={{ backgroundColor: colors.bg.secondary }}
>
<div className="w-full h-screen flex flex-col bg-bg-secondary">
<div className="flex flex-1 overflow-hidden">
<Sidebar
collapsed={sidebarCollapsed}
Expand Down Expand Up @@ -470,7 +467,6 @@ function PromptEditorContent() {
/>
) : (
<div className="flex-1 flex flex-col overflow-hidden">
{/* Split View: Prompt (left) + Config (right) */}
<div className="flex flex-1 overflow-hidden">
<div
className="flex"
Expand Down
Loading
Loading