# Project export: Pulsify (Personal Health Log)

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: CruzHacks 2026
- Tagline: Ever visited the doctor's office only to forget exactly when your symptoms started? Pulsify tracks your symptoms through text or speech. We don’t just track your health; we help you find the triggers.
- Devpost: https://devpost.com/software/pulsify-personal-health-log
- GitHub: https://github.com/misotofu7/personal-health-log.git
- Demo: https://pulsify-health.vercel.app/
- Video: https://www.youtube.com/embed/zdSG1cb7OkQ?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 3 GitHub contributor(s) — izzy-u (23 commits), Dan Pham (20 commits), Kaylie Chang (12 commits)

## Devpost submission (written by the team)

### Inspiration

We searched the internet for ideas and came across several medical-related ideas. A lot of the ideas involved the usage of AI, helping improve the efficiency of medical health workers, etc. Then we started brainstorming, what if we created a website that helped patients remember exactly when they experienced symptoms?

### What it does

Pulsify is a website that takes in user input (text/speech) and automatically logs down the symptom felt on the right day in the calendar. Through this way, the patient can accurately "recall" when symptoms occurred when visiting health professionals. This ensures a higher chance for quicker and accurate diagnosis. There's other features like the dark/light mode, login/logout, heat map, and live chat. The heat map color codes separate calendar based on how many symptoms were logged on that day. The live chat is an AI bot that takes in user input. They can also log in symptoms for you in the calendar.

### How we built it

We started by inputting the basic folders and files into VSCode. Then, we created our own individual branches and began coding. Whenever we added a feature, we saved, committed, and pushed to our own branches. After we achieved most of our baseline features, we merged the 3 branches together and began debugging the main branch together as a group. We maintained constant communication to help speed up our debugging process.

### Challenges we ran into

We ran into merge conflicts. We all independently worked on our own branches, but didn't merge the changes while they were still little. As a result, it was more difficult to navigate through all the new files and folders when debugging and adding new features. For instance, we had accidentally created 2 different aspects of the navbar separately and they overlapped each other, preventing the interaction of some buttons.

### Accomplishments we're proud of

We are all relatively very new to hackathons, so we're proud of producing a website with this much capability in a short time. We're also proud of our drive to keep coding through the night and motivation to fix our bugs.

### What we learned

Not only did we learn how to code in new languages or build new features we've never done before, we learned how to collaborate efficiently. Whether it was through how GitHub operates, we learned that taking initiative is key to improving and developing our project. Regular communication and group agreement on ideas is equally as important as to prevent multiple people from working on the same things together.

### What's next

We plan on debugging and beta testing the website for further improvements. Once that is done, we plan to add more features or expand our project scope, such as the following: Turn this website into a downloadable app available on all platforms/browsers and devices Improving the features to make usage of the website more intuitive for users Improve the accessibility within our website (apply WCAG AA standards) Add on MFA for extra security

## README (from the GitHub repository)

This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.


## Detected evidence (automated analysis)

Indexed codebase: 40 recognized source files, 139 KB.
- CSS (language) — detected in the code
- Google Gemini (technology) — detected in the code
- JavaScript (language) — detected in the code
- MongoDB (technology) — detected in the code
- Next.js (technology) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- HTML (language) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (47 of 47)

```
.gitignore
BIOLOG_ITERATION_PLAN.md
eslint.config.mjs
jsconfig.json
next.config.mjs
package.json
postcss.config.mjs
README.md
src/app/api/ask/route.js
src/app/api/auth/[auth0]/route.js
src/app/api/debug/route.js
src/app/api/logs/route.js
src/app/api/public-trends/route.js
src/app/api/seed/route.js
src/app/api/tts/route.js
src/app/calendar/page.jsx
src/app/chat/page.jsx
src/app/components/Calendar.jsx
src/app/components/HeatCalendar.jsx
src/app/components/HomeHeatmap.jsx
src/app/components/ModalPopup.jsx
src/app/components/Navbar.jsx
src/app/components/Navbar.module.css
src/app/components/Popup.jsx
src/app/components/StatsCard.jsx
src/app/components/SymptomChart.jsx
src/app/components/ThemeToggle.js
src/app/components/utils/date.jsx
src/app/components/ViewToggle.jsx
src/app/components/YearCalendar.jsx
src/app/dev/page.jsx
src/app/globals.css
src/app/heatcalendar/page.jsx
src/app/layout.js
src/app/lib/storage.jsx
src/app/lib/utils.js
src/app/page.js
src/app/sampleposts/page.jsx
src/app/voice-test/page.jsx
src/app/yearcalendar/page.jsx
src/hooks/useVoiceInput.js
src/lib/auth0.ts
src/lib/mongodb.js
src/lib/tts.js
src/middleware.ts
src/services/publicData.js
tsconfig.json
```

