# Project export: SoulSpeak

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 2025
- Tagline: Your companion for mindful mornings, productive days, and peaceful nights
- Devpost: https://devpost.com/software/soulspeak
- GitHub: https://github.com/meghagl17/SoulSpeak
- Demo: https://github.com/Alb111/ChimeAPI
- Team: 3 GitHub contributor(s) — Megha (28 commits), Sachuman (11 commits), Kenric Tee (1 commits)

## Devpost submission (written by the team)

### Inspiration

There are countless journaling apps available, but what if you want to talk about your day instead of writing it down? What if you prefer to "voice" your journal entries rather than type them out? Imagine having someone to talk to about your day, without the fear of judgment, while your journal entries are automatically recorded. This is the future of journaling. That’s why we created SoulSpeak, your companion for mindful mornings, productive days, and peaceful nights! The idea for this app came from asking a simple question: what are today’s journal apps missing, and how can we make journaling more meaningful and accessible? We wanted to create something that anyone can use—an app that naturally fits into your daily routine and truly supports personal growth. That’s how SoulSpeak was born. It's your very own voice journal—simple, supportive, and designed to improve your life. Try it out and see the difference for yourself!

### What it does

SoulSpeak simplifies the journaling experience and supports mental well-being by allowing users to talk through their thoughts instead of writing them down. Speaking out loud often makes it easier to process emotions, and with the help of an empathetic AI companion, users can express themselves freely—without the fear of judgment. To personalize the experience, users are prompted during sign-up to set preferred times for a morning and evening call. These calls happen automatically and are guided by the AI, which is designed to support, not dominate, the conversation. The user leads the discussion, choosing what they want to talk about. After each call, the app generates a journal entry summary—highlighting the key points of the conversation. These entries are saved and can be revisited at any time, just like a traditional journal. Our clean, user-friendly interface makes browsing past entries simple and enjoyable. In addition, each session provides a mood scale, offering insights into how the user felt during the conversation—such as happiness, sadness, anger, or calmness. This feature helps users track emotional patterns over time and better understand their mental state.

### How we built it

SoulSpeak is built using Next.js and React for the frontend, providing a smooth, modern user experience. On the backend, we’ve used Python with FastAPI to power the core functionality of the app. To deliver a seamless journaling experience, we’ve integrated several technologies: Bland AI handles the scheduled voice calls, which are carefully fine-tuned to guide meaningful conversations with the user. The conversation transcripts are then analyzed by Gemini, which generates a written journal entry and provides a mood breakdown—including levels of happiness, sadness, anger, and calmness. We use Supabase, a relational database, to securely store all user data including journal entries, mood insights, and call history. Together, these technologies create a thoughtful and intuitive journaling experience designed to support mental well-being.

### Challenges we ran into

We faced several challenges throughout the development of this app, starting with the initial setup. Since the app needed to coordinate between multiple services, ensuring smooth and efficient communication between them was critical. Choosing the tech stack was one of the first major decisions—and it wasn’t easy. After a lot of back and forth, we landed on Next.js for the frontend because of its intuitive routing system and overall developer experience. For the database, we initially considered MongoDB, but ultimately chose Supabase, a relational database, because it better suited the structure and needs of our project. Another major hurdle was splitting the backend from the Next.js frontend. Some of the API calls we needed to make weren’t possible within Next’s built-in API structure, especially given the complexity of features like scheduled voice calls and AI processing. This separation made development more flexible but also introduced complexity—sometimes a single backend commit would break the entire system, and other times a frontend change would cause unexpected errors. The biggest blocker we ran into involved integrating the Bland AI API—a crucial part of our app. At first, we struggled just to get the API to respond at all, which was a major concern since our entire concept relied on it. After resolving that issue, we hit another roadblock: retrieving the transcript from each call, which was essential for passing the data to Gemini for journal entry and mood analysis. We eventually realized that we weren’t receiving the response because we were requesting it before the call officially ended. We had to use webhooks to deal with this issue. Lastly, one of the biggest challenges was ensuring that users received their scheduled calls at the exact times they selected. Since servers don’t run continuously by default, we needed a reliable way to trigger actions even when no one was actively using the app. Even though we were not able to resolve this issue due to time constraints, we have a better understanding of the problem and would be solving it after the hackathon. Tackling problems like these is what pushes us to grow as developers. It’s through these real-world challenges that we learned the most and became better at building robust, scalable applications.

### Accomplishments we're proud of

We’re incredibly proud of what we were able to accomplish. Despite the time crunch, we successfully built our MVP within the deadline. While there were a few features we had to leave in the backlog, our main focus was always on delivering a strong and functional MVP—and we’re really happy with the results. We’re excited about where it can go from here!

### What we learned

