# Project export: PrepPal

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: UC Berkeley AI Hackathon 2025
- Tagline: An AI-powered Meal Planner
- Devpost: https://devpost.com/software/preppal-w89jai
- GitHub: https://github.com/tanushri-p/Prep_Pal
- Video: https://www.youtube.com/embed/wLJSD4FXqJU?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 4 GitHub contributor(s) — anaisha-d (6 commits), gpt-engineer-app[bot] (6 commits), Tavisha (2 commits), tanushri-p (1 commits)

## Devpost submission (written by the team)

### Inspiration

As busy college students who have recently started living on our own, we've realized that deciding what to meal prep or cook everyday is more difficult than it seems. This challenge is further exacerbated for students/working professionals with significant dietary restrictions. Individuals with medical conditions such as celiac disease, IBS, gastrointestinal disorders, diabetes, or severe allergies often have trouble finding delicious yet suitable meals for their specific dietary needs. We created PrepPal to help all of these people spend less time and energy searching the internet for recipes that work for them and instead enjoy their personalized meals!

### What it does

PrepPal is an AI-powered tool that helps you plan personalized meals for the week and generate a corresponding grocery list so you can buy your ingredients in one go. Simply enter any dietary restrictions/preferences you have, select your budget constraints and prep time constraints, and enter any existing ingredients you would like to incorporate in your meals. PrepPal will take into account all your preferences and give you breakfast, lunch, and dinner for all seven days of the week with details on necessary prep time and how to make it. Then, depending on your generated meals, PrepPal will also give you a grocery list separated by category (i.e. produce, protein) that you can check off as you gather the ingredients.

### How we built it

Frontend: We used Lovable, a platform that uses AI to generate a working frontend. This used TypeScript, Tailwind CSS, and React. Integration: We extracted the information from the form submission in the frontend, and used TypeScript to make an API call to our backend. Backend: From the API call to our backend, we set up another API call to Gemini API and received the meal plan and grocery list Gemini generated in JSON format.

### Challenges we ran into

We weren't familiar with TypeScript, so integrating our frontend with our backend logic took lots of trial and error. We initially intended for PrepPal to utilize Vapi and to be a voice-based assistant, however we ran into trouble with retrieving the specific call data using the Vapi API. Therefore, we had to pivot to using a form-based input.

### Accomplishments we're proud of

We successfully integrated our frontend with our backend logic that prompted Gemini API. Even though we had to pivot our initial plans a bit, we still persevered and put together a working final product.

### What we learned

We all learned how to work with React for the first time. We also figured out how to work with Gemini API and efficient prompt engineering strategies. Lastly, we explored using Vapi and created our own voice assistant for PrepPal, although it didn't make the final product.

### What's next

Implementing additional features such as retrying to implement a voice assistant using Vapi, including an estimated cost for each grocery item based on location, and including AI-generated images of each meal for user reference.

## README (from the GitHub repository)

# PrepPal

## Inspiration

As busy college students who have recently started living on our own, we've realized that deciding what to meal prep or cook everyday is more difficult than it seems. This challenge is further exacerbated for students/working professionals with significant dietary restrictions. Individuals with medical conditions such as celiac disease, IBS, gastrointestinal disorders, diabetes, or severe allergies often have trouble finding delicious yet suitable meals for their specific dietary needs. We created PrepPal to help all of these people spend less time and energy searching the internet for recipes that work for them and instead enjoy their personalized meals!
 
## What it does

PrepPal is an AI-powered tool that helps you plan personalized meals for the week and generate a corresponding grocery list so you can buy your ingredients in one go. Simply enter any dietary restrictions/preferences you have, select your budget constraints and prep time constraints, and enter any existing ingredients you would like to incorporate in your meals. PrepPal will take into account all your preferences and give you breakfast, lunch, and dinner for all seven days of the week with details on necessary prep time and how to make it. Then, depending on your generated meals, PrepPal will also give you a grocery list separated by category (i.e. produce, protein) that you can check off as you gather the ingredients.

## How we built it

- Frontend: We used Lovable, a platform that uses AI to generate a working frontend. This used TypeScript, Tailwind CSS, and React.
- Integration: We extracted the information from the form submission in the frontend, and used TypeScript to make an API call to our backend.
- Backend: From the API call to our backend, we set up another API call to Gemini API and received the meal plan and grocery list Gemini generated in JSON format.