### Dependencies

- package.json: @auth0/nextjs-auth0@^4.14.0, @google/generative-ai@^0.24.1, @tailwindcss/postcss@^4, @types/node@25.0.9, @types/react@19.2.8, better-sqlite3@^12.6.2, clsx@^2.1.1, eslint@^9, eslint-config-next@16.1.3, lucide-react@^0.562.0, mongodb@^7.0.0, next@16.1.3, react@19.2.3, react-dom@19.2.3, tailwind-merge@^3.4.0, tailwindcss@^4, typescript@5.9.3, uuid@^13.0.0

### Recent commits (newest first)

- final style changes
- logs ui change
- Add TTS, seed data route, CHF protocol, and UI improvements
- Pulsify top left & log in gone
- Merge branch 'main' of https://github.com/misotofu7/personal-health-loggit
- Minor changes
- Merge branch 'main' of https://github.com/misotofu7/personal-health-log
- whoops wrong folder
- fix: matched toggle light/dark modes on calendar and heat buttons
- Pulsify title top left
- merge: attempt to unify navbar and layout after branch merge
- the jsx file for it
- horizontal heatmap
- fix: add missing clsx and tailwind-merge dependencies
- merge: integrate calendar branch with heatmap and navbar
- semi-final calendar changes
- New Navbar and Calendar Updates
- fix: add missing lucide-react dependency
- early iteration of navbar, plus some more heatmap udpates
- merge: pull kaylie auth0 updates

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

### BIOLOG_ITERATION_PLAN.md

```markdown
# BioLog - Current State & Iteration Plan

**Last Updated:** January 2026  
**Status:** MVP Complete → Iteration Phase

---

## ✅ What's Built (Current State)

### Core Features
- ✅ **Voice Input** (`useVoiceInput` hook) - Web Speech API integration
- ✅ **AI Agent** (`/api/ask`) - Gemini 3 Flash with function calling
  - `save_symptom_log` - Logs symptoms with severity, tags, date
  - `query_logs` - Queries symptom history
  - `analyze_patterns` - Finds correlations and trends
- ✅ **MongoDB Integration** - Persistent storage via Atlas
- ✅ **Auth0** - Login/logout (per-user data not yet wired)
- ✅ **Home Heatmap** - GitHub-style 90-day visualization
- ✅ **Calendar Views** - Month/year calendar components
- ✅ **Chat Page** - Full conversation interface
- ✅ **Deployed to Vercel** - Live at `personal-health-log.vercel.app`

### UI Components
- ✅ Navbar
- ✅ Theme Toggle (light/dark)
- ✅ Voice button
- ✅ Response display
- ✅ Heatmap visualization

---

## 🎯 Current Gaps & Issues

### Critical Fixes Needed
1. **Auth0 per-user data** - All logs are shared. Need `userId` filtering
2. **Heatmap refresh** - Should auto-refresh after save (partially working)
3. **Date handling** - Ensure timezone consistency
4. **Error handling** - Better user feedback on API failures

### UX Improvements
1. **Home page layout** - Currently cramped, needs spacing
2. **Heatmap legend** - Make color scale clearer
3. **Loading states** - Show spinners during API calls
4. **Empty states** - "No logs yet" messaging

---

## 🚀 Iteration Ideas (Prioritized)

### Phase 1: Polish & Fix (Next 2-4 hours)
**Goal:** Make demo-ready

1. **Fix Auth0 per-user isolation**
   - Add `userId` to all log documents
   - Filter queries by `userId`
   - Test with multiple accounts

2. **Improve heatmap**
   - Add month labels
   - Better color scale (match severity levels)
   - Tooltip on hover showing date + details
   - Make it more compact if needed

3. **Home page polish**
   - Better spacing/layout
   - Move heatmap above or below input?
   - Add "Recent logs" section?

4. **Error handling**
   - Try/catch around all API calls
   - User-friendly error messages
   - Retry logic for failed requests

### Phase 2: Features (If time permits)
**Goal:** Add "wow" factor

1. **ElevenLabs Voice Response** (30 min)
   - AI speaks back after logging
   - "I've logged your headache. That's your third this week."
   - Wins MLH ElevenLabs prize

2. **Proactive Health Forecast** (1-2 hours)
   - Daily "Today's Risk" card
   - "Based on your patterns, you're 70% likely to have a migraine today"
   - Actionable recommendations

3. **Body Map Interface** (2-3 hours)
   - Tap body parts to log pain
   - Visual pain tracking
   - More intuitive than text

4. **Export for Doctor** (1 hour)
   - One-click PDF/JSON export
   - Clean summary format
   - "Share with doctor" button

### Phase 3: Advanced (Post-hackathon)
**Goal:** Real product features

1. **Predictive Analytics**
   - ML model for symptom prediction
[truncated — 2982 more characters]
```