This project was an incredible learning experience for all of us. Along the way, we encountered a variety of challenges that pushed us to think critically and adapt quickly. Every layer of the tech stack was unfamiliar to at least one team member, which meant that each of us had a unique opportunity to grow and expand our technical skill sets. All in all, the journey was filled with valuable lessons, both in problem-solving and collaboration.

### What's next

Looking ahead, we hope to transition this project into a mobile application. While time constraints during the hackathon led us to build a web-based solution, we believe a mobile app would offer a more seamless and accessible experience for users. Another improvement we’d like to implement is making the phone calls synchronous. Currently, our Python backend queues the calls, which introduces a delay in execution. While this setup worked for our hackathon prototype, it wouldn’t scale efficiently. For a production-ready system, synchronous handling would ensure real-time communication and a smoother user experience.

## README (from the GitHub repository)

This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/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.tsx`. 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: 22 recognized source files, 78 KB.
- 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
- TypeScript (language) — detected in the code
- FastAPI (technology) — claimed on Devpost, not found in the code
- Google Gemini (technology) — claimed on Devpost, not found in the code
- Python (language) — claimed on Devpost, not found in the code
- Supabase (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (28 of 28)

```
.gitignore
app/api/auth/callback/google/route.js
app/components/AuthStatus.js
app/components/AuthStatusWrapper.js
app/components/mood.js
app/dashboard/page.js
app/day/page.js
app/globals.css
app/information/page.js
app/layout.tsx
app/login/page.js
app/onboarding/page.js
app/page.tsx
app/protected/page.js
app/settings/page.js
components.json
components/ui/button.tsx
components/ui/card.tsx
components/ui/carousel.tsx
components/ui/input.tsx
eslint.config.mjs
lib/AuthContext.js
lib/utils.ts
next.config.ts
package.json
postcss.config.mjs
README.md
tsconfig.json
```

### Dependencies

- package.json: @emotion/react@^11.14.0, @emotion/styled@^11.14.0, @eslint/eslintrc@^3, @mui/icons-material@^7.0.2, @mui/material@^7.0.2, @mui/x-date-pickers@^7.28.3, @radix-ui/react-slot@^1.2.0, @tailwindcss/postcss@^4, @types/node@^20, @types/react@^19, @types/react-dom@^19, class-variance-authority@^0.7.1, clsx@^2.1.1, dayjs@^1.11.13, embla-carousel-react@^8.6.0, eslint@^9, eslint-config-next@15.3.0, lucide-react@^0.487.0, next@15.3.0, next-auth@^4.24.11, pnpm@^10.8.0, react@^19.0.0, react-dom@^19.0.0, tailwind-merge@^3.2.0, tailwindcss@^4, tw-animate-css@^1.2.5, typescript@^5

### Recent commits (newest first)

- done
- fix/ bug
- Merge pull request #10 from meghagl17/fixed-call-demo
- final commit, all working
- Merge pull request #9 from meghagl17/app-name
- changed name
- made the button, not working tho
- fixed build errors
- home page ui
- date page
- google login issue fixed
- Merge pull request #8 from meghagl17/sachin3
- Merge branch 'main' into sachin3
- CRUD operations
- frontend url changed
- changed url
- merging
- updated login url
- changed login url
- Merge pull request #7 from meghagl17/delete-yaml

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

### package.json

```
{
  "name": "daily_buddy",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@emotion/react": "^11.14.0",
    "@emotion/styled": "^11.14.0",
    "@mui/icons-material": "^7.0.2",
    "@mui/material": "^7.0.2",
    "@mui/x-date-pickers": "^7.28.3",
    "@radix-ui/react-slot": "^1.2.0",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "dayjs": "^1.11.13",
    "lucide-react": "^0.487.0",
    "next": "15.3.0",
    "next-auth": "^4.24.11",
    "embla-carousel-react": "^8.6.0",
    "lucide-react": "^0.487.0",
    "next": "15.3.0",
    "pnpm": "^10.8.0",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "tailwind-merge": "^3.2.0",
    "tw-animate-css": "^1.2.5"
  },
  "devDependencies": {
    "@eslint/eslintrc": "^3",
    "@tailwindcss/postcss": "^4",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "eslint": "^9",
    "eslint-config-next": "15.3.0",
    "tailwindcss": "^4",
    "typescript": "^5"
  }
}

```

### app/layout.tsx

```typescript
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { AuthProvider } from "../lib/AuthContext";

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

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

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

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

```

### app/page.tsx

```typescript
import Image from "next/image";
import Link from "next/link";
import ArrowRightAltIcon from '@mui/icons-material/ArrowRightAlt';

