# Project export: CreatorFlow

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

## Project metadata

- Hackathon: Cal Hacks 11.0
- Tagline: AI-Driven Agents and LLMs Powering Effortless Content Scheduling and Generation for Creators
- Devpost: https://devpost.com/software/createflow
- GitHub: https://github.com/dhrumilankola/Calhacks11_CreateFlow
- Video: https://www.youtube.com/embed/xBUmhCGe1Vg?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — dhrumilankola (2 commits)

## Devpost submission (written by the team)

### Inspiration

CreateFlow was inspired by the pressure content creators face managing ideas, drafting, and posting across platforms, leading to burnout. I wanted to create a tool that helps creators focus on their craft while automating repetitive tasks using AI-driven agents and LLMs to streamline content creation and scheduling. What We Learned As a team, we explored LLMs for generating human-like text and AI agents for automating scheduling and trend analysis. We also learned to design a user-friendly dashboard for manual review due to challenges with social media API access. What It Does CreateFlow automates content generation based on user preferences (interest, type, keywords, frequency). It creates a content schedule, generates posts, and suggests future topics, all displayed on a dashboard for review and manual posting. How We Built It We used Python for the backend, Next.js for the dashboard, and the Gemini API to generate content. Fetch AI agents handle scheduling and topic suggestions, while the dashboard offers full control to users. Challenges We Ran Into API limitations for social media posting led us to build a manual dashboard. Ensuring AI-generated content was natural and engaging required fine-tuning language models. Accomplishments We successfully integrated AI to reduce mental load for creators, automating content generation while giving users control through the dashboard. What's Next We plan to integrate more platforms, enhance AI trend detection, and expand dashboard features, including analytics and automated posting as API access improves.

## README (from the GitHub repository)

No README available.

## Detected evidence (automated analysis)