### package.json

```
{
  "name": "idk-proj",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint"
  },
  "dependencies": {
    "@auth0/nextjs-auth0": "^4.14.0",
    "@google/generative-ai": "^0.24.1",
    "better-sqlite3": "^12.6.2",
    "clsx": "^2.1.1",
    "lucide-react": "^0.562.0",
    "mongodb": "^7.0.0",
    "next": "16.1.3",
    "react": "19.2.3",
    "react-dom": "19.2.3",
    "tailwind-merge": "^3.4.0",
    "uuid": "^13.0.0"
  },
  "devDependencies": {
    "@tailwindcss/postcss": "^4",
    "@types/node": "25.0.9",
    "@types/react": "19.2.8",
    "eslint": "^9",
    "eslint-config-next": "16.1.3",
    "tailwindcss": "^4",
    "typescript": "5.9.3"
  }
}

```

### src/app/layout.js

```javascript
import { Geist, Geist_Mono } from "next/font/google";
import { Auth0Provider } from "@auth0/nextjs-auth0/client";
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: "Personal Health Log",
  description: "Created in CruzHacks 2026",
};

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <head>
        <link
          href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
          rel="stylesheet"
        />
      </head>

      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
        <Auth0Provider
          baseUrl={process.env.NEXT_PUBLIC_AUTH0_BASE_URL || "http://localhost:3000"}
          profileUrl="/api/auth/me"
        >
          {children}
        </Auth0Provider>
      </body>
    </html>
  );
}

```

### src/app/page.js