export default function Home() {
  return (
    <main className="relative min-h-screen bg-white">
      {/* Background image taking up ~75% of screen */}
      <div className="relative w-full h-[75vh] overflow-hidden">
        <Image
          src="/logo.jpeg"
          alt="Logo"
          fill
          priority
          className="object-cover rounded-b-3xl"
        />
      </div>

      {/* White section overlapping and going to bottom */}
      <div className="absolute top-[70vh] left-0 w-full z-10">
        <div className="bg-white rounded-t-3xl px-6 py-6 shadow-2xl h-[30vh] flex flex-col items-center text-center">
          <h1 className="text-3xl font-extrabold mb-2 text-gray-800">
            SoulSpeak
          </h1>
          <p className="text-gray-500 mb-4 text-sm">
            Your companion for mindful mornings, productive days, and peaceful nights.
          </p>
          <Link href="/information">
            <div className="px-6 py-2 bg-[#F98500] text-white text-sm font-semibold rounded-full hover:bg-[#e97e00] transition">
              Let&apos;s Go!
              <ArrowRightAltIcon/>
            </div>
          </Link>
        </div>
      </div>
    </main>
  );
}

```

### app/protected/page.js

```javascript
"use client";

import { useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { useAuth } from '../../lib/AuthContext';
import AuthStatusWrapper from '../components/AuthStatusWrapper';

export default function ProtectedPage() {
  const { isAuthenticated, isLoading } = useAuth();
  const router = useRouter();

  useEffect(() => {
    // Redirect to login if not authenticated and not loading
    if (!isAuthenticated && !isLoading) {
      router.push('/login');
    }
  }, [isAuthenticated, isLoading, router]);

  // Show loading state if still checking authentication
  if (isLoading) {
    return <div className="flex justify-center items-center min-h-screen">Loading...</div>;
  }

  // Only render page content if authenticated
  if (!isAuthenticated) {
    return null; // Will redirect via useEffect
  }

  return (
    <div className="flex flex-col items-center justify-center min-h-screen p-4">
      <div className="w-full max-w-md">
        <h1 className="text-2xl font-bold mb-6">Protected Page</h1>
        <p className="mb-6">This page is only visible to authenticated users.</p>
        
        <div className="mb-8">
          <AuthStatusWrapper />
        </div>
        
        <div className="p-6 bg-white dark:bg-gray-800 rounded-lg shadow-md">
          <h2 className="text-xl font-semibold mb-4">Your Private Data</h2>
          <p>This is some protected content that only logged-in users can see.</p>
        </div>
      </div>
    </div>
  );
} 
```

### app/information/page.js

```javascript
"use client"

import { useRouter } from "next/navigation"
import { ArrowDown } from "lucide-react"
// import Link from "next/link"

export default function WorkflowPage() {
  const router = useRouter()

  const handleNext = () => {
    router.push("/onboarding")
  }

  return (
    <div className="flex flex-col items-center justify-center min-h-screen px-6 py-10 bg-gradient-to-br from-[#E0FBFC] via-[#C2F0F2] to-[#A0E7EC]">
      <h1 className="text-3xl sm:text-4xl font-bold text-[#164C4F] mb-12 font-[Quicksand] text-center">
        Welcome to <span className="text-[#F98500]">SoulSpeak!</span>
      </h1>

      <div className="flex flex-col items-center space-y-8 mb-12 w-full max-w-md">
        <StepBox content="Sign up and create your profile" />
        <AnimatedArrow />
        <StepBox content="Select your preferred times to call" />
        <AnimatedArrow />
        <StepBox content="Get scheduled calls and journal with your AI buddy" />
        <AnimatedArrow />
        <StepBox content="View your day summary, mood trends, and more!" />
      </div>

      <button
        onClick={handleNext}
        className="mt-6 bg-[#F98500] hover:bg-[#e27b00] text-white font-semibold py-3 px-6 rounded-full shadow-lg transition"
      >
        Get Started
      </button>
    </div>
  )
}

// Components

function StepBox({ content }) {
  return (
    <div className="bg-white text-[#1D5C5F] px-6 py-4 rounded-2xl shadow-md w-full text-center text-base sm:text-lg font-medium">
      {content}
    </div>
  )
}

function AnimatedArrow() {
  return (
    <ArrowDown className="text-[#1D5C5F] animate-bounce" size={24} />
  )
}

```

### app/login/page.js

```javascript
"use client";

import React, { useState, useEffect } from "react";
import Image from "next/image";
import { useSearchParams, useRouter } from "next/navigation";
import { Suspense } from "react";

export default function GoogleLogin() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LoginPage />
    </Suspense>
  );
}

