# Project export: Orbit

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 12.0
- Tagline: AI that maps your day
- Devpost: https://devpost.com/software/orbit-do5ezr
- GitHub: https://github.com/Nishchal-007/orbit
- Video: https://www.youtube.com/embed/vUQe0OdPliw?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 2 GitHub contributor(s) — Nishchal-007 (5 commits), khalatevarun (3 commits)

## Devpost submission (written by the team)

### Inspiration

The inspiration for Orbit came from a simple frustration we all face daily: wasting time planning our day. Whether it's figuring out the optimal order to run errands, scheduling meetings around travel time, or coordinating multiple tasks across different locations, manual planning is inefficient and time-consuming. The vision was to create something as simple as adding tasks and locations, then letting AI handle the rest - from intelligent prioritization to actual driving route optimization using real Google Maps data. What We Learned Technical Insights Google Routes API v2 provides incredibly detailed route data with actual driving distances, traffic-aware routing, and precise polyline data Claude AI excels at understanding context and constraints when planning tasks, considering factors like priority, location proximity, and time dependencies Voice input integration with Deepgram + Claude creates a seamless natural language interface for task creation GeoJSON LineString format provides the most accurate polyline rendering for complex routes How We Built It Architecture Frontend: Next.js 16 with React 19, Tailwind CSS, and shadcn/ui components Maps: Google Maps JavaScript API with Routes API v2 integration AI: Anthropic Claude API for intelligent task planning and voice parsing Voice: Deepgram Speech-to-Text API for natural language input Storage: Browser session storage Key Features Built Smart Task Management Voice input with AI parsing (Deepgram → Claude) Location-aware task creation Priority and duration settings Smart Task Management Voice input with AI parsing (Deepgram → Claude) Location-aware task creation Priority and duration settings AI Route Planning Claude AI analyzes tasks and creates optimal visit order Considers location proximity, priorities, and time constraints Provides reasoning for planning decisions AI Route Planning Claude AI analyzes tasks and creates optimal visit order Considers location proximity, priorities, and time constraints Provides reasoning for planning decisions Real Driving Routes Google Routes API v2 for accurate driving distances Traffic-aware routing with TRAFFIC_AWARE_OPTIMAL GeoJSON LineString polylines for precise route visualization Individual leg-by-leg route breakdown Real Driving Routes Google Routes API v2 for accurate driving distances Traffic-aware routing with TRAFFIC_AWARE_OPTIMAL GeoJSON LineString polylines for precise route visualization Individual leg-by-leg route breakdown Interactive Map Visualization Real-time route rendering with numbered waypoints Detailed route statistics (distance, duration, stops) Responsive design with mobile support Interactive Map Visualization Real-time route rendering with numbered waypoints Detailed route statistics (distance, duration, stops) Responsive design with mobile support Challenges We Faced 1. Google Routes API Migration Challenge: Migrating from Directions API to Routes API v2 required complete restructuring of request/response handling. Solution: Implemented geocoding preprocessing to convert addresses to coordinates Updated field names (waypoints → intermediates) Handled GeoJSON LineString format conversion Added comprehensive error handling for API limitations 2. Polyline Rendering Complexity Challenge: Routes API returns polylines in different formats (encoded strings vs GeoJSON arrays), causing rendering issues. Solution: Created robust polyline detection and conversion logic Implemented fallback rendering for different data formats Added extensive debugging logs to trace data flow Ensured coordinate format consistency ([lng, lat] → {lat, lng}) 3. Real-time Map Updates Challenge: New tasks weren't appearing on the map until page refresh. Solution: Implemented proper state management with onItemAdd callbacks Created reactive map updates triggered by task changes Added loading states and error handling for async operations 4. Voice Input Integration Challenge: Converting speech to structured task data required multiple API calls and error handling. Solution: Built a pipeline: Deepgram (speech → text) → Claude (text → structured data) Added loading states for each step Implemented fallback mechanisms for API failures Created intuitive UI feedback for voice input status 5. Performance Optimization Challenge: Multiple API calls and complex route calculations could cause UI lag. Solution: Implemented React useMemo for expensive calculations Added loading states and skeleton screens Optimized API calls with proper error boundaries Used session storage for data persistence Impact Orbit transforms daily planning from a time-consuming manual process into an intelligent, automated system. Users can: Save 2-3 hours per week on route planning and task organization Reduce travel time by 15-25% through AI-optimized routing Focus on execution rather than planning Make data-driven decisions about task prioritization Built with ❤️ for the calhacks. Stop planning. Start optimizing.