```javascript
"use client";

import { useState, useEffect } from "react";
import { useVoiceInput } from "../hooks/useVoiceInput";
import { Navbar } from "./components/Navbar";
import { HomeHeatmap } from "./components/HomeHeatmap";
import { useUser } from "@auth0/nextjs-auth0/client";
import { playAudioResponse } from "../lib/tts";

export default function Home() {
  const [input, setInput] = useState("");
  const [response, setResponse] = useState("");
  const [isSaving, setIsSaving] = useState(false);
  const [refreshTrigger, setRefreshTrigger] = useState(0);
  const [localUserId, setLocalUserId] = useState(null);
  const { user, isLoading, error } = useUser();
  

  // Generate localUserId for anonymous users (only if not logged in)
  // This persists in localStorage, so anonymous logs remain visible after sign out
  useEffect(() => {
    if (typeof window !== "undefined" && !user) {
      // Restore localUserId from localStorage (persists across login/logout)
      let storedId = localStorage.getItem("localUserId");
      if (!storedId) {
        // Create new localUserId only if one doesn't exist
        storedId = `local_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
        localStorage.setItem("localUserId", storedId);
      }
      setLocalUserId(storedId);
    } else if (user) {
      // When logged in, clear localUserId from state (but keep in localStorage for when you sign out)
      setLocalUserId(null);
    }
  }, [user]);

  const { transcript, isListening, startListening, stopListening, isSupported } =
    useVoiceInput();

  useEffect(() => {
    if (transcript) setInput(transcript);
  }, [transcript]);

  const handleSave = async () => {
    if (!input.trim() || isSaving) return;

    setIsSaving(true);
    setResponse("");

    try {
      const res = await fetch("/api/ask", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ 
          message: input,
          userId: user?.sub || null,
          localUserId: user ? null : localUserId 
        }),
      });

      const data = await res.json();

      if (data.success) {
        setResponse(data.response);
        setInput("");
        // Trigger heatmap and stats refresh
        setRefreshTrigger(prev => prev + 1);
        // Play audio response
        playAudioResponse(data.response);
      } else {
        setResponse(`Error: ${data.error}`);
      }
    } catch (error) {
      setResponse(`Error: ${error.message}`);
    } finally {
      setIsSaving(false);
    }
  };

  const handleVoiceClick = () => {
    if (isListening) stopListening();
    else startListening();
  };

  return (
    <>
      <Navbar user={user} isLoading={isLoading} />

      <main className="page" style={{ paddingTop: 55 }}>
        <h1 className="main-title">Personal Health Log</h1>
        <p className="muted">The most efficient AI-powered tool for tracking symptoms & health.</p>

        <div className="textarea-wrapper">
          <textarea
            id="symptoms"
            placeholder={isListening ? "Listening..." : "Describe your symptoms..."}
            value={input}
            onChange={(e) => setInput(e.target.value)}
          />

          {isSupported && (
            <button
              type="button"
              className="voice-button"
              aria-label="Voice input"
              onClick={handleVoiceClick}
              style={isListening ? { background: "var(--accent)", color: "white" } : {}}
            >
              {isListening ? "⏹ Stop" : "🎤 Voice"}
            </button>
          )}
        </div>

        <button
          className="save-button"
          onClick={handleSave}
          disabled={isSaving || !input.trim()}
          style={{
            ...(isSaving || !input.trim() ? { opacity: 0.5, cursor: "not-allowed" } : {}),
            padding: "8px 16px",
            fontSize: "14px"
          }}
        >
          {isSaving ? "Saving..." : "Save entry"}
        </button>

        {response && (
          <div className="response-box">
            <p>{response}</p>
          </div>
        )}

        {/* Heatmap - horizontal and stretched */}
        <div style={{ marginBottom: "8px" }}>
          <p style={{ 
            fontSize: "12px", 
            color: "var(--foreground)", 
            opacity: 0.6, 
            margin: 0,
            textAlign: "center"
          }}>
            Last 90 days
          </p>
        </div>
        <HomeHeatmap refreshTrigger={refreshTrigger} userId={user?.sub || null} localUserId={user ? null : localUserId} />

        <div className="nav-links">
          <a href="/chat">💬 Chat with your data</a>
        </div>

        <p className="disclaimer">
          This tool is for tracking only. It is not medical advice. Please consult a
          healthcare professional for diagnosis and treatment.
        </p>

        <footer className="footer">CruzHacks 2026</footer>
      </main>
    </>
  );
}

```

### src/app/heatcalendar/page.jsx

```javascript
"use client";

import { HeatCalendar } from "../components/HeatCalendar";
import { Navbar } from "../components/Navbar";
import { useUser } from "@auth0/nextjs-auth0/client";

/* note that Date objects are built into javascript as pre-existing classes */

export default function Home() {
  const { user, isLoading } = useUser();
  
  return (
    <div className = "relative min-h-screen"> 
        <Navbar user={user} isLoading={isLoading} />
        <main className="mt-20">
            <HeatCalendar/>
        </main>
    </div>
  );
}
```

### src/app/yearcalendar/page.jsx

```javascript
"use client";

import { useState, useEffect } from "react";
import { ViewToggle } from "../components/ViewToggle";
import { Calendar } from "../components/Calendar";
import { YearCalendar } from "../components/YearCalendar";
import { ModalPopup } from "../components/ModalPopup";

/* note that Date objects are built into javascript as pre-existing classes */

export default function Home() {
  return <div className = "relative min-h-screen"> 

  
    
        <main>

            <ModalPopup/>

         
        </main>
    </div>
  
}
```

### src/app/sampleposts/page.jsx

```javascript
"use client";

import { useEffect, useState } from "react";

function formatDate(date) {
  return date.toISOString().split("T")[0];
}