function LoginPage() {
  const searchParams = useSearchParams();
  const router = useRouter();
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState("");

  useEffect(() => {
    // Check if we have userId in URL (from backend callback)
    const userId = searchParams.get("userId");
    if (userId) {
      // Store userId in localStorage
      localStorage.setItem("userId", userId);
      // Skip onboarding and redirect directly to dashboard
      router.push("/dashboard");
    }
  }, [searchParams, router]);

  const handleGoogleLogin = () => {
    setIsLoading(true);
    setError("");
    try {
      window.location.href = "http://localhost:8000/auth/google/";
    } catch (err) {
      console.error("Login error:", err);
      setError("Failed to login with Google. Please try again.");
      setIsLoading(false);
    }
  };

  return (
    <div className="flex flex-col items-center justify-center min-h-screen bg-gradient-to-br from-[#F98500] to-[#EA7400] p-6">
      <div className="text-center mb-8">
        <h1 className="text-3xl sm:text-4xl font-semibold text-white mb-4">Welcome to SoulSpeak!</h1>
        <p className="text-white text-lg">Please login to continue</p>
      </div>

      {error && <p className="text-red-500 mb-4">{error}</p>}

      {/* Email/Password Fields */}
      <div className="w-full max-w-md space-y-4 mb-6">
        <input 
          type="email" 
          placeholder="Email" 
          className="w-full px-4 py-3 rounded-md border border-slate-300 focus:outline-none focus:ring-2 focus:ring-[#F98500] placeholder-slate-500"
        />
        <input 
          type="password" 
          placeholder="Password" 
          className="w-full px-4 py-3 rounded-md border border-slate-300 focus:outline-none focus:ring-2 focus:ring-[#F98500] placeholder-slate-500"
        />
      </div>

      {/* OR Separator */}
      <div className="flex items-center mb-6">
        <hr className="w-full border-t-1 border-slate-300" />
        <span className="mx-3 text-white">OR</span>
        <hr className="w-full border-t-1 border-slate-300" />
      </div>

      {/* Google Login Button */}
      <button 
        className="flex items-center gap-3 px-6 py-3 bg-white text-[#F98500] font-semibold text-lg rounded-full shadow-md hover:bg-[#FBE2B0] hover:shadow-lg transition-all duration-300"
        onClick={handleGoogleLogin}
        disabled={isLoading}
      >
        <Image 
          className="w-6 h-6" 
          src="https://www.svgrepo.com/show/475656/google-color.svg" 
          loading="lazy" 
          alt="google logo" 
          width={500} 
          height={500}
        />
        <span>{isLoading ? "Loading..." : "Login with Google"}</span>
      </button>

      {/* Other Login Options (Email/Password)
      <div className="mt-6">
        <p className="text-white text-center text-sm">
          Don't have an account? <a href="/signup" className="text-[#FBE2B0] hover:text-white">Sign up</a>
        </p>
      </div> */}
    </div>
  );
}

```

### app/onboarding/page.js

```javascript
"use client"

import { useRouter, useSearchParams } from "next/navigation"
import { Input } from "@/components/ui/input"
import { Button } from "@/components/ui/button"
import { useState, useEffect, Suspense } from "react"
import {
  Select,
  MenuItem,
  FormControl,
  InputLabel,
} from "@mui/material"

export default function Onboarding() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <OnboardingPage />
    </Suspense>
  );
}

