formatting

This commit is contained in:
PawelC4
2025-11-09 00:07:25 -05:00
parent 3686ae72da
commit 972df9b70b
15 changed files with 1283 additions and 1107 deletions
+262 -262
View File
@@ -1,294 +1,294 @@
"use client";
// "use client";
import React, { useState } from "react";
// import React, { useState } from "react";
import {
FormData,
PROFICIENCY_LEVELS
} from './formTypes'; // Ensure path is correct
// import {
// FormData,
// PROFICIENCY_LEVELS
// } from './formTypes'; // Ensure path is correct
interface FormPageProps {
onFormSubmit: (data: FormData) => void;
}
// 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 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 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;
// const handleTracksChange = (event: React.ChangeEvent<HTMLInputElement>, index: number) => {
// const newTracksStack = [...formData.tracksStack];
// newTracksStack[index] = event.target.value;
setFormData({
...formData,
tracksStack: newTracksStack,
});
};
// setFormData({
// ...formData,
// tracksStack: newTracksStack,
// });
// };
const handleAddTracks = () => {
setFormData({
...formData,
tracksStack: [...formData.tracksStack, ''],
});
};
// const handleAddTracks = () => {
// setFormData({
// ...formData,
// tracksStack: [...formData.tracksStack, ''],
// });
// };
const handleRemoveTracks = (index: number) => {
const newTracksStack = formData.tracksStack.filter((_, i) => i !== index);
setFormData({
...formData,
tracksStack: newTracksStack,
});
};
// 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 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 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 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;
// 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;
}
// if (targetName === 'skillName') {
// newStudents[studentIndex].skills[skillIndex].skillName = targetValue;
// } else if (targetName === 'proficiency') {
// newStudents[studentIndex].skills[skillIndex].proficiency = targetValue;
// }
setFormData({ ...formData, students: newStudents });
};
// 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 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 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
};
// 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>
// 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>
// <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>
// <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>
// <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>
// <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-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="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 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>
// <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>
// <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="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>
// <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>
);
};
// </div>
// </div>
// );
// };
export default FormPage;
// export default FormPage;
+18 -18
View File
@@ -1,21 +1,21 @@
// Define Skill structure
export interface Skill {
skillName: string;
proficiency: string;
}
// // Define Skill structure
// export interface Skill {
// skillName: string;
// proficiency: string;
// }
// Define Student structure
export interface Student {
studentName: string;
skills: Skill[];
}
// // 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
}
// // 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'];
// export const PROFICIENCY_LEVELS = ['Beginner', 'Intermediate', 'Expert', 'Master'];
+56 -56
View File
@@ -1,71 +1,71 @@
import { GoogleGenerativeAI } from "@google/generative-ai";
import type { NextApiRequest, NextApiResponse } from 'next';
// 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;
// // 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);
// 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' });
}
// // 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;
// // 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' });
}
// // 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}`;
// // 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
// 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 });
// 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,
};
// // 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 }]
}],
// // 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,
});
// // 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 });
// // 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.' });
}
}
// } catch (error) {
// console.error("Gemini API Error:", error);
// res.status(500).json({ error: 'Failed to generate idea from AI.' });
// }
// }
+160 -176
View File
@@ -1,188 +1,172 @@
// 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 } from "../exportType/types";
// import { FormData, PROFICIENCY_LEVELS } from './formTypes';
"use client";
import React, { useState, useEffect, useCallback } from "react";
import FormPage from './FormPage';
import { ChatSetting, CardData } from "@/app/exportType/types";
import { FormData, PROFICIENCY_LEVELS } from './formTypes';
import SwipeCards from '../cards/page';
// // --- Helper function to get default form data ---
// const getDefaultFormData = (): FormData => ({
// Hackathon: '',
// mainTheme: '',
// tracksStack: [''],
// students: [{
// studentName: '',
// skills: [{ skillName: '', proficiency: PROFICIENCY_LEVELS[1] }]
// }],
// });
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] }]
}],
};
});
// // --- New Initializer Function ---
// const getInitialState = () => {
// if (typeof window === 'undefined') {
// return {
// data: getDefaultFormData(),
// showForm: true,
// runEffect: false
// };
// }
// const storedFormData = localStorage.getItem('formData');
// if (storedFormData) {
// localStorage.removeItem('formData');
// return {
// data: JSON.parse(storedFormData) as FormData,
// showForm: false,
// runEffect: true,
// };
// }
// return {
// data: getDefaultFormData(),
// showForm: true,
// runEffect: false
// };
// };
const [ideaResponse, setIdeaResponse] = useState<string>("");
const [generatedCards, setGeneratedCards] = useState<CardData[]>([]); // New state for parsed cards
// // FIX 1 (Memoization): Moved settings OUTSIDE the component.
// // This makes it a true constant.
// const AI_SETTINGS: ChatSetting = {
// temperature: 1,
// model: "gemini-2.5-pro",
// sysTemInstructions: `You are an elite hackathon idea generator. Your task is to generate five different creative, feasible, and relevant project idea.
// The output MUST be in Markdown format and strictly follow this structure:
// ### Project Idea Title
// **Relevance to Theme:** [Explain briefly]
// **Core Technology:** [List 3-5 primary technologies]
// **Team Fit:** [How the team's skills apply]
// **Concept:** [A concise, single paragraph description of the product and its primary function.]
// **Bonus Feature:** [A single, ambitious feature to impress judges.]`,
// };
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() !== "");
// export default function Home() {
// const [initialState] = useState(getInitialState);
// const [showForm, setShowForm] = useState(initialState.showForm);
// const [formData, setFormData] = useState<FormData>(initialState.data);
// const [ideaResponse, setIdeaResponse] = useState<string>("");
// // The 'settings' state [useState] is now removed from here.
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 handleFormSubmitFromFormPage = (submittedData: FormData) => {
// setFormData(submittedData);
// setShowForm(false);
// generateIdeas(submittedData);
// };
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 !== '');
// // Wrapped generateIdeas in useCallback
// const generateIdeas = useCallback(async (dataToProcess: FormData) => {
// // 1. Data Cleaning
// 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),
// };
ideas.push({
id: index + 1,
title,
tracks,
summary,
});
});
console.log("Parsed Cards:", ideas);
return ideas;
};
// // 2. Prompt Construction
// 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.`;
const handleFormSubmitFromFormPage = (submittedData: FormData) => {
setFormData(submittedData);
setShowForm(false);
generateIdeas(submittedData);
};
// // 3. API Call
// try {
// setIdeaResponse("Generating ideas... Please wait.");
// const response = await fetch("/api/generate-idea", {
// method: "POST",
// headers: { "Content-Type": "application/json" },
// body: JSON.stringify({
// prompt: prompt,
// settings: AI_SETTINGS, // Use the constant
// }),
// });
// 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 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);
// // FIX 2 (Unexpected any): Changed 'any' to 'unknown' and added a type check.
// } catch (error: unknown) {
// console.error("Request Failed:", error);
// let errorMessage = "A network error occurred. Please try again.";
// if (error instanceof Error) {
// errorMessage += ` Details: ${error.message}`;
// }
// setIdeaResponse(errorMessage);
// }
// // FIX 3 (Linter): Disable the faulty memoization rule.
// // The empty array is correct because setIdeaResponse is a stable state setter.
// // eslint-disable-next-line react-hooks/preserve-manual-memoization
// }, []);
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('; ')}
// // Replaced useEffect
// useEffect(() => {
// if (initialState.runEffect) {
// generateIdeas(initialState.data);
// }
// }, [initialState, generateIdeas]);
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>
);
}
// // --- JSX Rendering ---
// 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} />
// ) : (
// <>
// <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>
// );
// }