export default function Calendar() {
  const [logs, setLogs] = useState([]);

 useEffect(() => {
  fetch("/api/logs")
    .then(res => res.json())
    .then(data => setLogs(data.logs)); // ← THIS is the fix
}, []);

  const logsByDate = logs.reduce((acc, log) => {
    acc[log.date] ??= [];
    acc[log.date].push(log);
    return acc;
  }, {});

  const today = new Date();
  const days = Array.from({ length: 31 }, (_, i) =>
    new Date(today.getFullYear(), today.getMonth(), i + 1)
  );

  return (
    <div className="grid grid-cols-7 gap-2">
      {days.map(day => {
        const key = formatDate(day);
        const dayLogs = logsByDate[key] || [];

        return (
          <div key={key} className="border p-2">
            <div>{day.getDate()}</div>
            {dayLogs.map(log => (
              <div key={log._id}>
                {log.symptom}
              </div>
            ))}
          </div>
        );
      })}
    </div>
  );
}
```

### src/app/calendar/page.jsx

```javascript
"use client";

import { useState, useEffect } from "react";
import { ViewToggle } from "../components/ViewToggle";
import { Calendar } from "../components/Calendar";
import { YearCalendar } from "../components/YearCalendar";
import { Navbar } from "../components/Navbar";
import { useUser } from "@auth0/nextjs-auth0/client";

/* note that Date objects are built into javascript as pre-existing classes */

export default function Home() {
    const [isYearView, setYearView] = useState(false);
    const { user, isLoading } = useUser();

    useEffect(() => {
        const storedViewType = localStorage.getItem("viewType");

        if(storedViewType === "year"){
            setYearView(true);
        }
        else{
            localStorage.setItem("viewType", "month"); // if it doesn't exist yet, then add it?
            setYearView(false);
        }

    }, []) // initialize default view type to monthlong

console.log("isYearView in Home:", isYearView);

/*     
            <ViewToggle isYearView={isYearView} setYearView={setYearView} />
            { isYearView ? 
            <YearCalendar/> : <Calendar/> }
*/

  return <div className = "relative min-h-screen"> 

  <Navbar user={user} isLoading={isLoading} />
    
        <main className = "mt-20">
            
            <Calendar/>


         
        </main>
    </div>
  
}
```

### src/app/voice-test/page.jsx

```javascript
"use client";

import { useState } from "react";
import { useVoiceInput } from "../../hooks/useVoiceInput";

