mirror of
https://github.com/PinkSheep27/SBU-Hackathon.git
synced 2026-10-11 00:30:26 -04:00
pasted all my ai file
This commit is contained in:
@@ -0,0 +1,294 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState } from "react";
|
||||
|
||||
import {
|
||||
FormData,
|
||||
PROFICIENCY_LEVELS
|
||||
} from './formTypes'; // Ensure path is correct
|
||||
|
||||
interface FormPageProps {
|
||||
onFormSubmit: (data: FormData) => void;
|
||||
}
|
||||
|
||||
const FormPage: React.FC<FormPageProps> = ({ onFormSubmit }) => {
|
||||
const [formData, setFormData] = useState<FormData>({
|
||||
Hackathon: '',
|
||||
mainTheme: '',
|
||||
tracksStack: [''],
|
||||
students: [{
|
||||
studentName: '',
|
||||
skills: [{ skillName: '', proficiency: PROFICIENCY_LEVELS[1] }]
|
||||
}],
|
||||
});
|
||||
|
||||
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
[event.target.name]: event.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
const handleTracksChange = (event: React.ChangeEvent<HTMLInputElement>, index: number) => {
|
||||
const newTracksStack = [...formData.tracksStack];
|
||||
newTracksStack[index] = event.target.value;
|
||||
|
||||
setFormData({
|
||||
...formData,
|
||||
tracksStack: newTracksStack,
|
||||
});
|
||||
};
|
||||
|
||||
const handleAddTracks = () => {
|
||||
setFormData({
|
||||
...formData,
|
||||
tracksStack: [...formData.tracksStack, ''],
|
||||
});
|
||||
};
|
||||
|
||||
const handleRemoveTracks = (index: number) => {
|
||||
const newTracksStack = formData.tracksStack.filter((_, i) => i !== index);
|
||||
setFormData({
|
||||
...formData,
|
||||
tracksStack: newTracksStack,
|
||||
});
|
||||
};
|
||||
|
||||
const handleStudentNameChange = (e: React.ChangeEvent<HTMLInputElement>, studentIndex: number) => {
|
||||
const newStudents = [...formData.students];
|
||||
newStudents[studentIndex].studentName = e.target.value;
|
||||
setFormData({ ...formData, students: newStudents });
|
||||
};
|
||||
|
||||
const handleAddStudent = () => {
|
||||
setFormData({
|
||||
...formData,
|
||||
students: [...formData.students, { studentName: '', skills: [{ skillName: '', proficiency: PROFICIENCY_LEVELS[1] }] }]
|
||||
});
|
||||
};
|
||||
|
||||
const handleRemoveStudent = (studentIndex: number) => {
|
||||
const newStudents = formData.students.filter((_, i) => i !== studentIndex);
|
||||
setFormData({ ...formData, students: newStudents });
|
||||
};
|
||||
|
||||
const handleSkillChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>,
|
||||
studentIndex: number,
|
||||
skillIndex: number
|
||||
) => {
|
||||
const newStudents = [...formData.students];
|
||||
const targetName = e.target.name;
|
||||
const targetValue = e.target.value;
|
||||
|
||||
if (targetName === 'skillName') {
|
||||
newStudents[studentIndex].skills[skillIndex].skillName = targetValue;
|
||||
} else if (targetName === 'proficiency') {
|
||||
newStudents[studentIndex].skills[skillIndex].proficiency = targetValue;
|
||||
}
|
||||
|
||||
setFormData({ ...formData, students: newStudents });
|
||||
};
|
||||
|
||||
const handleAddSkill = (studentIndex: number) => {
|
||||
const newStudents = [...formData.students];
|
||||
newStudents[studentIndex].skills.push({ skillName: '', proficiency: PROFICIENCY_LEVELS[1] });
|
||||
setFormData({ ...formData, students: newStudents });
|
||||
};
|
||||
|
||||
const handleRemoveSkill = (studentIndex: number, skillIndex: number) => {
|
||||
const newStudents = [...formData.students];
|
||||
newStudents[studentIndex].skills = newStudents[studentIndex].skills.filter((_, i) => i !== skillIndex);
|
||||
setFormData({ ...formData, students: newStudents });
|
||||
};
|
||||
|
||||
const handleSubmit = (event: React.FormEvent) => {
|
||||
event.preventDefault();
|
||||
onFormSubmit(formData); // Call the prop function with formData
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex items-start justify-center p-4 sm:p-6 font-inter">
|
||||
<div className="w-full max-w-2xl bg-white p-8 shadow-2xl rounded-xl mt-8">
|
||||
<h1 className="text-3xl font-extrabold text-center text-gray-800 mb-8 tracking-tight">
|
||||
Generate Project Idea
|
||||
</h1>
|
||||
|
||||
<form className="space-y-8" onSubmit={handleSubmit}>
|
||||
<div className="space-y-6 border-b pb-6 border-gray-200">
|
||||
<h2 className="text-xl font-bold text-gray-700">Project Context</h2>
|
||||
<div>
|
||||
<label htmlFor="hackathonName" className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Hackathon Name
|
||||
</label>
|
||||
<input
|
||||
id="hackathonName"
|
||||
required
|
||||
type="text"
|
||||
placeholder="e.g., Google Code Jam"
|
||||
name="Hackathon"
|
||||
value={formData.Hackathon}
|
||||
onChange={handleChange}
|
||||
className="mt-1 block w-full px-4 py-2 border border-gray-300 rounded-lg shadow-sm focus:ring-blue-500 focus:border-blue-500 transition duration-150"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="mainTheme" className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Main Theme
|
||||
</label>
|
||||
<input
|
||||
id="mainTheme"
|
||||
required
|
||||
type="text"
|
||||
placeholder="e.g., Sustainability or AI Ethics"
|
||||
name="mainTheme"
|
||||
value={formData.mainTheme}
|
||||
onChange={handleChange}
|
||||
className="mt-1 block w-full px-4 py-2 border border-gray-300 rounded-lg shadow-sm focus:ring-blue-500 focus:border-blue-500 transition duration-150"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4 border-b pb-6 border-gray-200">
|
||||
<label className="block text-xl font-bold text-gray-700">
|
||||
Tracks Stack
|
||||
</label>
|
||||
<div className="space-y-3">
|
||||
{formData.tracksStack.map((tracks, index) => (
|
||||
<div key={index} className="flex gap-2 items-center">
|
||||
<input
|
||||
type="text"
|
||||
required
|
||||
placeholder={`Tracks #${index + 1}`}
|
||||
name={`tracksStack-${index}`}
|
||||
value={tracks}
|
||||
onChange={(e) => handleTracksChange(e, index)}
|
||||
className="flex-grow px-4 py-2 border border-gray-300 rounded-lg shadow-sm focus:ring-green-500 focus:border-green-500 transition duration-150"
|
||||
/>
|
||||
{formData.tracksStack.length > 1 && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemoveTracks(index)}
|
||||
className="text-sm px-3 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition duration-150 flex-shrink-0"
|
||||
aria-label={`Remove tracks ${index + 1}`}
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleAddTracks}
|
||||
className="flex items-center justify-center gap-1 py-2 px-4 border border-transparent rounded-lg shadow-sm text-sm font-medium text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 transition duration-150"
|
||||
>
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path></svg>
|
||||
Add Tracks
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-xl font-bold text-gray-700">Team Members and Skills</h2>
|
||||
|
||||
<div className="space-y-6">
|
||||
{formData.students.map((student, studentIndex) => (
|
||||
<div key={studentIndex} className="p-4 border-2 border-indigo-200 rounded-lg bg-indigo-50 shadow-inner space-y-4">
|
||||
|
||||
<div className="flex justify-between items-center pb-2 border-b border-indigo-300">
|
||||
<h4 className="text-lg font-semibold text-indigo-700">Student #{studentIndex + 1}</h4>
|
||||
{formData.students.length > 1 && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemoveStudent(studentIndex)}
|
||||
className="text-xs px-2 py-1 bg-red-400 text-white rounded-md hover:bg-red-500 transition duration-150"
|
||||
>
|
||||
Remove Student
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-indigo-700 mb-1">Student Name</label>
|
||||
<input
|
||||
type="text"
|
||||
required
|
||||
placeholder="Student Name"
|
||||
value={student.studentName}
|
||||
onChange={(e) => handleStudentNameChange(e, studentIndex)}
|
||||
className="w-full px-4 py-2 border border-indigo-300 rounded-lg shadow-sm focus:ring-indigo-500 focus:border-indigo-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3 p-3 bg-white rounded-lg border border-indigo-100">
|
||||
<h5 className="text-md font-medium text-indigo-700">Skills</h5>
|
||||
{student.skills.map((skill, skillIndex) => (
|
||||
<div key={skillIndex} className="flex gap-2 items-center">
|
||||
<input
|
||||
type="text"
|
||||
required
|
||||
name="skillName"
|
||||
placeholder="Skill (e.g., React, Python)"
|
||||
value={skill.skillName}
|
||||
onChange={(e) => handleSkillChange(e, studentIndex, skillIndex)}
|
||||
className="flex-grow px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-purple-500 focus:border-purple-500"
|
||||
/>
|
||||
<select
|
||||
name="proficiency"
|
||||
value={skill.proficiency}
|
||||
onChange={(e) => handleSkillChange(e, studentIndex, skillIndex)}
|
||||
className="px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-purple-500 focus:border-purple-500 w-1/3"
|
||||
>
|
||||
{PROFICIENCY_LEVELS.map(level => (
|
||||
<option key={level} value={level}>{level}</option>
|
||||
))}
|
||||
</select>
|
||||
{student.skills.length > 1 && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemoveSkill(studentIndex, skillIndex)}
|
||||
className="text-xs px-2 py-1 bg-red-500 text-white rounded-md hover:bg-red-600 transition duration-150 flex-shrink-0"
|
||||
>
|
||||
X
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleAddSkill(studentIndex)}
|
||||
className="mt-2 text-xs flex items-center gap-1 py-1 px-3 border border-transparent rounded-lg text-white bg-purple-600 hover:bg-purple-700 transition duration-150"
|
||||
>
|
||||
+ Add Skill
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleAddStudent}
|
||||
className="mt-4 w-full flex items-center justify-center gap-2 py-2 px-4 border border-transparent rounded-lg shadow-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transition duration-150"
|
||||
>
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z"></path></svg>
|
||||
Add Team Member
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full flex justify-center py-3 px-4 border border-transparent rounded-lg shadow-lg text-lg font-semibold text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition duration-150 mt-6"
|
||||
>
|
||||
Generate Idea
|
||||
</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FormPage;
|
||||
@@ -0,0 +1,21 @@
|
||||
// Define Skill structure
|
||||
export interface Skill {
|
||||
skillName: string;
|
||||
proficiency: string;
|
||||
}
|
||||
|
||||
// Define Student structure
|
||||
export interface Student {
|
||||
studentName: string;
|
||||
skills: Skill[];
|
||||
}
|
||||
|
||||
// Updated main form interface
|
||||
export interface FormData {
|
||||
Hackathon: string;
|
||||
mainTheme: string;
|
||||
tracksStack: string[];
|
||||
students: Student[]; // Array for dynamic students
|
||||
}
|
||||
|
||||
export const PROFICIENCY_LEVELS = ['Beginner', 'Intermediate', 'Expert', 'Master'];
|
||||
@@ -0,0 +1,71 @@
|
||||
import { GoogleGenerativeAI } from "@google/generative-ai";
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
// Initialize the Gemini Client with your API Key
|
||||
// IMPORTANT: The GEMINI_API_KEY should be an environment variable.
|
||||
const apiKey = process.env.GEMINI_API_KEY;
|
||||
|
||||
if(!apiKey){
|
||||
throw new Error("api key not found");
|
||||
}
|
||||
const ai = new GoogleGenerativeAI(apiKey);
|
||||
|
||||
|
||||
|
||||
// Define the handler function for the API route
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
) {
|
||||
// Only allow POST requests
|
||||
if (req.method !== 'POST') {
|
||||
return res.status(405).json({ error: 'Method Not Allowed' });
|
||||
}
|
||||
|
||||
// Destructure the data sent from the frontend
|
||||
let { prompt, settings } = req.body;
|
||||
|
||||
// Basic validation
|
||||
if (!prompt || typeof prompt !== 'string') {
|
||||
return res.status(400).json({ error: 'Prompt is required' });
|
||||
}
|
||||
|
||||
// Modify the prompt to instruct the AI to generate only the game, concept, and track
|
||||
prompt = `You're going to fill out a card base off the document.
|
||||
you will Generate the project short title.
|
||||
you will list out the track you used ${prompt}`;
|
||||
|
||||
try {
|
||||
// 1. Call the Gemini API using the correct method signature
|
||||
|
||||
const model = settings?.model || 'gemini-2.5-flash-lite';
|
||||
const generativeModel = ai.getGenerativeModel({ model });
|
||||
|
||||
// 1. Define the generation configuration object
|
||||
const generationConfig = {
|
||||
temperature: settings?.temperature || 0.9,
|
||||
// The SDK property name for system instructions is 'systemInstruction'
|
||||
systemInstruction: settings?.sysTemInstructions,
|
||||
};
|
||||
|
||||
// 2. Call generateContent with the single GenerateContentRequest object.
|
||||
const result = await generativeModel.generateContent({
|
||||
// contents must be an array of Content objects
|
||||
contents: [{
|
||||
role: "user",
|
||||
parts: [{ text: prompt }]
|
||||
}],
|
||||
|
||||
// The fixed property name that resolves the TypeScript error
|
||||
generationConfig: generationConfig,
|
||||
});
|
||||
|
||||
// 2. Extract the response text
|
||||
// The result object now contains a property 'text' at the top level for simple text generation
|
||||
const geminiResponseText = result.response.text(); res.status(200).json({ response: geminiResponseText });
|
||||
|
||||
} catch (error) {
|
||||
console.error("Gemini API Error:", error);
|
||||
res.status(500).json({ error: 'Failed to generate idea from AI.' });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
// File: Home.tsx (Renamed from form.tsx for export consistency)
|
||||
|
||||
"use client";
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import FormPage from './FormPage';
|
||||
import { ChatSetting, CardData } from "../exportType/types";
|
||||
import { FormData, PROFICIENCY_LEVELS } from './formTypes';
|
||||
import SwipeCards from '../cards/page';
|
||||
|
||||
export default function Home() {
|
||||
const [showForm, setShowForm] = useState(true);
|
||||
const [formData, setFormData] = useState<FormData>(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const storedFormData = localStorage.getItem('formData');
|
||||
if (storedFormData) {
|
||||
const parsedData: FormData = JSON.parse(storedFormData);
|
||||
return parsedData;
|
||||
}
|
||||
}
|
||||
return {
|
||||
Hackathon: '',
|
||||
mainTheme: '',
|
||||
tracksStack: [''],
|
||||
students: [{
|
||||
studentName: '',
|
||||
skills: [{ skillName: '', proficiency: PROFICIENCY_LEVELS[1] }]
|
||||
}],
|
||||
};
|
||||
});
|
||||
|
||||
const [ideaResponse, setIdeaResponse] = useState<string>("");
|
||||
const [generatedCards, setGeneratedCards] = useState<CardData[]>([]); // New state for parsed cards
|
||||
|
||||
const [settings] = useState<ChatSetting>({
|
||||
temperature: 1,
|
||||
model: "gemini-2.5-flash-lite",
|
||||
sysTemInstructions: `You are an elite hackathon idea generator. Your task is to choose at lease two track and generate five different creative, feasible, and relevant project idea.
|
||||
The output MUST be in Markdown format and strictly follow this structure:
|
||||
### Project Idea Title
|
||||
**Track** [print out the track you used]
|
||||
**Concept:** [A concise, single paragraph description of the product and its primary function.]`,
|
||||
});
|
||||
|
||||
const parseAiResponse = (markdownString: string): CardData[] => {
|
||||
console.log("Raw AI Response:", markdownString);
|
||||
const ideas: CardData[] = [];
|
||||
const ideaBlocks = markdownString.split("### ").filter(block => block.trim() !== "");
|
||||
|
||||
ideaBlocks.forEach((block, index) => {
|
||||
// Match title: anything from the start of the block until the first newline or the start of "**Track**"
|
||||
const titleMatch = block.match(/^(.*?)(?=\n\*\*Track\*\*|$)/s);
|
||||
// Match track: "**Track**" followed by optional whitespace and then capture everything until the next "**Concept**" or end of block/line
|
||||
const trackMatch = block.match(/\*\*Track\*\*\s*:\s*(.*?)(?=\n\*\*Concept\*\*|$)/s); // Added ':' and made whitespace more flexible
|
||||
// Match concept: "**Concept:**" followed by optional whitespace and then capture everything until the end of the block
|
||||
const conceptMatch = block.match(/\*\*Concept:\*\*\s*(.*)/s);
|
||||
|
||||
const title = titleMatch ? titleMatch[1].trim() : `Idea ${index + 1}`;
|
||||
const summary = conceptMatch ? conceptMatch[1].trim() : "No concept provided.";
|
||||
const tracksRaw = trackMatch ? trackMatch[1].trim() : "";
|
||||
console.log("tracksRaw:", tracksRaw); // Added console.log for debugging
|
||||
const tracks = tracksRaw.split(',').map(track => track.trim()).filter(track => track !== '');
|
||||
|
||||
ideas.push({
|
||||
id: index + 1,
|
||||
title,
|
||||
tracks,
|
||||
summary,
|
||||
});
|
||||
});
|
||||
console.log("Parsed Cards:", ideas);
|
||||
return ideas;
|
||||
};
|
||||
|
||||
const handleFormSubmitFromFormPage = (submittedData: FormData) => {
|
||||
setFormData(submittedData);
|
||||
setShowForm(false);
|
||||
generateIdeas(submittedData);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react-hooks/preserve-manual-memoization
|
||||
const generateIdeas = useCallback(async (dataToProcess: FormData) => {
|
||||
const submissionData: FormData = {
|
||||
...dataToProcess,
|
||||
tracksStack: dataToProcess.tracksStack.filter(tracks => tracks.trim() !== ''),
|
||||
students: dataToProcess.students
|
||||
.map(student => ({
|
||||
...student,
|
||||
skills: student.skills.filter(skill => skill.skillName.trim() !== ''),
|
||||
}))
|
||||
.filter(student => student.studentName.trim() !== '' || student.skills.length > 0),
|
||||
};
|
||||
|
||||
const prompt = `
|
||||
Hackathon: ${submissionData.Hackathon}
|
||||
Main Theme: ${submissionData.mainTheme}
|
||||
Required Tracks Stack: ${submissionData.tracksStack.join(', ')}
|
||||
Team Members and Skills: ${submissionData.students.map(s =>
|
||||
`${s.studentName || 'Unamed Member'} (Skills: ${s.skills.map(sk => `${sk.skillName} - ${sk.proficiency}`).join(', ')})`
|
||||
).join('; ')}
|
||||
|
||||
Using the SYSTEM INSTRUCTIONS, generate 5 project ideas based on the data above.`;
|
||||
|
||||
try {
|
||||
setIdeaResponse("Generating ideas... Please wait.");
|
||||
setGeneratedCards([]); // Clear previous cards
|
||||
const response = await fetch("/api/generate-idea", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
prompt: prompt,
|
||||
settings: settings,
|
||||
}),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.error || !response.ok) {
|
||||
console.error("AI Error:", data.error);
|
||||
setIdeaResponse(`Error: Failed to generate idea. ${data.error || response.statusText}`);
|
||||
return;
|
||||
}
|
||||
|
||||
setIdeaResponse(data.response);
|
||||
const parsedCards = parseAiResponse(data.response);
|
||||
setGeneratedCards(parsedCards);
|
||||
|
||||
} catch (error) {
|
||||
console.error("Request Failed:", error);
|
||||
let errorMessage = "An unknown error occurred.";
|
||||
if (error instanceof Error) {
|
||||
errorMessage = error.message;
|
||||
} else if (typeof error === 'string') {
|
||||
errorMessage = error;
|
||||
}
|
||||
setIdeaResponse(`A network error occurred. Please try again. Details: ${errorMessage}`);
|
||||
}
|
||||
}, [setIdeaResponse, setGeneratedCards, settings, parseAiResponse]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const storedFormData = localStorage.getItem('formData');
|
||||
if (storedFormData) {
|
||||
const parsedData: FormData = JSON.parse(storedFormData);
|
||||
setTimeout(() => setShowForm(false), 0);
|
||||
generateIdeas(parsedData);
|
||||
localStorage.removeItem('formData');
|
||||
}
|
||||
}
|
||||
}, [generateIdeas]);
|
||||
|
||||
return (
|
||||
<main className="min-h-screen bg-gray-50 flex items-start justify-center p-4 sm:p-6 font-inter">
|
||||
<div className="w-full max-w-2xl bg-white p-8 shadow-2xl rounded-xl mt-8">
|
||||
{showForm ? (
|
||||
<FormPage onFormSubmit={handleFormSubmitFromFormPage} />
|
||||
) : (
|
||||
<>
|
||||
{generatedCards.length > 0 ? (
|
||||
<SwipeCards initialCards={generatedCards} />
|
||||
) : (
|
||||
<>
|
||||
<h1 className="text-3xl font-extrabold text-center text-gray-800 mb-8 tracking-tight">
|
||||
Generated Project Idea 💡
|
||||
</h1>
|
||||
<div className="mt-8 p-4 bg-yellow-50 rounded-lg border border-yellow-200">
|
||||
<h2 className="text-xl font-bold text-gray-700 mb-2">Generated Ideas:</h2>
|
||||
<div
|
||||
className="prose max-w-none"
|
||||
dangerouslySetInnerHTML={{ __html: ideaResponse.replace(/\n/g, '<br/>') }}
|
||||
/>
|
||||
{ideaResponse === "" && <p className="text-gray-500">Submit the form to generate project ideas.</p>}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<button
|
||||
onClick={() => setShowForm(true)}
|
||||
className="mt-6 w-full flex justify-center py-3 px-4 border border-transparent rounded-lg shadow-lg text-lg font-semibold text-white bg-blue-600 hover:bg-blue-700 transition duration-150"
|
||||
>
|
||||
Go Back to Form
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user