dograh/ui/AGENTS.md
Matt Van Horn dd85c4a1b4
fix: support object and array parameters in custom HTTP tools (#373)
* fix: support object and array parameters in custom HTTP tools

* feat(ui): expose object and array types in the custom tool parameter editor

* fix: error handling and schema generation

---------

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Abhishek Kumar <abhishek@a6k.me>
2026-06-02 11:35:38 +05:30

3.7 KiB

UI - Frontend Application

Next.js 15 frontend for the Dograh voice AI platform.

Project Structure

ui/
├── src/
│   ├── app/          # Next.js App Router pages
│   ├── components/   # React components
│   ├── lib/          # Utilities and configurations
│   ├── client/       # Auto-generated API client
│   ├── context/      # React context providers
│   ├── hooks/        # Custom React hooks
│   ├── constants/    # Application constants
│   └── types/        # TypeScript type definitions
├── public/           # Static assets
└── package.json

Where to Find Things

Looking for... Go to...
Pages/routes src/app/ - Next.js App Router (file-based routing)
Reusable components src/components/ - organized by feature
Base UI primitives src/components/ui/ - shadcn/ui components
Workflow builder src/components/flow/ - React Flow based
API calls src/client/ - auto-generated from OpenAPI spec
Auth utilities src/lib/auth/
Helper functions src/lib/utils.ts
Global state src/context/ - React context providers

Tech Stack

  • Next.js 15 with App Router, React 19, TypeScript
  • Tailwind CSS with shadcn/ui components
  • Zustand for state management
  • @xyflow/react for workflow builder

API Client

The src/client/ directory is auto-generated from the backend OpenAPI spec. Whenever you add a new api route in backend, and wish to use it in the UI, generate the client using below command.

npm run generate-client

Conventions

File Uploads

Always use a hidden <input type="file"> with a visible <Button> that triggers it via fileInputRef.current?.click(). Never use a visible <Input type="file"> — the native file input styling is inconsistent and confusing. Show the selected filename next to or below the button.

Authenticated API Calls

Components that make API calls must wait for auth to be ready before fetching. Use useAuth() and guard the useEffect with authLoading and user:

const { user, loading: authLoading } = useAuth();
const hasFetched = useRef(false);

useEffect(() => {
  if (authLoading || !user || hasFetched.current) return;
  hasFetched.current = true;
  fetchData();
}, [authLoading, user]);

The auth interceptor (which attaches the Bearer token) is only registered once auth is fully loaded. Fetching before that sends unauthenticated requests that silently fail.

API Error Handling

The generated client does not throw on HTTP error responses — it resolves to { data, error }. A try/catch only catches network failures, so a 4xx/5xx slips through silently if you only check response.data. Always check response.error:

const response = await someApiCall({ ... });
if (response.error) {
  setError(detailFromError(response.error, "Failed to save thing"));
  return;
}
// ...use response.data

Use detailFromError from @/lib/apiError to turn the error into a string — never render error.detail directly. FastAPI returns detail as a string for HTTPException but as an array of { msg, loc, ... } objects for 422 validation errors; passing that array to React ({error}) crashes the page with "Objects are not valid as a React child". The helper normalizes both shapes and takes an optional fallback message.

Development

npm install
npm run dev    # Runs on port 3000