export default function VoiceTestPage() {
  const {
    transcript,
    isListening,
    startListening,
    stopListening,
    clearTranscript,
    error,
    isSupported,
  } = useVoiceInput();

  const [savedText, setSavedText] = useState("");

  const handleSave = () => {
    if (transcript.trim()) {
      setSavedText(transcript);
      // This is where you'd send to Gemini API later
      console.log("Ready to send to Gemini:", transcript);
    }
  };

  // Sample inputs for demo fallback
  const sampleInputs = [
    "My head hurts really bad this morning",
    "Mild back pain after sitting too long",
    "Feeling nauseous, probably from dinner last night",
    "Worst migraine ever, can barely see",
  ];

  const useSampleInput = (sample) => {
    clearTranscript();
    // Manually set via a workaround since transcript is controlled by speech
    setSavedText(sample);
  };

  return (
    <div className="min-h-screen bg-slate-50 p-8">
      <div className="max-w-2xl mx-auto">
        <h1 className="text-3xl font-semibold text-slate-900 mb-2">
          Voice Input Test
        </h1>
        <p className="text-slate-600 mb-8">
          Test the voice-to-text functionality for symptom logging.
        </p>

        {/* Status */}
        <div className="mb-6">
          {!isSupported && (
            <div className="bg-amber-100 border border-amber-300 text-amber-800 px-4 py-3 rounded-lg">
              ⚠️ Speech recognition not supported. Use sample inputs below.
            </div>
          )}
          {error && (
            <div className="bg-red-100 border border-red-300 text-red-800 px-4 py-3 rounded-lg">
              {error}
            </div>
          )}
        </div>

        {/* Voice Input Section */}
        <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6 mb-6">
          <div className="flex items-center gap-4 mb-4">
            <button
              onClick={isListening ? stopListening : startListening}
              disabled={!isSupported}
              className={`
                flex items-center justify-center w-16 h-16 rounded-full 
                transition-all duration-200 
                ${isListening 
                  ? "bg-red-500 hover:bg-red-600 animate-pulse" 
                  : "bg-green-500 hover:bg-green-600"
                }
                ${!isSupported ? "opacity-50 cursor-not-allowed" : ""}
                text-white
              `}
            >
              {isListening ? (
                <span className="text-2xl">⏹</span>
              ) : (
                <span className="text-2xl">🎤</span>
              )}
            </button>
            <div>
              <p className="font-medium text-slate-900">
                {isListening ? "Listening..." : "Tap to speak"}
              </p>
              <p className="text-sm text-slate-500">
                {isListening 
                  ? "Speak your symptoms clearly" 
                  : "Describe how you're feeling"
                }
              </p>
            </div>
          </div>

          {/* Transcript Display */}
          <div className="bg-slate-50 rounded-lg p-4 min-h-[80px] border border-slate-200">
            {transcript ? (
              <p className="text-slate-900">{transcript}</p>
            ) : (
              <p className="text-slate-400 italic">
                Your speech will appear here...
              </p>
            )}
          </div>

          {/* Action Buttons */}
          <div className="flex gap-3 mt-4">
            <button
              onClick={handleSave}
              disabled={!transcript.trim()}
              className={`
                px-4 py-2 rounded-lg font-medium transition-colors
                ${transcript.trim()
                  ? "bg-green-600 text-white hover:bg-green-700"
                  : "bg-slate-200 text-slate-400 cursor-not-allowed"
                }
              `}
            >
              Save & Parse
            </button>
            <button
              onClick={clearTranscript}
              className="px-4 py-2 rounded-lg font-medium bg-slate-200 text-slate-700 hover:bg-slate-300 transition-colors"
            >
              Clear
            </button>
          </div>
        </div>

        {/* Sample Inputs (Demo Fallback) */}
        <div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6">
          <h2 className="text-lg font-medium text-slate-900 mb-3">
            Demo Mode: Sample Inputs
          </h2>
          <p className="text-sm text-slate-500 mb-4">
            Click any sample to use it (for demo reliability)
          </p>
          <div className="flex flex-wrap gap-2">
            {sampleInputs.map((sample, i) => (
              <button
                key={i}
                onClick={() => useSampleInput(sample)}
                className="px-3 py-2 bg-slate-100 hover:bg-slate-200 text-slate-700 rounded-lg text-sm transition-colors text-left"
              >
                "{sample}"
              </button>
            ))}
          </div>
        </div>

        {/* Saved Text Display */}
        {savedText && (
          <div className="mt-6 bg-green-50 border border-green-200 rounded-xl p-6">
            <h3 className="text-sm font-medium text-green-800 mb-2">
              Ready to send to Gemini:
            </h3>
            <p className="text-green-900 font-mono">{savedText}</p>
          </div>
        )}
      </div>
    </div>
  );
}

```

### src/app/dev/page.jsx

```javascript
"use client";

import { useState } from "react";
import { useUser } from "@auth0/nextjs-auth0/client";
import { Navbar } from "../components/Navbar";