Indexed codebase: 36 recognized source files, 60 KB.
- CSS (language) — 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
- Flask (technology) — claimed on Devpost, not found in the code
- Google Gemini (technology) — claimed on Devpost, not found in the code
- Node.js (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (42 of 42)

```
.gitignore
backend/agents/__init__.py
backend/agents/content_generation_agent.py
backend/agents/main_coordinator_agent.py
backend/agents/scheduling_agent.py
backend/agents/storage_agent.py
backend/agents/topic_suggestion_agent.py
backend/app.py
frontend/.gitignore
frontend/package.json
frontend/public/index.html
frontend/public/manifest.json
frontend/public/robots.txt
frontend/README.md
frontend/src/App.css
frontend/src/App.js
frontend/src/App.test.js
frontend/src/components/FormStep.css
frontend/src/components/FormStep.js
frontend/src/components/GeneratingAnimation.css
frontend/src/components/GeneratingAnimation.js
frontend/src/components/Navbar.js
frontend/src/components/ScheduleCard.js
frontend/src/components/ScheduleDisplay.css
frontend/src/components/ScheduleDisplay.js
frontend/src/components/SharedCardStyles.css
frontend/src/components/SummariesCard.js
frontend/src/components/TweetCard.css
frontend/src/components/TweetCard.js
frontend/src/components/TypingAnimation.css
frontend/src/components/TypingAnimation.js
frontend/src/index.css
frontend/src/index.js
frontend/src/pages/Step1.js
frontend/src/pages/Step2.js
frontend/src/pages/Step3.js
frontend/src/pages/Step4.js
frontend/src/pages/TweetsPage.css
frontend/src/pages/TweetsPage.js
frontend/src/reportWebVitals.js
frontend/src/setupTests.js
package.json
```

### Dependencies

- frontend/package.json: @testing-library/jest-dom@^5.17.0, @testing-library/react@^13.4.0, @testing-library/user-event@^13.5.0, axios@^1.7.7, bootstrap@^5.3.3, cra-template@1.2.0, react@^18.3.1, react-dom@^18.3.1, react-scripts@5.0.1, web-vitals@^2.1.4
- package.json: react-markdown@^9.0.1

### Recent commits (newest first)

- Final Agents - Working
- intial commit, working backend and frontend, Agents working, WIP

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

### package.json

```
{
  "dependencies": {
    "react-markdown": "^9.0.1"
  }
}

```

### frontend/package.json

```
{
  "name": "frontend",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "axios": "^1.7.7",
    "bootstrap": "^5.3.3",
    "cra-template": "1.2.0",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

```

### backend/app.py

```python
from flask import Flask, request, jsonify
from flask_cors import CORS
import google.generativeai as genai
from datetime import datetime
from dotenv import load_dotenv
import os
load_dotenv()



app = Flask(__name__)
CORS(app)

genai.configure(api_key=os.getenv('GOOGLE_API_KEY'))

def generate_schedule(post_frequency):
    prompt = f"""Generate a posting schedule for {post_frequency} posts per week.
    Format the response as a markdown list with emojis, like this:
    
    1. 📅 [Day] at [Time]
    2. 📅 [Day] at [Time]
    3. 📅 [Day] at [Time]
    """
    
    model = genai.GenerativeModel(model_name="gemini-1.5-flash")
    response = model.generate_content(prompt)
    return response.text

def generate_summaries(area_of_interest, content_type, keywords, schedule):
    prompt = f"""Given the following context:
    - Area of interest: {area_of_interest}
    - Content type: {content_type}
    - Keywords: {', '.join(keywords)}
    - Schedule: {schedule}

    Generate brief summaries for {len(schedule.split('\n'))} posts.
    Format the response as a markdown list with emojis related to the content, like this:
    
    1. 📌 [Emoji] Brief summary of post 1
    2. 📌 [Emoji] Brief summary of post 2
    3. 📌 [Emoji] Brief summary of post 3
    """
    
    model = genai.GenerativeModel(model_name="gemini-1.5-flash")
    response = model.generate_content(prompt)
    return response.text

def generate_full_posts(area_of_interest, content_type, keywords, schedule, summaries):
    current_date = datetime.now().strftime('%Y-%m-%d')
    prompt = f"""Given the following context:
    - Area of interest: {area_of_interest}
    - Content type: {content_type}
    - Keywords: {', '.join(keywords)}
    - Schedule: {schedule}
    - Summaries: {summaries}

    Generate full content for each post. Format each post as follows:

    ### Post [Number] - {current_date}
    
    #### 🚀 [Catchy Title]
    
    [Full content of the post]
    
    **Key Points:**
    - 🔑 [Key point 1]
    - 🔑 [Key point 2]
    - 🔑 [Key point 3]
    
    #[Hashtag1] #[Hashtag2] #[Hashtag3]
    """
    
    model = genai.GenerativeModel(model_name="gemini-1.5-flash")
    response = model.generate_content(prompt)
    return response.text

@app.route('/schedule', methods=['POST'])
def generate_content():
    try:
        data = request.get_json()
        area_of_interest = data.get('area_of_interest')
        content_type = data.get('content_type')
        post_frequency = data.get('post_frequency')
        keywords = data.get('keywords')

        schedule = generate_schedule(post_frequency)
        summaries = generate_summaries(area_of_interest, content_type, keywords, schedule)
        full_posts = generate_full_posts(area_of_interest, content_type, keywords, schedule, summaries)

        return jsonify({
            'schedule': schedule,
            'post_summaries': summaries,
            'full_posts': full_posts,
        }), 200
    except Exception as e:
        print(f"Error: {str(e)}")
        return jsonify({'detail': str(e)}), 500

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000, debug=True)
```

### frontend/src/index.js

```javascript
import 'bootstrap/dist/css/bootstrap.min.css';
import './index.css';
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

```

### frontend/src/App.js

```javascript
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import Navbar from './components/Navbar';
import FormStep from './components/FormStep';
import ScheduleCard from './components/ScheduleCard';
import SummariesCard from './components/SummariesCard';
import TweetCard from './components/TweetCard';
import TypingAnimation from './components/TypingAnimation';
import GeneratingAnimation from './components/GeneratingAnimation';
import './App.css';
import './components/SharedCardStyles.css';

const App = () => {
  const [step, setStep] = useState(0);
  const [areaOfInterest, setAreaOfInterest] = useState('');
  const [contentType, setContentType] = useState('');
  const [postFrequency, setPostFrequency] = useState('');
  const [keywords, setKeywords] = useState('');
  const [schedule, setSchedule] = useState('');
  const [postSummaries, setPostSummaries] = useState([]);
  const [fullPosts, setFullPosts] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    console.log('Full Posts:', fullPosts);
  }, [fullPosts]);

  const getFrequencyCount = (frequency) => {
    switch (frequency) {
      case 'Three Times a Week':
        return 3;
      case 'Weekly Once':
        return 1;
      case 'Biweekly':
        return 2;
      case 'Monthly':
        return 4; // Assuming 4 weeks in a month
      default:
        return 3; // Default to 3 if unknown
    }
  };

  const handleSubmit = async () => {
    setIsLoading(true);
    setError(null);
    setStep(5); // Immediately move to the next step to show animation
    
    try {
      const response = await axios.post('http://localhost:8000/schedule', {
        area_of_interest: areaOfInterest,
        content_type: contentType,
        post_frequency: postFrequency,
        keywords: keywords.split(',').map(k => k.trim()),
      });

      const { schedule, post_summaries, full_posts } = response.data;
      
      setSchedule(schedule);
      setPostSummaries(post_summaries);
      
      // Process full_posts
      if (typeof full_posts === 'string') {
        const processedPosts = full_posts.split(/(?=### Post)/).filter(post => post.trim() !== '');
        setFullPosts(processedPosts);
      } else if (Array.isArray(full_posts)) {
        setFullPosts(full_posts);
      } else {
        console.error('Unexpected full_posts format:', full_posts);
        setFullPosts([]);
      }

      setStep(6); // Move to the final step to display content
    } catch (error) {
      console.error('Error:', error);
      setError('An error occurred while generating content. Please try again.');
      setStep(4); // Go back to the form if there's an error
    } finally {
      setIsLoading(false);
    }
  };

  const renderFormSteps = () => {
    return (
      <>
        <FormStep stepNumber={1} title="Area of Interest" isVisible={step >= 1}>
          <div className="options">
            {['Technology', 'Automotive', 'Music', 'Health', 'Fashion', 'Sports', 'Travel'].map((option) => (
              <div 
                key={option}
                className={`option ${areaOfInterest === option ? 'selected' : ''}`} 
                onClick={() => { setAreaOfInterest(option); setStep(2); }}
              >
                {option}
              </div>
            ))}
          </div>
        </FormStep>

        <FormStep stepNumber={2} title="Type of Content" isVisible={step >= 2}>
          <div className="options">
            {['Educational', 'Updates', 'Formal', 'Informal', 'Tutorial', 'Announcements'].map((option) => (
              <div 
                key={option}
                className={`option ${contentType === option ? 'selected' : ''}`} 
                onClick={() => { setContentType(option); setStep(3); }}
              >
                {option}
              </div>
            ))}
          </div>
        </FormStep>

        <FormStep stepNumber={3} title="Frequency" isVisible={step >= 3}>
          <div className="options">
            {['Three Times a Week', 'Weekly Once', 'Monthly', 'Biweekly'].map((option) => (
              <div 
                key={option}
                className={`option ${postFrequency === option ? 'selected' : ''}`} 
                onClick={() => { setPostFrequency(option); setStep(4); }}
              >
                {option}
              </div>
            ))}
          </div>
        </FormStep>

        <FormStep stepNumber={4} title="Enter Keywords" isVisible={step >= 4}>
          <div className="keyword-input">
            <input 
              type="text" 
              value={keywords} 
              onChange={(e) => setKeywords(e.target.value)} 
              placeholder="Enter keywords separated by commas" 
            />
            <button 
              className="generate-button" 
              onClick={handleSubmit}
              disabled={isLoading || !keywords.trim()}
            >
              {isLoading ? 'Generating...' : 'Generate Content'}
            </button>
          </div>
        </FormStep>
      </>
    );
  };

  const renderContent = () => {
    if (step < 5) return renderFormSteps();

    if (step === 5 || isLoading) {
      return <GeneratingAnimation />;
    }

    if (error) {
      return <div className="error-message">{error}</div>;
    }

    console.log('Rendering content, fullPosts:', fullPosts);

    return (
      <div className="card generated-content">
        <div className="info-row">
          <ScheduleCard schedule={schedule} />
          <SummariesCard summaries={postSummaries} />
        </div>
        <div className="tweets-grid">
          {fullPosts.map((post, index) => (
            <TweetCard key={index} post={post} />
          ))}
        </div>
      </div>
    );
  };

  return (
    <div className="App">
      <Navbar userName="User" />
      <div className="content">
        {step === 0 ? (
          <TypingAnimation userName="User" onBeg
[truncated — 131 more characters]
```

### frontend/src/setupTests.js

```javascript
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';

```

### frontend/src/App.test.js

```javascript
import { render, screen } from '@testing-library/react';
import App from './App';

test('renders learn react link', () => {
  render(<App />);
  const linkElement = screen.getByText(/learn react/i);
  expect(linkElement).toBeInTheDocument();
});

```

### frontend/src/reportWebVitals.js

```javascript
const reportWebVitals = onPerfEntry => {
  if (onPerfEntry && onPerfEntry instanceof Function) {
    import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
      getCLS(onPerfEntry);
      getFID(onPerfEntry);
      getFCP(onPerfEntry);
      getLCP(onPerfEntry);
      getTTFB(onPerfEntry);
    });
  }
};

export default reportWebVitals;

```

### frontend/src/index.css

```css
body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
    monospace;
}

```

### frontend/public/index.html

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  </body>
</html>

```

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