## README (from the GitHub repository)

# Orbit - Smart Route Planner

An AI-powered daily route planner that optimizes your schedule by combining calendar events and to-dos, then uses Claude AI for intelligent prioritization and Google Maps for route optimization.

## Features

- **AI-Powered Planning**: Uses Claude AI to intelligently prioritize and order your tasks
- **Real Driving Routes**: Google Maps provides actual car driving distances and routes (not straight-line)
- **Travel Time Estimates**: Accurate driving time calculations with traffic considerations
- **Calendar Events**: Import and manage your daily calendar events
- **To-Do Management**: Add flexible tasks with priorities and locations
- **Voice Input**: Use voice commands to add tasks with AI-powered parsing
- **Interactive Map**: Visualize your optimized route with markers and directions
- **Privacy-First**: All data stored locally in your browser
- **Demo Mode**: Pre-loaded with sample data for immediate testing

## Tech Stack

- **Frontend**: Next.js 16, React 19, Tailwind CSS
- **UI Components**: shadcn/ui
- **Maps**: Google Maps JavaScript API
- **Routing**: Google Routes API v2 (for accurate driving routes)
- **AI**: Anthropic Claude API
- **Voice**: Deepgram Speech-to-Text API
- **Storage**: Browser session storage (no database required)

## Quick Start

### 1. Install Dependencies

```bash
npm install
```

### 2. Set Up Environment Variables

Create a `.env.local` file in the root directory:

```env
# Claude API Key for AI planning
CLAUDE_API_KEY=your_claude_api_key_here

# Google Maps API Key (for both JavaScript API and Directions API)
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=your_google_maps_api_key_here

# Google Maps API Key for server-side API calls
GOOGLE_MAPS_API_KEY=your_google_maps_api_key_here

# Deepgram API Key for voice-to-text transcription
DEEPGRAM_API_KEY=your_deepgram_api_key_here

```

### 3. Get API Keys