## Challenges we ran into
- We weren't familiar with TypeScript, so integrating our frontend with our backend logic took lots of trial and error.
- We initially intended for PrepPal to utilize Vapi and to be a voice-based assistant, however we ran into trouble with retrieving the specific call data using the Vapi API. Therefore, we had to pivot to using a form-based input.

## Accomplishments that we're proud of
- We successfully integrated our frontend with our backend logic that prompted Gemini API.
- Even though we had to pivot our initial plans a bit, we still persevered and put together a working final product.

## What we learned
- We all learned how to work with React for the first time.
- We also figured out how to work with Gemini API and efficient prompt engineering strategies.
- Lastly, we explored using Vapi and created our own voice assistant for PrepPal, although it didn't make the final product.

## What's next for PrepPal
Implementing additional features such as retrying to implement a voice assistant using Vapi, including an estimated cost for each grocery item based on location, and including AI-generated images of each meal for user reference.


## Detected evidence (automated analysis)

Indexed codebase: 74 recognized source files, 209 KB.
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Google Gemini (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (85 of 85)

```
backend/.env
backend/get_output.py
backend/requirements.txt
backend/schema/output_schema.py
frontend/.gitignore
frontend/bun.lockb
frontend/components.json
frontend/eslint.config.js
frontend/index.html
frontend/package.json
frontend/postcss.config.js
frontend/public/robots.txt
frontend/README.md
frontend/src/App.css
frontend/src/App.tsx
frontend/src/components/ConversationInterface.tsx
frontend/src/components/GroceryList.tsx
frontend/src/components/HeroSection.tsx
frontend/src/components/MealPlanDisplay.tsx
frontend/src/components/PrepStyleSelector.tsx
frontend/src/components/ui/accordion.tsx
frontend/src/components/ui/alert-dialog.tsx
frontend/src/components/ui/alert.tsx
frontend/src/components/ui/aspect-ratio.tsx
frontend/src/components/ui/avatar.tsx
frontend/src/components/ui/badge.tsx
frontend/src/components/ui/breadcrumb.tsx
frontend/src/components/ui/button.tsx
frontend/src/components/ui/calendar.tsx
frontend/src/components/ui/card.tsx
frontend/src/components/ui/carousel.tsx
frontend/src/components/ui/chart.tsx
frontend/src/components/ui/checkbox.tsx
frontend/src/components/ui/collapsible.tsx
frontend/src/components/ui/command.tsx
frontend/src/components/ui/context-menu.tsx
frontend/src/components/ui/dialog.tsx
frontend/src/components/ui/drawer.tsx
frontend/src/components/ui/dropdown-menu.tsx
frontend/src/components/ui/form.tsx
frontend/src/components/ui/hover-card.tsx
frontend/src/components/ui/input-otp.tsx
frontend/src/components/ui/input.tsx
frontend/src/components/ui/label.tsx
frontend/src/components/ui/menubar.tsx
frontend/src/components/ui/navigation-menu.tsx
frontend/src/components/ui/pagination.tsx
frontend/src/components/ui/popover.tsx
frontend/src/components/ui/progress.tsx
frontend/src/components/ui/radio-group.tsx
frontend/src/components/ui/resizable.tsx
frontend/src/components/ui/scroll-area.tsx
frontend/src/components/ui/select.tsx
frontend/src/components/ui/separator.tsx
frontend/src/components/ui/sheet.tsx
frontend/src/components/ui/sidebar.tsx
frontend/src/components/ui/skeleton.tsx
frontend/src/components/ui/slider.tsx
frontend/src/components/ui/sonner.tsx
frontend/src/components/ui/switch.tsx
frontend/src/components/ui/table.tsx
frontend/src/components/ui/tabs.tsx
frontend/src/components/ui/textarea.tsx
frontend/src/components/ui/toast.tsx
frontend/src/components/ui/toaster.tsx
frontend/src/components/ui/toggle-group.tsx
frontend/src/components/ui/toggle.tsx
frontend/src/components/ui/tooltip.tsx
frontend/src/components/ui/use-toast.ts
frontend/src/hooks/use-mobile.tsx
frontend/src/hooks/use-toast.ts
frontend/src/index.css
frontend/src/lib/utils.ts
frontend/src/main.tsx
frontend/src/pages/Index.tsx
frontend/src/pages/NotFound.tsx
frontend/src/types/api.ts
frontend/src/vite-env.d.ts
frontend/tailwind.config.ts
frontend/tsconfig.app.json
frontend/tsconfig.json
frontend/tsconfig.node.json
frontend/vite.config.ts
package.json
README.md
```

### Dependencies

- backend/requirements.txt: fastapi@==0.104.1, google-genai@>=0.2.0, pydantic@==2.5.0, python-dotenv@==1.0.0, uvicorn[standard]@==0.24.0
- frontend/package.json: @eslint/js@^9.9.0, @hookform/resolvers@^3.9.0, @radix-ui/react-accordion@^1.2.0, @radix-ui/react-alert-dialog@^1.1.1, @radix-ui/react-aspect-ratio@^1.1.0, @radix-ui/react-avatar@^1.1.0, @radix-ui/react-checkbox@^1.1.1, @radix-ui/react-collapsible@^1.1.0, @radix-ui/react-context-menu@^2.2.1, @radix-ui/react-dialog@^1.1.2, @radix-ui/react-dropdown-menu@^2.1.1, @radix-ui/react-hover-card@^1.1.1, @radix-ui/react-label@^2.1.0, @radix-ui/react-menubar@^1.1.1, @radix-ui/react-navigation-menu@^1.2.0, @radix-ui/react-popover@^1.1.1, @radix-ui/react-progress@^1.1.0, @radix-ui/react-radio-group@^1.2.0, @radix-ui/react-scroll-area@^1.1.0, @radix-ui/react-select@^2.1.1, @radix-ui/react-separator@^1.1.0, @radix-ui/react-slider@^1.2.0, @radix-ui/react-slot@^1.1.0, @radix-ui/react-switch@^1.1.0, @radix-ui/react-tabs@^1.1.0, @radix-ui/react-toast@^1.2.1, @radix-ui/react-toggle@^1.1.0, @radix-ui/react-toggle-group@^1.1.0, @radix-ui/react-tooltip@^1.1.4, @tailwindcss/typography@^0.5.15, @tanstack/react-query@^5.56.2, @types/node@^22.5.5, @types/react@^18.3.3, @types/react-dom@^18.3.0, @vitejs/plugin-react-swc@^3.5.0, autoprefixer@^10.4.20, class-variance-authority@^0.7.1, clsx@^2.1.1, cmdk@^1.0.0, date-fns@^3.6.0, embla-carousel-react@^8.3.0, eslint@^9.9.0, eslint-plugin-react-hooks@^5.1.0-rc.0, eslint-plugin-react-refresh@^0.4.9, globals@^15.9.0, input-otp@^1.2.4, lovable-tagger@^1.1.7, lucide-react@^0.462.0, next-themes@^0.3.0, postcss@^8.4.47, react@^18.3.1, react-day-picker@^8.10.1, react-dom@^18.3.1, react-hook-form@^7.53.0, react-resizable-panels@^2.1.3, react-router-dom@^6.26.2, recharts@^2.12.7, sonner@^1.5.0, tailwind-merge@^2.5.2, tailwindcss@^3.4.11, tailwindcss-animate@^1.0.7, typescript@^5.5.3, typescript-eslint@^8.0.1, vaul@^0.9.3, vite@^5.4.19, zod@^3.23.8
- package.json: concurrently@^7.6.0

### Recent commits (newest first)

- Merge branch 'main' of https://github.com/tanushri-p/Prep_Pal
- Fixed some small bugs
- Create README.md
- Create package-lock.json
- Create package.json
- Update get_output.py
- integrated frontend with backend
- im dying pls help
- added backend
- Refactor: Remove voice agent and update hero section
- Remove "PrepPal" text from title
- Fix: Improve logo theme and meal plan generation
- Refactor: Update logo, color theme, and input fields
- feat: Implement meal planning application
- Use tech stack vite_react_shadcn_ts

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

### package.json

```
{
  "scripts": {
    "dev": "concurrently --names \"FRONTEND,BACKEND\" --prefix-colors \"blue,green\" \"npm run dev:frontend\" \"npm run dev:backend\"",
    "dev:frontend": "cd frontend && npm run dev",
    "dev:backend": "cd backend && python -m uvicorn get_output:app --reload --port 8000"
  },
  "devDependencies": {
    "concurrently": "^7.6.0"
  }
}

```

### backend/requirements.txt

```
fastapi==0.104.1
uvicorn[standard]==0.24.0
python-dotenv==1.0.0
google-genai>=0.2.0
pydantic==2.5.0
```

### frontend/package.json

```
{
  "name": "vite_react_shadcn_ts",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "build:dev": "vite build --mode development",
    "lint": "eslint .",
    "preview": "vite preview"
  },
  "dependencies": {
    "@hookform/resolvers": "^3.9.0",
    "@radix-ui/react-accordion": "^1.2.0",
    "@radix-ui/react-alert-dialog": "^1.1.1",
    "@radix-ui/react-aspect-ratio": "^1.1.0",
    "@radix-ui/react-avatar": "^1.1.0",
    "@radix-ui/react-checkbox": "^1.1.1",
    "@radix-ui/react-collapsible": "^1.1.0",
    "@radix-ui/react-context-menu": "^2.2.1",
    "@radix-ui/react-dialog": "^1.1.2",
    "@radix-ui/react-dropdown-menu": "^2.1.1",
    "@radix-ui/react-hover-card": "^1.1.1",
    "@radix-ui/react-label": "^2.1.0",
    "@radix-ui/react-menubar": "^1.1.1",
    "@radix-ui/react-navigation-menu": "^1.2.0",
    "@radix-ui/react-popover": "^1.1.1",
    "@radix-ui/react-progress": "^1.1.0",
    "@radix-ui/react-radio-group": "^1.2.0",
    "@radix-ui/react-scroll-area": "^1.1.0",
    "@radix-ui/react-select": "^2.1.1",
    "@radix-ui/react-separator": "^1.1.0",
    "@radix-ui/react-slider": "^1.2.0",
    "@radix-ui/react-slot": "^1.1.0",
    "@radix-ui/react-switch": "^1.1.0",
    "@radix-ui/react-tabs": "^1.1.0",
    "@radix-ui/react-toast": "^1.2.1",
    "@radix-ui/react-toggle": "^1.1.0",
    "@radix-ui/react-toggle-group": "^1.1.0",
    "@radix-ui/react-tooltip": "^1.1.4",
    "@tanstack/react-query": "^5.56.2",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "cmdk": "^1.0.0",
    "date-fns": "^3.6.0",
    "embla-carousel-react": "^8.3.0",
    "input-otp": "^1.2.4",
    "lucide-react": "^0.462.0",
    "next-themes": "^0.3.0",
    "react": "^18.3.1",
    "react-day-picker": "^8.10.1",
    "react-dom": "^18.3.1",
    "react-hook-form": "^7.53.0",
    "react-resizable-panels": "^2.1.3",
    "react-router-dom": "^6.26.2",
    "recharts": "^2.12.7",
    "sonner": "^1.5.0",
    "tailwind-merge": "^2.5.2",
    "tailwindcss-animate": "^1.0.7",
    "vaul": "^0.9.3",
    "zod": "^3.23.8"
  },
  "devDependencies": {
    "@eslint/js": "^9.9.0",
    "@tailwindcss/typography": "^0.5.15",
    "@types/node": "^22.5.5",
    "@types/react": "^18.3.3",
    "@types/react-dom": "^18.3.0",
    "@vitejs/plugin-react-swc": "^3.5.0",
    "autoprefixer": "^10.4.20",
    "eslint": "^9.9.0",
    "eslint-plugin-react-hooks": "^5.1.0-rc.0",
    "eslint-plugin-react-refresh": "^0.4.9",
    "globals": "^15.9.0",
    "lovable-tagger": "^1.1.7",
    "postcss": "^8.4.47",
    "tailwindcss": "^3.4.11",
    "typescript": "^5.5.3",
    "typescript-eslint": "^8.0.1",
    "vite": "^5.4.19"
  }
}

```

### frontend/src/main.tsx

```typescript
import { createRoot } from 'react-dom/client'
import App from './App.tsx'
import './index.css'

createRoot(document.getElementById("root")!).render(<App />);

```

### frontend/src/App.tsx

```typescript
import { Toaster } from "@/components/ui/toaster";
import { Toaster as Sonner } from "@/components/ui/sonner";
import { TooltipProvider } from "@/components/ui/tooltip";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Index from "./pages/Index";
import NotFound from "./pages/NotFound";


const queryClient = new QueryClient();

const App = () => (
  <QueryClientProvider client={queryClient}>
    <TooltipProvider>
      <Toaster />
      <Sonner />
      <BrowserRouter>
        <Routes>
          <Route path="/" element={<Index />} />
          {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
          <Route path="*" element={<NotFound />} />
        </Routes>
      </BrowserRouter>
    </TooltipProvider>
  </QueryClientProvider>
);

export default App;

```

### frontend/src/pages/Index.tsx

```typescript

import React, { useState } from 'react';
import HeroSection from '@/components/HeroSection';
import ConversationInterface from '@/components/ConversationInterface';
import MealPlanDisplay from '@/components/MealPlanDisplay';
import GroceryList from '@/components/GroceryList';

export type AppState = 'hero' | 'conversation' | 'meal-plan' | 'grocery-list';
export type PrepStyle = 'bulk_prep' | 'daily_light' | 'minimal_cleanup';

export interface UserPreferences {
  dietary: string[];
  timeConstraints: string;
  familySize: number;
  prepStyle?: PrepStyle;
}

export interface MealPlan {
  week: string;
  prepStyle: PrepStyle;
  meals: DayMeals[];
}

export interface DayMeals {
  day: string;
  breakfast: Meal;
  lunch: Meal;
  dinner: Meal;
}

export interface Meal {
  name: string;
  prepTime: string;
  difficulty: 'Easy' | 'Medium' | 'Hard';
  explanation: string;
  ingredients: string[];
  nutrition: string[];
}

export interface GroceryData {
  produce: string[];
  protein: string[];
  pantry: string[];
  dairy: string[];
}

const Index = () => {

  const [currentState, setCurrentState] = useState<AppState>('hero');
  const [userPreferences, setUserPreferences] = useState<UserPreferences>({
    dietary: [],
    timeConstraints: '',
    familySize: 1
  });
  const [mealPlan, setMealPlan] = useState<MealPlan | null>(null);
  const [groceryList, setGroceryList] = useState<GroceryData | null>(null);

  const handleInputSubmit = async (input: string, isVoice: boolean = false) => {
  console.log('Input received:', input, 'Voice:', isVoice);

  try {
  // Make POST request to your FastAPI backend
  const response = await fetch('http://localhost:8000/generate-meal-plan-simple', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      prompt: input
    })
  });

  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }

  const data = await response.json();
  console.log('API Response:', data.response.meal_plan);

  setMealPlan(data.response.meal_plan);
  console.log('THIS IS WORKING #1');
  setGroceryList(data.response.grocery_list);
  console.log('THIS IS WORKING #2');
  setCurrentState('meal-plan');
  console.log('THIS IS WORKING #3');

  } catch {
    // const mockMealPlan: MealPlan = {
    //   week: '2024-01-15',
    //   prepStyle: 'daily_light',
    //   meals: [
    //     {
    //       day: 'Monday',
    //       breakfast: {
    //         name: 'Overnight Oats with Berries',
    //         prepTime: '5 min',
    //         difficulty: 'Easy',
    //         explanation: 'High fiber start that uses berries you\'ll have all week. Perfect for busy mornings.',
    //         ingredients: ['oats', 'berries', 'yogurt', 'honey'],
    //         nutrition: ['High Fiber', 'Protein', 'Antioxidants']
    //       },
    //       lunch: {
    //         name: 'Mediterranean Quinoa Bowl',
    //         prepTime: '15 min',
    //         difficulty: 'Easy',
    //         explanation: 'Protein-packed quinoa with fresh vegetables. Can be prepped in bulk.',
    //         ingredients: ['quinoa', 'cucumber', 'tomatoes', 'feta', 'olive oil'],
    //         nutrition: ['Complete Protein', 'Healthy Fats', 'Vitamins']
    //       },
    //       dinner: {
    //         name: 'Herb-Crusted Salmon',
    //         prepTime: '25 min',
    //         difficulty: 'Medium',
    //         explanation: 'Omega-3 rich salmon with herbs you\'ll use throughout the week.',
    //         ingredients: ['salmon', 'herbs', 'lemon', 'asparagus'],
    //         nutrition: ['Omega-3', 'Protein', 'B Vitamins']
    //       }
    //     },
    //     {
    //       day: 'Tuesday',
    //       breakfast: {
    //         name: 'Greek Yogurt Parfait',
    //         prepTime: '3 min',
    //         difficulty: 'Easy',
    //         explanation: 'Quick protein boost using the same berries and yogurt from yesterday.',
    //         ingredients: ['yogurt', 'berries', 'granola', 'honey'],
    //         nutrition: ['Probiotics', 'Protein', 'Fiber']
    //       },
    //       lunch: {
    //         name: 'Leftover Quinoa Wrap',
    //         prepTime: '10 min',
    //         difficulty: 'Easy',
    //         explanation: 'Transform yesterday\'s quinoa bowl into a portable wrap.',
    //         ingredients: ['quinoa bowl', 'tortilla', 'hummus', 'spinach'],
    //         nutrition: ['Complete Protein', 'Fiber', 'Iron']
    //       },
    //       dinner: {
    //         name: 'One-Pot Chicken & Vegetables',
    //         prepTime: '30 min',
    //         difficulty: 'Easy',
    //         explanation: 'Minimal cleanup dinner that provides protein for tomorrow\'s lunch.',
    //         ingredients: ['chicken', 'mixed vegetables', 'herbs', 'broth'],
    //         nutrition: ['Lean Protein', 'Vitamins', 'Minerals']
    //       }
    //     },
    //     {
    //       day: 'Wednesday',
    //       breakfast: {
    //         name: 'Avocado Toast with Egg',
    //         prepTime: '8 min',
    //         difficulty: 'Easy',
    //         explanation: 'Healthy fats and protein to power through mid-week.',
    //         ingredients: ['avocado', 'bread', 'egg', 'tomato'],
    //         nutrition: ['Healthy Fats', 'Protein', 'Fiber']
    //       },
    //       lunch: {
    //         name: 'Chicken Salad from Yesterday',
    //         prepTime: '5 min',
    //         difficulty: 'Easy',
    //         explanation: 'Using leftover chicken in a fresh, crunchy salad.',
    //         ingredients: ['leftover chicken', 'lettuce', 'carrots', 'dressing'],
    //         nutrition: ['Lean Protein', 'Vitamins', 'Fiber']
    //       },
    //       dinner: {
    //         name: 'Vegetarian Stir-Fry',
    //         prepTime: '20 min',
    //         difficulty: 'Easy',
    //         explanation: 'Colorful vegetables with tofu for a plant-based protein boost.',
    //         ingredients: ['tofu', 'bell peppers', 'broccol
[truncated — 7740 more characters]
```

### frontend/postcss.config.js

```javascript
export default {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}

```

### frontend/vite.config.ts

```typescript
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import path from "path";
import { componentTagger } from "lovable-tagger";

// https://vitejs.dev/config/
export default defineConfig(({ mode }) => ({
  server: {
    host: "::",
    port: 8080,
  },
  plugins: [
    react(),
    mode === 'development' &&
    componentTagger(),
  ].filter(Boolean),
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
}));

```

### frontend/eslint.config.js

```javascript
import js from "@eslint/js";
import globals from "globals";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import tseslint from "typescript-eslint";

export default tseslint.config(
  { ignores: ["dist"] },
  {
    extends: [js.configs.recommended, ...tseslint.configs.recommended],
    files: ["**/*.{ts,tsx}"],
    languageOptions: {
      ecmaVersion: 2020,
      globals: globals.browser,
    },
    plugins: {
      "react-hooks": reactHooks,
      "react-refresh": reactRefresh,
    },
    rules: {
      ...reactHooks.configs.recommended.rules,
      "react-refresh/only-export-components": [
        "warn",
        { allowConstantExport: true },
      ],
      "@typescript-eslint/no-unused-vars": "off",
    },
  }
);

```

### frontend/index.html

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>cook-chat-weekly-meals</title>
    <meta name="description" content="Lovable Generated Project" />
    <meta name="author" content="Lovable" />

    <meta property="og:title" content="cook-chat-weekly-meals" />
    <meta property="og:description" content="Lovable Generated Project" />
    <meta property="og:type" content="website" />
    <meta property="og:image" content="https://lovable.dev/opengraph-image-p98pqg.png" />

    <meta name="twitter:card" content="summary_large_image" />
    <meta name="twitter:site" content="@lovable_dev" />
    <meta name="twitter:image" content="https://lovable.dev/opengraph-image-p98pqg.png" />
  </head>

  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

```

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