function OnboardingPage() {
  const searchParams = useSearchParams()
  const userId = searchParams.get('userId')
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState("")

  const [formData, setFormData] = useState({
    name: "",
    phone: "",
  })

  const [amTime, setAmTime] = useState({ hour: "", minute: "" })
  const [pmTime, setPmTime] = useState({ hour: "", minute: "" })

  const handleChange = (e) => {
    const { name, value } = e.target
    setFormData((prev) => ({ ...prev, [name]: value }))
  }

  const router = useRouter()
    
  const handleNext = async () => {
    try {
      setLoading(true)
      setError("")

      // Validate inputs
      if (!formData.name || !formData.phone || !amTime.hour || !amTime.minute || !pmTime.hour || !pmTime.minute) {
        setError("Please fill in all fields")
        return
      }

      // Format times
      const timeam = `${amTime.hour}:${amTime.minute}`
      const timepm = `${pmTime.hour}:${pmTime.minute}`

      const response = await fetch(`http://localhost:8000/api/users/${userId}/onboard`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          name: formData.name,
          phone: formData.phone,
          timeam: timeam,
          timepm: timepm
        })
      })

      if (!response.ok) {
        const errorData = await response.json()
        throw new Error(errorData.detail || 'Failed to update user details')
      }

      // Store userId in localStorage before redirecting
      localStorage.setItem('userId', userId)
      
      // Redirect to dashboard on success
      router.push("/dashboard")
    } catch (err) {
      console.error('Error updating user details:', err)
      setError(err.message || 'Failed to update user details')
    } finally {
      setLoading(false)
    }
  }

  // Check if user should see this page
  useEffect(() => {
    // Check if we're in the browser
    if (typeof window !== 'undefined') {
      // If userId exists in localStorage, user has already completed onboarding
      const storedUserId = localStorage.getItem('userId');
      if (storedUserId) {
        router.push('/dashboard');
        return;
      }
      
      // If no userId in URL, redirect to login
      if (!userId) {
        router.push('/login');
      }
    }
  }, [userId, router]);

  const hourOptions = Array.from({ length: 12 }, (_, i) => i + 1)
  const minuteOptions = ["00", "15", "30", "45"]

  // Don't render anything while redirecting
  if (typeof window !== 'undefined' && localStorage.getItem('userId')) {
    return null; 
  }

  // Don't render if no userId in URL
  if (!userId) {
    return null;
  }

  return (
    <div className="flex flex-col items-center justify-center min-h-screen p-4 space-y-4 bg-gray-100">
      <h1 className="text-2xl font-semibold">Complete Your Profile</h1>
      <p className="text-sm text-center mb-4">Please provide your details to continue</p>

      {error && (
        <div className="w-full max-w-md p-3 text-red-500 bg-red-100 rounded">
          {error}
        </div>
      )}

      <div className="w-full max-w-md space-y-4">
        <Input 
          placeholder="Name" 
          name="name" 
          value={formData.name}
          onChange={handleChange}
          disabled={loading}
        />
        <Input 
          placeholder="Phone" 
          name="phone" 
          value={formData.phone}
          onChange={handleChange}
          disabled={loading}
        />

        <div className="w-full">
          <p className="font-medium mt-4 mb-2">Select AM Time</p>
          <div className="flex gap-4">
            <FormControl fullWidth>
              <InputLabel>Hour</InputLabel>
              <Select
                value={amTime.hour}
                label="Hour"
                onChange={(e) => setAmTime((prev) => ({ ...prev, hour: e.target.value }))}
                disabled={loading}
              >
                {hourOptions.map((hour) => (
                  <MenuItem key={hour} value={hour}>
                    {hour}
                  </MenuItem>
                ))}
              </Select>
            </FormControl>

            <FormControl fullWidth>
              <InputLabel>Minute</InputLabel>
              <Select
                value={amTime.minute}
                label="Minute"
                onChange={(e) => setAmTime((prev) => ({ ...prev, minute: e.target.value }))}
                disabled={loading}
              >
                {minuteOptions.map((minute) => (
                  <MenuItem key={minute} value={minute}>
                    {minute}
                  </MenuItem>
                ))}
              </Select>
            </FormControl>
          </div>
        </div>

        <div className="w-full">
          <p className="font-medium mt-4 mb-2">Select PM Time</p>
          <div className="flex gap-4">
            <FormControl fullWidth>
              <InputLabel>Hour</InputLabel>
              <Select
                value={pmTime.hour}
                label="Hour"
                onChange={(e) => setPmTime((prev) => ({ ...prev, hour: e.target.value }))}
                disabled={loading}
              >
                {hourOptions.map((hour) => (
                  <MenuItem key={hour} value={hour}>
                    {hour}
                  </MenuItem>
                ))}
              </Select>
            </FormControl>

            <FormControl fullWidth>
             
[truncated — 798 more characters]
```

### app/settings/page.js

```javascript
'use client';

import {
  Box,
  TextField,
  Typography,
  Button,
  Paper,
  FormControl,
  InputLabel,
  MenuItem,
  Select,
  IconButton,
  Avatar,
  Snackbar,
  Alert,
  CircularProgress,
} from '@mui/material';
import { useState, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import PersonIcon from '@mui/icons-material/Person';
import LogoutIcon from '@mui/icons-material/Logout';

const initialForm = { name: "", phone: "" };
const initialAm = { hour: "", minute: "" };
const initialPm = { hour: "", minute: "" };

export default function SettingsPage() {
  const router = useRouter();

  const [formData, setFormData] = useState(initialForm);
  const [amTime, setAmTime] = useState(initialAm);
  const [pmTime, setPmTime] = useState(initialPm);
  const [hasChanged, setHasChanged] = useState(false);
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [userId, setUserId] = useState(null);
  const [alertOpen, setAlertOpen] = useState(false);
  const [alertMessage, setAlertMessage] = useState("");
  const [alertSeverity, setAlertSeverity] = useState("success");

  useEffect(() => {
    // Check authentication
    if (typeof window !== 'undefined') {
      const storedUserId = localStorage.getItem('userId');
      if (!storedUserId) {
        router.push('/login');
        return;
      }
      setUserId(storedUserId);
      fetchUserDetails(storedUserId);
    }
  }, [router]);

  const fetchUserDetails = async (id) => {
    try {
      setLoading(true);
      const response = await fetch(`http://localhost:8000/api/users/${id}`);
      
      if (!response.ok) {
        throw new Error('Failed to fetch user details');
      }
      
      const userData = await response.json();
      
      // Update form data
      setFormData({
        name: userData.name || "",
        phone: userData.phone || ""
      });
      
      // Parse AM time if exists
      if (userData.timeam) {
        const [hour, minute] = userData.timeam.split(':');
        setAmTime({
          hour: parseInt(hour),
          minute: minute
        });
      }
      
      // Parse PM time if exists
      if (userData.timepm) {
        const [hour, minute] = userData.timepm.split(':');
        setPmTime({
          hour: parseInt(hour),
          minute: minute
        });
      }
      
    } catch (error) {
      console.error('Error fetching user details:', error);
      showAlert('Failed to load user details', 'error');
    } finally {
      setLoading(false);
    }
  };

  const saveChanges = async () => {
    if (!userId) return;
    
    try {
      setSaving(true);
      
      // Format times
      const timeam = amTime.hour && amTime.minute ? `${amTime.hour}:${amTime.minute}` : '';
      const timepm = pmTime.hour && pmTime.minute ? `${pmTime.hour}:${pmTime.minute}` : '';
      
      const response = await fetch(`http://localhost:8000/api/users/${userId}/onboard`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          name: formData.name,
          phone: formData.phone,
          timeam,
          timepm
        })
      });
      
      if (!response.ok) {
        throw new Error('Failed to update user details');
      }
      
      showAlert('Settings updated successfully', 'success');
      
    } catch (error) {
      console.error('Error updating user details:', error);
      showAlert('Failed to update settings', 'error');
    } finally {
      setSaving(false);
    }
  };
  
  const handleLogout = () => {
    if (typeof window !== 'undefined') {
      localStorage.removeItem('userId');
      router.push('/login');
    }
  };
  
  const showAlert = (message, severity = 'success') => {
    setAlertMessage(message);
    setAlertSeverity(severity);
    setAlertOpen(true);
  };
  
  const handleAlertClose = () => {
    setAlertOpen(false);
  };

  useEffect(() => {
    // Track changes to form data
    if (loading) return; // Don't check for changes during initial load
    
    const hasFormChanged = 
      formData.name !== initialForm.name || 
      formData.phone !== initialForm.phone;
      
    const hasAmChanged = 
      amTime.hour !== initialAm.hour || 
      amTime.minute !== initialAm.minute;
      
    const hasPmChanged = 
      pmTime.hour !== initialPm.hour || 
      pmTime.minute !== initialPm.minute;
      
    setHasChanged(hasFormChanged || hasAmChanged || hasPmChanged);
  }, [formData, amTime, pmTime, loading]);

  if (loading) {
    return (
      <Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
        <CircularProgress sx={{ color: '#F99A00' }} />
      </Box>
    );
  }

  return (
    <Box sx={{ p: 3 }}>
      <div className="flex items-center justify-between mb-3">
        <div className="flex items-center gap-2">
          <IconButton onClick={() => router.back()} sx={{ color: '#F99A00' }}>
            <ArrowBackIcon />
          </IconButton>
          <Typography variant="h5" sx={{ color: '#F99A00', fontWeight: 600 }}>
            Your Settings
          </Typography>
        </div>
        <IconButton 
          onClick={handleLogout} 
          sx={{ 
            color: '#F99A00',
            backgroundColor: 'rgba(249, 154, 0, 0.1)',
            '&:hover': {
              backgroundColor: 'rgba(249, 154, 0, 0.2)',
            }
          }}
        >
          <LogoutIcon />
        </IconButton>
      </div>
      
      <Box display="flex" justifyContent="center" mb={2}>
        <Avatar sx={{ width: 200, height: 200, bgcolor: '#F99A00' }}>
          <PersonIcon sx={{ fontSize: 40, color: 'white', width: 100, height: 100 }} />
        </Avatar>
      </Box>

      <Paper elevation={3} sx={{ p: 4, mt: 2, borderRadius: 3, boxShadow: '0 4px 20px rgba(59, 219, 227, 0.5)', }}>
        <Typography variant="h6" sx={{ mb
[truncated — 3989 more characters]
```

### app/dashboard/page.js

```javascript
"use client"

import { StaticDatePicker } from '@mui/x-date-pickers/StaticDatePicker'
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'
import dayjs from 'dayjs'
import TextField from '@mui/material/TextField'
import { 
  Checkbox, 
  FormControlLabel, 
  IconButton, 
  createTheme, 
  ThemeProvider, 
  Typography,
  Button,
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  CircularProgress,
  Box
} from '@mui/material'
import CallIcon from "@mui/icons-material/Call";

import { Settings, Add } from '@mui/icons-material'
import { useState, useEffect } from 'react'
import { useRouter } from 'next/navigation'
import MoodComponent from '../components/mood';
// import { userInfo } from 'os'

export default function Dashboard() {
  const [value, setValue] = useState(dayjs())
  const [tasks, setTasks] = useState([])
  const [mood, setMood] = useState(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState("")
  const router = useRouter()
  
  // Task dialog state
  const [taskDialogOpen, setTaskDialogOpen] = useState(false)
  const [newTaskDescription, setNewTaskDescription] = useState("")

  const handleCheckboxChange = async (id) => {
    try {
      // Find the task
      const task = tasks.find(t => t.id === id);
      if (!task) return;
      
      // Toggle completed status in UI immediately for responsive feel
      setTasks((prevTasks) =>
        prevTasks.map((task) =>
          task.id === id ? { ...task, completed: !task.completed } : task
        )
      );
      
      // Get user ID from localStorage
      const userId = localStorage.getItem('userId');
      if (!userId) {
        console.error("User ID not found");
        return;
      }
      
      // Call API to update task status
      const response = await fetch(`http://localhost:8000/api/tasks/${id}`, {
        method: 'PUT',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          user_id: userId,
          completed: !task.completed
        })
      });
      
      if (!response.ok) {
        throw new Error('Failed to update task');
        // If update fails, revert the UI change
        setTasks((prevTasks) =>
          prevTasks.map((t) =>
            t.id === id ? { ...t, completed: task.completed } : t
          )
        );
      }
      
      // Successfully updated task in backend
      const updatedTask = await response.json();
      console.log('Task updated:', updatedTask);
      
    } catch (error) {
      console.error('Error updating task:', error);
    }
  };

  const handleDateChange = (newValue) => {
    // Always navigate to day view regardless of whether date changed
    const formatted = newValue.format('YYYY-MM-DD');
    router.push(`/day?date=${formatted}`);
    // Update state after navigation to avoid any potential issues
    setValue(newValue);
  }
  
  const handleAddTaskClick = () => {
    setNewTaskDescription("");
    setTaskDialogOpen(true);
  };
  
  const handleTaskDialogClose = () => {
    setTaskDialogOpen(false);
  };
  
  const handleAddTask = async () => {
    if (!newTaskDescription.trim()) return;
    
    try {
      const userId = localStorage.getItem('userId');
      if (!userId) {
        console.error("User ID not found");
        return;
      }
      
      const currentDate = dayjs().format('YYYY-MM-DD');
      
      const response = await fetch('http://localhost:8000/api/tasks', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          user_id: userId,
          date: currentDate,
          description: newTaskDescription,
          completed: false
        })
      });
      
      if (!response.ok) {
        throw new Error('Failed to create task');
      }
      
      const newTask = await response.json();
      setTasks([...tasks, newTask]);
      setTaskDialogOpen(false);
      
    } catch (error) {
      console.error('Error creating task:', error);
    }
  };

  const callUser = async () => {
    try {
      const userId = localStorage.getItem('userId');
      if (!userId) {
        console.error("User ID not found");
        return;
      }
  
      // const response = await fetch("https://capable-stace-alb123-fd82ce46.koyeb.app/make_call", {
      //   method: "POST",
      //   // headers: {
      //   //   "Content-Type": "application/json",
      //   // },
      //   body: JSON.stringify({ user_id: userId }),
      // });

      fetch(`https://capable-stace-alb123-fd82ce46.koyeb.app/make_call?user_id=${userId}`, {
        method: "POST",
      })
        .then((response) => response.json())
        .then((data) => {
          console.log("Call response:", data);
        })
        .catch((error) => {
          console.error("Error making call:", error);
        });
  
      // if (!response.ok) {
      //   const errorData = await response.json(); // This might have more info on the 422
      //   console.error('Failed to initiate call:', errorData);
      //   return;
      // }
  
      // const data = await response.json();
      // console.log('Call initiated:', data);
    } catch (error) {
      console.error('Error initiating call:', error);
    }
  };
  

  useEffect(() => {
    // Simple authentication check
    const userId = localStorage.getItem('userId');
    if (!userId) {
      router.push('/login');
      return;
    }
    // Fetch dashboard data
    const fetchDashboardData = async () => {
      try {
        const currentDate = dayjs().format('YYYY-MM-DD');

        const tasksResponse = await fetch(`http://localhost:8000/api/tasks?userId=${userId}&date=${currentDate}`, {
          method: 'GET',
          headers: {
            'Content-Type': 'application/json',
          }
        });

        const moodResponse = await fetch(`http://localhost:8000/api/mo
[truncated — 6503 more characters]
```

### app/day/page.js

```javascript
'use client';

import React, { useState, useEffect } from 'react';
import { Suspense } from 'react';
import {
  Box,
  Typography,
  Checkbox,
  FormControlLabel,
  IconButton,
  Paper,
  Collapse,
  TextField,
  Button,
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  TextareaAutosize,
} from '@mui/material';
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
import ArrowDropUpIcon from '@mui/icons-material/ArrowDropUp';
import AddIcon from '@mui/icons-material/Add';
import EditIcon from '@mui/icons-material/Edit';
import DeleteIcon from '@mui/icons-material/Delete';
import dayjs from 'dayjs';
import { useSearchParams } from 'next/navigation';
import MoodComponent from '../components/mood';

// Import API - creating a mock API client since we can't directly import from outside Next.js app directory
// We'll implement this directly here instead of trying to import from outside the Next.js app
class DailyBuddyAPI {
  constructor(base_url="http://localhost:8000/api") {
    this.base_url = base_url;
    // For demo purposes, hardcoding user_id
    // In production, this would come from authentication
    this.user_id = typeof window !== 'undefined' ? localStorage.getItem('userId') || "user-123" : "user-123";
  }
  
  // Summary Methods (Morning/Night entries)
  async get_summary(date, am_pm=null) {
    let url = `${this.base_url}/summary`;
    let params = new URLSearchParams({
      user_id: this.user_id,
      date: date
    });
    
    if (am_pm !== null) {
      params.append("am_pm", am_pm);
    }
    
    try {
      const response = await fetch(`${url}?${params.toString()}`);
      if (!response.ok) throw new Error('Network response was not ok');
      return await response.json();
    } catch (error) {
      console.error("Error fetching summary:", error);
      return [];
    }
  }
  
  async create_update_summary(date, text, am_pm) {
    const url = `${this.base_url}/summary`;
    const data = {
      user_id: this.user_id,
      date: date,
      text: text,
      am_pm: am_pm
    };
    
    try {
      const response = await fetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(data)
      });
      
      if (!response.ok) throw new Error('Network response was not ok');
      return await response.json();
    } catch (error) {
      console.error("Error creating/updating summary:", error);
      return null;
    }
  }
  
  // Task Methods
  async get_tasks(date) {
    const url = `${this.base_url}/tasks`;
    const params = new URLSearchParams({
      userId: this.user_id,
      date: date
    });
    
    try {
      const response = await fetch(`${url}?${params.toString()}`);
      if (!response.ok) throw new Error('Network response was not ok');
      return await response.json();
    } catch (error) {
      console.error("Error fetching tasks:", error);
      return [];
    }
  }
  
  async create_task(date, description, completed=false) {
    const url = `${this.base_url}/tasks`;
    const data = {
      user_id: this.user_id,
      date: date,
      description: description,
      completed: completed
    };
    
    try {
      const response = await fetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(data)
      });
      
      if (!response.ok) throw new Error('Network response was not ok');
      return await response.json();
    } catch (error) {
      console.error("Error creating task:", error);
      return null;
    }
  }
  
  async update_task(task_id, description=null, completed=null) {
    const url = `${this.base_url}/tasks/${task_id}`;
    const data = { user_id: this.user_id };
    
    if (description !== null) data.description = description;
    if (completed !== null) data.completed = completed;
    
    try {
      const response = await fetch(url, {
        method: 'PUT',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(data)
      });
      
      if (!response.ok) throw new Error('Network response was not ok');
      return await response.json();
    } catch (error) {
      console.error("Error updating task:", error);
      return null;
    }
  }
  
  async delete_task(task_id) {
    const url = `${this.base_url}/tasks/${task_id}`;
    const params = new URLSearchParams({
      userId: this.user_id
    });
    
    try {
      const response = await fetch(`${url}?${params.toString()}`, {
        method: 'DELETE'
      });
      
      if (!response.ok) throw new Error('Network response was not ok');
      return true;
    } catch (error) {
      console.error("Error deleting task:", error);
      return false;
    }
  }
  
  // Mood Methods
  async get_mood(date) {
    const url = `${this.base_url}/mood`;
    const params = new URLSearchParams({
      userId: this.user_id,
      date: date
    });
    
    try {
      const response = await fetch(`${url}?${params.toString()}`);
      if (!response.ok) throw new Error('Network response was not ok');
      return await response.json();
    } catch (error) {
      console.error("Error fetching mood:", error);
      return null;
    }
  }
  
  async create_update_mood(date, happy, sad, angry, calm) {
    const url = `${this.base_url}/mood`;
    const data = {
      user_id: this.user_id,
      date: date,
      happy: happy,
      sad: sad,
      angry: angry,
      calm: calm
    };
    
    try {
      const response = await fetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(data)
      });
      
      if (!response.ok) throw new Error('Network response was not ok');
      return await response.json();
    } catch (error) {
      console.error("Error creating/updating mood:", error);
   
[truncated — 17448 more characters]
```

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