**Claude API Key:**
1. Go to [console.anthropic.com](https://console.anthropic.com)
2. Sign up or log in
3. Create a new API key
4. Copy the key to your `.env.local` file as `CLAUDE_API_KEY`

**Google Maps API Key:**
1. Go to [Google Cloud Console](https://console.cloud.google.com)
2. Create a new project or select existing
3. Enable the following APIs:
   - Maps JavaScript API
   - **Routes API** (NEW - provides actual driving routes with polylines)
   - Geocoding API
4. Create credentials (API Key)
5. Copy the key to your `.env.local` file

**Deepgram & Claude API Keys (Optional - for AI voice input):**
1. **Deepgram**: Go to [Deepgram Console](https://console.deepgram.com), sign up, create an API key, and add to `.env.local` as `DEEPGRAM_API_KEY`
2. **Claude**: Ensure your `CLAUDE_API_KEY` is set (from step above)
3. Note: Voice input requires BOTH Deepgram (for transcription) and Claude (for intelligent parsing) API keys


### 4. Run the Development Server

```bash
npm run dev
```

Open [http://localhost:3000](http://localhost:3000) to see the application.

## Demo Script

1. **Load the Application**: Open http://localhost:3000
2. **Review Pre-loaded Data**: The app comes with sample calendar events and to-dos
3. **Add Custom Tasks**: Use the "To-Do List" tab to add your own tasks
   - **Type to add**: Enter task title, location, and priority
   - **AI Voice input**: Click "Voice Input" to speak your task - Deepgram transcribes it and Claude AI intelligently extracts task details, location, priority, and duration
4. **Plan Your Day**: Switch to the "Plan" tab and click "Plan My Day"
5. **View Optimized Route**: See the AI-generated itinerary with actual driving distances between each stop
6. **Test Re-routing**: Click "Recalculate Route" to see different optimizations
7. **Mark Tasks Complete**: Toggle tasks as done to see how it affects planning

## Project Structure

```
src/
├── app/
│   ├── api/
│   │   ├── claude/plan/route.js    # Claude AI planning endpoint
│   │   └── maps/
│   │       ├── directions/route.js # Google Maps Directions API
│   │       └── geocode/route.js    # Address geocoding
│   ├── layout.js                   # Root layout with metadata
│   └── page.js                     # Main dashboard page
├── components/
│   ├── ui/                         # shadcn/ui components
│   ├── CalendarEventsList.jsx      # Calendar events management
│   ├── TodoList.jsx               # To-do list management
│   ├── MapView.jsx                # Google Maps integration
│   ├── PlannerPanel.jsx           # AI planning interface
│   └── TaskCard.jsx               # Reusable task component
├── hooks/
│   └── useGoogleMaps.js           # Google Maps utilities
├── lib/
│   ├── storage.js                 # Session storage helpers
│   ├── seedData.js                # Demo data generation
│   └── claudePrompt.js            # AI prompt engineering
└── components.json                # shadcn/ui configuration
```

## API Endpoints

- `POST /api/claude/plan` - AI route planning
- `POST /api/maps/directions` - Google Maps route optimization
- `GET /api/maps/geocode` - Address geocoding

## Development

### Adding New Features

1. **New Components**: Add to `src/components/`
2. **API Routes**: Add to `src/app/api/`
3. **Utilities**: Add to `src/lib/`
4. **Hooks**: Add to `src/hooks/`

### Styling

The project uses Tailwind CSS with shadcn/ui components. All styling follows the design system defined in the shadcn configuration.

### State Management

The app uses React's built-in state management with:
- `useState` for local component state
- `useEffect` for side effects
- Session storage for data persistence

## Deployment

### Vercel (Recommended)

1. Push your code to GitHub
2. Connect your repository to Vercel
3. Add environment variables in Vercel dashboard
4. Deploy

### Other Platforms

The app can be deployed to any platform that supports Next.js:
- Netlify
- Railway
- Render
- AWS Amplify

## Troubleshooting

### Common Issues

1. **Maps not loading**: Check your Google Maps API key and ensure the required APIs are enabled
2. **AI planning fails**: Verify your Anthropic API key and check the console for errors
3. **Styling issues**: Ensure Tailwind CSS is properly configured and shadcn components are installed

### Debug Mode

Enable debug logging by adding `?debug=true` to the URL to see additional console output.

## Contributing

1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Test thoroughly
5. Submit a pull request

## License

MIT License - see LICENSE file for details.

## Detected evidence (automated analysis)

Indexed codebase: 35 recognized source files, 178 KB.
- Anthropic (technology) — detected in the code
- CSS (language) — detected in the code
- JavaScript (language) — detected in the code
- Next.js (technology) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code

## Codebase structure (from repository index)

### Files (42 of 42)

```
.gitignore
AI_VOICE_PARSING.md
components.json
cursor.md
eslint.config.mjs
jsconfig.json
next.config.mjs
package.json
postcss.config.mjs
README.md
src/app/api/calendar/events/route.js
src/app/api/claude/parse-todo/route.js
src/app/api/claude/plan/route.js
src/app/api/deepgram/transcribe/route.js
src/app/api/maps/directions/route.js
src/app/api/maps/geocode/route.js
src/app/api/maps/validate/route.js
src/app/globals.css
src/app/layout.js
src/app/page.js
src/components/CalendarEventsList.jsx
src/components/LandingPage.jsx
src/components/MapView.jsx
src/components/PlannerPanel.jsx
src/components/TaskCard.jsx
src/components/TodoList.jsx
src/components/ui/badge.jsx
src/components/ui/button.jsx
src/components/ui/card.jsx
src/components/ui/dialog.jsx
src/components/ui/input.jsx
src/components/ui/scroll-area.jsx
src/components/ui/separator.jsx
src/components/ui/tabs.jsx
src/components/VoiceInput.jsx
src/hooks/useGoogleCalendar.js
src/hooks/useGoogleMaps.js
src/lib/claudePrompt.js
src/lib/googleCalendar.js
src/lib/seedData.js
src/lib/storage.js
src/lib/utils.js
```

### Dependencies

- package.json: @anthropic-ai/sdk@^0.67.0, @deepgram/sdk@^4.11.2, @googlemaps/js-api-loader@^2.0.1, @googlemaps/polyline-codec@^1.0.28, @radix-ui/react-dialog@^1.1.15, @radix-ui/react-scroll-area@^1.2.10, @radix-ui/react-separator@^1.1.7, @radix-ui/react-slot@^1.2.3, @radix-ui/react-tabs@^1.1.13, @react-google-maps/api@^2.20.7, @tailwindcss/postcss@^4, class-variance-authority@^0.7.1, clsx@^2.1.1, date-fns@^4.1.0, eslint@^9, eslint-config-next@16.0.0, googleapis@^164.1.0, lucide-react@^0.548.0, next@16.0.0, react@19.2.0, react-dom@19.2.0, tailwind-merge@^3.3.1, tailwindcss@^4, tw-animate-css@^1.4.0

### Recent commits (newest first)

- fix hero pill
- Landing page
- Route planning
- Orbit V3
- Merge pull request #1 from Nishchal-007/feat/landing-page
- add landing page
- Orbit V2
- first commit

## Key source files (fetched from GitHub, selected and truncated for size)

### AI_VOICE_PARSING.md

```markdown
# AI-Powered Voice Input Enhancement

## Overview

The Orbit app now features intelligent voice input that uses a two-stage AI pipeline:
1. **Deepgram** - Converts speech to text
2. **Claude AI** - Parses and structures the text into task details

This enhancement allows users to speak naturally, and the AI intelligently extracts the task, location, priority, and duration estimates.

## How It Works

### Flow
```
User speaks → Deepgram transcription → Claude AI parsing → Auto-filled form fields
```

### Example
**User says:** "Buy groceries at Whole Foods urgent"

**Result:** 
- Title: "Buy groceries"
- Location: "Whole Foods"
- Priority: "high"
- Duration: 30 minutes

## Implementation Details

### New Files Created

1. **`/src/app/api/claude/parse-todo/route.js`**
   - API endpoint that uses Claude AI to parse transcripts
   - Extracts: title, location, priority, duration
   - Returns structured JSON data
   - Has fallback handling for parsing errors

### Modified Files

1. **`/src/components/VoiceInput.jsx`**
   - Added `parseTranscriptWithClaude()` function
   - New `onParsedData` callback prop
   - Added `isParsing` state for Claude processing
   - Enhanced UI with "Analyzing with AI..." state
   - Shows "AI is extracting task details..." message

2. **`/src/components/TodoList.jsx`**
   - Replaced `handleVoiceTranscript` with `handleVoiceParsedData`
   - Now uses Claude-parsed structured data
   - Auto-fills all form fields (title, location, priority, duration)

## Claude AI Parsing

The Claude parsing endpoint uses intelligent prompt engineering to extract:

### Extracted Data
- **Title**: Clean task description
- **Location**: Extracted from phrases like "at", "in", "to", "near"
- **Priority**: Detects urgency indicators ("urgent", "asap", "low priority", etc.)
- **Duration**: Estimates time based on task type

### Parsing Examples

| Voice Input | Title | Location | Priority | Duration |
|-------------|-------|----------|----------|----------|
| "Buy groceries at Whole Foods urgent" | Buy groceries | Whole Foods | high | 30 min |
| "Call doctor tomorrow" | Call doctor | | medium | 15 min |
| "Meeting with Sarah at Starbucks" | Meeting with Sarah | Starbucks | low | 30 min |
| "Pick up dry cleaning" | Pick up dry cleaning | | medium | 10 min |

## User Experience

### Visual Feedback

1. **Recording State**: "Recording..." with pulsing red dot
2. **Transcribing State**: "Transcribing..." with spinner
3. **Parsing State**: "Analyzing with AI..." with spinner and "AI is extracting task details..."
4. **Complete**: All fields auto-filled with parsed data

### Error Handling

- Falls back gracefully if Claude parsing fails
- Uses fallback data structure if needed
- Shows error messages for API failures
- Button disabled during processing states

## API Integration

### Endpoint: `POST /api/claude/parse-todo`

**Request:**
```json
{
  "transcript": "Buy groceries at Whole Foods urgent"
}
```

**Response:**
```json
{
  "success": true,
  "data"
[truncated — 1633 more characters]
```

### cursor.md

```markdown
You are an AI developer assistant. The user already has a Next.js project initialized and wants a hackathon-ready MVP for a smart daily route planner that syncs Google Calendar, personal schedule inputs, and to-dos, then uses an LLM to prioritize and generate an efficient route with Google Maps directions. Build a working scaffold and implement the core features end-to-end so the team can demo in 36 hours.

Project constraints and tech stack

Next.js with the App Router (React + server components allowed).
TypeScript enabled.
Tailwind CSS for styling.
Prisma + SQLite for a simple local DB during hackathon; provide migrations and schema.
NextAuth for Google OAuth and calendar permission scopes.
Google Calendar API integration to read events for the current day.
A simple to-do input UI stored in Prisma.
OpenAI API (or equivalent LLM) used for prioritization logic and human-friendly plan descriptions. Use a single API call to return prioritized tasks and soft constraints reasoning.
Google Maps Directions API for route optimization and travel-time estimates.
Environment variables to expect: GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_MAPS_API_KEY, OPENAI_API_KEY, DATABASE_URL.
Minimal privacy notice page and explicit permissions screen explaining calendar and location usage.
MVP features to implement

Sign in with Google and request calendar read scope.
Fetch today’s calendar events for authenticated user and display them.
Provide a simple to-do input form where the user can add one-off tasks (title, location text, priority optional).
A “Plan My Day” button that sends combined tasks and calendar events to the LLM for prioritization, then calls Google Maps Directions API to compute an ordered route and total travel time.
Show an interactive map view with waypoints in the ordered sequence and estimated travel times between stops.
Allow the user to mark items as fixed (must happen at a given time/place) or flexible, and the LLM must respect fixed constraints.
Provide a reroute button to recalculate if user marks an event done earlier or adds a new urgent task.
Basic habit learning: store chosen reorderings and user feedback to a simple embeddings table so the assistant can bias future suggestions (store as JSON, don't implement a full vector DB).
Error handling for missing locations, ambiguous addresses, and API quota issues, with clear user feedback messages.
Deliverables and files to generate

pages or app route layout with global auth provider and Tailwind.
components: SignInButton, CalendarList, TodoList, PlannerPanel, MapView, TaskCard, PermissionNotice.
API routes: /api/auth/[...nextauth], /api/calendar/fetch, /api/todos (CRUD), /api/plan (accepts tasks+events, calls LLM, queries Maps Directions, returns ordered waypoints).
Prisma schema and seed script with a demo user and demo tasks.
README with setup steps, required env vars, and demo instructions.
Sample unit test for the /api/plan route using a mocked OpenAI and Maps response (jest or vite
[truncated — 2138 more characters]
```

### package.json

```
{
  "name": "orbit",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev --webpack",
    "build": "next build --webpack",
    "start": "next start",
    "lint": "eslint"
  },
  "dependencies": {
    "@anthropic-ai/sdk": "^0.67.0",
    "@deepgram/sdk": "^4.11.2",
    "@googlemaps/js-api-loader": "^2.0.1",
    "@googlemaps/polyline-codec": "^1.0.28",
    "@radix-ui/react-dialog": "^1.1.15",
    "@radix-ui/react-scroll-area": "^1.2.10",
    "@radix-ui/react-separator": "^1.1.7",
    "@radix-ui/react-slot": "^1.2.3",
    "@radix-ui/react-tabs": "^1.1.13",
    "@react-google-maps/api": "^2.20.7",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "date-fns": "^4.1.0",
    "googleapis": "^164.1.0",
    "lucide-react": "^0.548.0",
    "next": "16.0.0",
    "react": "19.2.0",
    "react-dom": "19.2.0",
    "tailwind-merge": "^3.3.1"
  },
  "devDependencies": {
    "@tailwindcss/postcss": "^4",
    "eslint": "^9",
    "eslint-config-next": "16.0.0",
    "tailwindcss": "^4",
    "tw-animate-css": "^1.4.0"
  }
}

```

### src/app/layout.js

```javascript
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

export const metadata = {
  title: "Orbit - Smart Route Planner",
  description: "AI-powered daily route planner that optimizes your schedule with Google Maps integration",
};

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
        {children}
      </body>
    </html>
  );
}

```

### src/app/page.js

```javascript
'use client';

import { useState, useEffect } from 'react';
import { useJsApiLoader } from '@react-google-maps/api';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Separator } from '@/components/ui/separator';
import {
  MapPin,
  Calendar,
  Brain,
  RefreshCw,
  AlertCircle,
  Loader2,
  Info,
  LogOut,
  User,
  Home as HomeIcon
} from 'lucide-react';

import LandingPage from '@/components/LandingPage';
import CalendarEventsList from '@/components/CalendarEventsList';
import TodoList from '@/components/TodoList';
import MapView from '@/components/MapView';
import PlannerPanel from '@/components/PlannerPanel';
import { getAllItems } from '@/lib/storage';
import { loadDemoData } from '@/lib/seedData';

const libraries = ['places'];

export default function Home() {
  const [showLanding, setShowLanding] = useState(true);
  const [selectedItemId, setSelectedItemId] = useState(null);
  const [items, setItems] = useState([]);
  const [isPlanning, setIsPlanning] = useState(false);
  const [planError, setPlanError] = useState(null);
  const [planResult, setPlanResult] = useState(null);
  const [route, setRoute] = useState(null);
  const [orderedItems, setOrderedItems] = useState([]);

  const { isLoaded, loadError } = useJsApiLoader({
    id: 'google-map-script',
    googleMapsApiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY || '',
    libraries: libraries,
    version: 'weekly'
  });

  // Load demo data on mount
  useEffect(() => {
    loadDemoData();
    loadItems();
  }, []);

  // Debug API key loading
  useEffect(() => {
    console.log('Google Maps API Key status:', {
      hasKey: !!process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY,
      keyLength: process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY?.length || 0,
      isLoaded,
      loadError
    });
  }, [isLoaded, loadError]);


  const loadItems = () => {
    const allItems = getAllItems();
    setItems(allItems);
  };

  const handleItemSelect = (itemId) => {
    setSelectedItemId(itemId);
  };

  const handleItemEdit = (item) => {
    // TODO: Implement edit modal
    console.log('Edit item:', item);
  };

  const handleItemDelete = (itemId) => {
    loadItems();
    if (selectedItemId === itemId) {
      setSelectedItemId(null);
    }
  };

  const handleItemToggleComplete = (itemId) => {
    loadItems();
  };

  const handlePlanRoute = async () => {
    setIsPlanning(true);
    setPlanError(null);
    setOrderedItems([]); // Clear previous plan

    try {
      // Call Claude planning API
      const response = await fetch('/api/claude/plan', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          items: items.filter(item => !item.completed),
          userTimezone: 'America/New_York'
        }),
      });

      if (!response.ok) {
        throw new Error('Planning failed');
      }

      const result = await response.json();

      if (!result.success) {
        throw new Error(result.error || 'Planning failed');
      }

      setPlanResult(result.data);

      // Get ordered items
      const orderedItemsList = result.data.orderedIds.map(id =>
        items.find(item => item.id === id)
      ).filter(Boolean);

      setOrderedItems(result.data.orderedIds);

      // Call Google Maps Directions API
      const locations = orderedItemsList
        .filter(item => item.location)
        .map(item => item.location);

      if (locations.length > 1) {
        const directionsResponse = await fetch('/api/maps/directions', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            locations,
            optimizeWaypoints: true
          }),
        });

        if (directionsResponse.ok) {
          const directionsResult = await directionsResponse.json();
          console.log('Directions API result:', directionsResult);
          if (directionsResult.success) {
            setRoute(directionsResult.route);
          }
        } else {
          console.error('Directions API failed:', await directionsResponse.text());
        }
      }

    } catch (error) {
      console.error('Planning error:', error);
      setPlanError(error.message);
    } finally {
      setIsPlanning(false);
    }
  };

  const handleRecalculate = async () => {
    await handlePlanRoute();
  };

  const handleClearPlan = () => {
    setOrderedItems([]);
    setRoute(null);
    setPlanResult(null);
    setPlanError(null);
  };

  if (showLanding) {
    return <LandingPage onGetStarted={() => setShowLanding(false)} />;
  }

  if (loadError) {
    console.error('Google Maps load error:', loadError);
    return (
      <div className="h-screen flex items-center justify-center bg-gray-50">
        <div className="text-center">
          <AlertCircle className="h-12 w-12 text-red-500 mx-auto mb-4" />
          <h3 className="text-lg font-medium text-gray-900 mb-2">Map Loading Error</h3>
          <p className="text-sm text-gray-600 mb-2">
            Failed to load Google Maps. Please check your API key and internet connection.
          </p>
          <p className="text-xs text-gray-500 mb-4">
            Error: {loadError.message || 'Unknown error'}
          </p>
          <div className="text-xs text-gray-400 mb-4">
            API Key: {process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY ? 'Set' : 'Not set'}
          </div>
          <Button onClick={() => window.location.reload()}>
            <RefreshCw className="h-4 w-4 mr-2" />
            Retry
          </Button>
        </div>
      </div>
    );
  }

  return (
    <div className="h-screen flex flex-col bg-gray-50">
      {/* Header */}
      <header className="bg-white border-b px-4 py-3 flex items-center justify-b
[truncated — 4540 more characters]
```

### src/app/api/maps/validate/route.js

```javascript
export async function GET(request) {
    try {
        const apiKey = process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY;

        if (!apiKey) {
            return Response.json({
                valid: false,
                error: 'API key not configured',
                details: 'NEXT_PUBLIC_GOOGLE_MAPS_API_KEY environment variable is not set'
            });
        }

        // Test the API key with a simple geocoding request
        const testResponse = await fetch(
            `https://maps.googleapis.com/maps/api/geocode/json?address=New+York&key=${apiKey}`
        );

        const testData = await testResponse.json();

        if (testData.status === 'OK') {
            return Response.json({
                valid: true,
                message: 'API key is valid and working',
                keyLength: apiKey.length,
                testResult: testData.results[0]?.formatted_address || 'Test successful'
            });
        } else {
            return Response.json({
                valid: false,
                error: 'API key validation failed',
                details: testData.error_message || testData.status,
                keyLength: apiKey.length
            });
        }

    } catch (error) {
        return Response.json({
            valid: false,
            error: 'API key validation error',
            details: error.message
        });
    }
}

```

### src/app/api/maps/geocode/route.js

```javascript
export async function GET(request) {
    try {
        const { searchParams } = new URL(request.url);
        const address = searchParams.get('address');

        if (!address) {
            return Response.json(
                { error: 'Address parameter is required' },
                { status: 400 }
            );
        }

        if (!process.env.GOOGLE_MAPS_API_KEY) {
            return Response.json(
                { error: 'Google Maps API key not configured' },
                { status: 500 }
            );
        }

        const response = await fetch(
            `https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(address)}&key=${process.env.GOOGLE_MAPS_API_KEY}`
        );

        const data = await response.json();

        if (data.status !== 'OK') {
            return Response.json(
                {
                    error: 'Geocoding failed',
                    details: data.error_message || data.status,
                    status: data.status
                },
                { status: 400 }
            );
        }

        const results = data.results.map(result => ({
            address: result.formatted_address,
            location: {
                lat: result.geometry.location.lat,
                lng: result.geometry.location.lng
            },
            placeId: result.place_id,
            types: result.types
        }));

        return Response.json({
            success: true,
            results
        });

    } catch (error) {
        console.error('Geocoding API error:', error);
        return Response.json(
            { error: 'Internal server error', details: error.message },
            { status: 500 }
        );
    }
}

```

### src/app/api/deepgram/transcribe/route.js

```javascript
import { createClient } from '@deepgram/sdk';
import { NextResponse } from 'next/server';

export async function POST(request) {
  try {
    const deepgram = createClient(process.env.DEEPGRAM_API_KEY);

    // Get the audio data from the request
    const formData = await request.formData();
    const audioFile = formData.get('audio');
    
    if (!audioFile) {
      return NextResponse.json(
        { error: 'No audio file provided' },
        { status: 400 }
      );
    }

    // Convert File to Buffer
    const arrayBuffer = await audioFile.arrayBuffer();
    const buffer = Buffer.from(arrayBuffer);

    // Transcribe audio using Deepgram
    const { result, error } = await deepgram.listen.prerecorded.transcribeFile(
      buffer,
      {
        model: 'nova-2',
        language: 'en',
        smart_format: true,
        punctuation: true,
        diarize: false,
      }
    );

    if (error) {
      console.error('Deepgram transcription error:', error);
      return NextResponse.json(
        { error: 'Transcription failed', details: error.message },
        { status: 500 }
      );
    }

    // Extract transcript text
    const transcript = result?.results?.channels?.[0]?.alternatives?.[0]?.transcript || '';

    return NextResponse.json({
      success: true,
      transcript: transcript.trim(),
      confidence: result?.results?.channels?.[0]?.alternatives?.[0]?.confidence || 0,
      metadata: {
        words: result?.results?.channels?.[0]?.alternatives?.[0]?.words || []
      }
    });

  } catch (error) {
    console.error('Transcription error:', error);
    return NextResponse.json(
      { error: 'Failed to transcribe audio', details: error.message },
      { status: 500 }
    );
  }
}

```

### src/app/api/claude/plan/route.js

```javascript
import Anthropic from '@anthropic-ai/sdk';
import { buildPlanningPrompt, parseClaudeResponse, validateItems } from '@/lib/claudePrompt';

const anthropic = new Anthropic({
    apiKey: process.env.CLAUDE_API_KEY,
});

export async function POST(request) {
    try {
        const { items, userTimezone = 'America/New_York' } = await request.json();

        // Validate input
        const validation = validateItems(items);
        if (!validation.isValid) {
            return Response.json(
                { error: 'Invalid items data', details: validation.errors },
                { status: 400 }
            );
        }

        if (!process.env.CLAUDE_API_KEY) {
            return Response.json(
                { error: 'Claude API key not configured' },
                { status: 500 }
            );
        }

        // Build prompt
        const { systemPrompt, userPrompt } = buildPlanningPrompt(items, userTimezone);

        // Call Claude API
        const response = await anthropic.messages.create({
            model: 'claude-3-5-haiku-20241022',
            max_tokens: 1000,
            temperature: 0.3,
            system: systemPrompt,
            messages: [
                {
                    role: 'user',
                    content: userPrompt
                }
            ]
        });

        const content = response.content[0];
        if (content.type !== 'text') {
            throw new Error('Unexpected response type from Claude');
        }

        // Parse response
        const result = parseClaudeResponse(content.text);

        if (!result.success) {
            return Response.json(
                { error: 'Failed to parse Claude response', details: result.error },
                { status: 500 }
            );
        }

        return Response.json({
            success: true,
            data: result.data
        });

    } catch (error) {
        console.error('Claude planning API error:', error);

        // Handle specific error types
        if (error.message.includes('API key')) {
            return Response.json(
                { error: 'Invalid API key' },
                { status: 401 }
            );
        }

        if (error.message.includes('quota') || error.message.includes('rate limit')) {
            return Response.json(
                { error: 'API quota exceeded. Please try again later.' },
                { status: 429 }
            );
        }

        return Response.json(
            { error: 'Internal server error', details: error.message },
            { status: 500 }
        );
    }
}

```

### src/app/api/claude/parse-todo/route.js

```javascript
import Anthropic from '@anthropic-ai/sdk';
import { NextResponse } from 'next/server';

const anthropic = new Anthropic({
  apiKey: process.env.CLAUDE_API_KEY,
});

export async function POST(request) {
  try {
    const { transcript } = await request.json();

    if (!transcript || typeof transcript !== 'string') {
      return NextResponse.json(
        { error: 'Transcript is required and must be a string' },
        { status: 400 }
      );
    }

    if (!process.env.CLAUDE_API_KEY) {
      return NextResponse.json(
        { error: 'Claude API key not configured' },
        { status: 500 }
      );
    }

    // Use Claude to parse the voice transcript and extract todo details
    const prompt = `You are a helpful assistant that parses voice transcripts into structured todo tasks. Parse the following voice transcript and extract the task details.

Voice Transcript: "${transcript}"

Extract the following information from the transcript:
1. Task title (main thing to do)
2. Location (if mentioned)
3. Priority (low, medium, high - infer from urgency indicators)
4. Estimated duration in minutes (infer from task type if not mentioned)

Respond ONLY with a valid JSON object in this exact format (no markdown, no code blocks, just raw JSON):
{
  "title": "extracted task title",
  "location": "extracted location or empty string if not mentioned",
  "priority": "low|medium|high",
  "durationMinutes": number (reasonable estimate based on task type)
}

Examples:
- "Buy groceries at Whole Foods" → {"title": "Buy groceries", "location": "Whole Foods", "priority": "medium", "durationMinutes": 30}
- "Call doctor urgent" → {"title": "Call doctor", "location": "", "priority": "high", "durationMinutes": 15}
- "Pick up dry cleaning tomorrow" → {"title": "Pick up dry cleaning", "location": "", "priority": "medium", "durationMinutes": 10}
- "Meeting with Sarah at Starbucks for coffee" → {"title": "Meeting with Sarah", "location": "Starbucks", "priority": "low", "durationMinutes": 30}

Parse this transcript: "${transcript}"

Return ONLY the JSON object:`;

    const message = await anthropic.messages.create({
      model: 'claude-sonnet-4-20250514',
      max_tokens: 200,
      temperature: 0.3,
      messages: [
        {
          role: 'user',
          content: prompt,
        },
      ],
    });

    // Extract the response text
    const responseText = message.content[0].text.trim();

    // Try to parse the JSON response
    let parsedData;
    try {
      parsedData = JSON.parse(responseText);
    } catch (parseError) {
      console.error('Failed to parse Claude response:', responseText);
      // Fallback: return a simple parsed version
      parsedData = {
        title: transcript,
        location: '',
        priority: 'medium',
        durationMinutes: 30,
      };
    }

    // Validate and sanitize the response
    const result = {
      title: parsedData.title || transcript || 'Untitled Task',
      location: parsedData.location || '',
      priority: ['low', 'medium', 'high'].includes(parsedData.priority) 
        ? parsedData.priority 
        : 'medium',
      durationMinutes: typeof parsedData.durationMinutes === 'number' && parsedData.durationMinutes > 0
        ? parsedData.durationMinutes
        : 30,
    };

    return NextResponse.json({
      success: true,
      data: result,
    });

  } catch (error) {
    console.error('Claude parsing error:', error);
    
    return NextResponse.json(
      {
        error: 'Failed to parse transcript',
        details: error.message,
        fallback: {
          title: 'Transcript parsed with errors',
          location: '',
          priority: 'medium',
          durationMinutes: 30,
        },
      },
      { status: 500 }
    );
  }
}

```

[25 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]