export default function DevPage() {
  const { user, isLoading: userLoading } = useUser();
  const [loading, setLoading] = useState(false);
  const [result, setResult] = useState(null);
  const [error, setError] = useState(null);

  const handleSeed = async (clean = false) => {
    if (!user?.sub) {
      setError("Please log in first");
      return;
    }

    setLoading(true);
    setError(null);
    setResult(null);

    try {
      const url = `/api/seed?demo=true${clean ? "&clean=true" : ""}`;
      const response = await fetch(url, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ userId: user.sub }),
      });

      const data = await response.json();

      if (data.success) {
        setResult(data);
      } else {
        setError(data.error || "Failed to seed data");
      }
    } catch (err) {
      setError(err.message || "Network error");
    } finally {
      setLoading(false);
    }
  };

  if (userLoading) {
    return (
      <div style={{ padding: "20px", textAlign: "center" }}>
        <p>Loading...</p>
      </div>
    );
  }

  if (!user) {
    return (
      <>
        <Navbar user={user} isLoading={userLoading} />
        <div style={{ padding: "40px", textAlign: "center" }}>
          <h1>Dev Tools</h1>
          <p>Please <a href="/api/auth/login">log in</a> to access dev tools.</p>
        </div>
      </>
    );
  }

  return (
    <>
      <Navbar user={user} isLoading={userLoading} />
      <div style={{ padding: "40px", maxWidth: "800px", margin: "0 auto" }}>
        <h1 style={{ marginBottom: "20px" }}>🔧 Dev Tools - Seed Data</h1>
        <p style={{ marginBottom: "10px", opacity: 0.7 }}>
          Demo-only tools for hackathon presentation. Use with caution.
        </p>
        <p style={{ marginBottom: "30px", fontSize: "14px", opacity: 0.6 }}>
          After seeding, you'll be redirected to the Calendar to see your logs. Or view them in{" "}
          <a href="/calendar" style={{ color: "var(--accent)", textDecoration: "underline" }}>
            Calendar
          </a>{" "}
          or{" "}
          <a href="/heatcalendar" style={{ color: "var(--accent)", textDecoration: "underline" }}>
            Heat Map
          </a>
          .
        </p>

        <div style={{ display: "flex", gap: "15px", marginBottom: "30px", flexWrap: "wrap" }}>
          <button
            onClick={() => handleSeed(false)}
            disabled={loading}
            style={{
              padding: "12px 24px",
              fontSize: "16px",
              background: "var(--accent)",
              color: "white",
              border: "none",
              borderRadius: "8px",
              cursor: loading ? "not-allowed" : "pointer",
              opacity: loading ? 0.6 : 1,
            }}
          >
            {loading ? "Loading..." : "➕ Add Seed Data (Safe)"}
          </button>

          <button
            onClick={() => handleSeed(true)}
            disabled={loading}
            style={{
              padding: "12px 24px",
              fontSize: "16px",
              background: "#ef4444",
              color: "white",
              border: "none",
              borderRadius: "8px",
              cursor: loading ? "not-allowed" : "pointer",
              opacity: loading ? 0.6 : 1,
            }}
          >
            {loading ? "Loading..." : "🗑️ Clean & Seed (Destructive)"}
          </button>
        </div>

        {error && (
          <div
            style={{
              padding: "15px",
              background: "#fee2e2",
              border: "1px solid #ef4444",
              borderRadius: "8px",
              color: "#991b1b",
              marginBottom: "20px",
            }}
          >
            <strong>Error:</strong> {error}
          </div>
        )}

        {result && (
          <div
            style={{
              padding: "20px",
              background: "var(--textbox)",
              border: "1px solid var(--border)",
              borderRadius: "8px",
              marginBottom: "20px",
            }}
          >
            <h3 style={{ marginTop: 0, marginBottom: "15px" }}>✅ Seed Complete</h3>
            <div style={{ marginBottom: "10px" }}>
              <strong>Deleted:</strong> {result.deleted || 0} logs
            </div>
            <div style={{ marginBottom: "10px" }}>
              <strong>Inserted:</strong> {result.inserted} seed logs
            </div>
            <div style={{ marginBottom: "15px", fontSize: "14px", opacity: 0.8 }}>
              {result.note}
            </div>
            <details style={{ marginTop: "15px" }}>
              <summary style={{ cursor: "pointer", marginBottom: "10px" }}>
                Seed Data Details
              </summary>
              <pre
                style={{
                  background: "var(--background)",
                  padding: "15px",
                  borderRadius: "6px",
                  overflow: "auto",
                  fontSize: "12px",
                }}
              >
                {JSON.stringify(result.seedData, null, 2)}
              </pre>
            </details>
          </div>
        )}

        <div
          style={{
            marginTop: "40px",
            padding: "15px",
            background: "var(--textbox)",
            border: "1px solid var(--border)",
            borderRadius: "8px",
            fontSize: "14px",
          }}
        >
          <h3 style={{ marginTop: 0 }}>📋 What This Does</h3>
          <ul style={{ marginBottom: 0, paddingLeft: "20px" }}>
            <li>
              <strong>Add Seed Data (Safe):</strong> Adds realistic CHF scenario logs without deleting existing data
            </li>
            <li>
              <strong>Clean & Seed (Destructive):</strong> Deletes all your logs
[truncated — 874 more characters]
```

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