added images, changed css for homepage, dashboard and got rid of templates for cards and made them take actual local variables

This commit is contained in:
Ivan Rudik
2025-11-09 12:26:45 +00:00
parent 8f08b053f2
commit cd28b6ca93
12 changed files with 112 additions and 105 deletions
+44 -14
View File
@@ -1,20 +1,27 @@
"use client";
import React, { useState, useEffect } from "react";
import { getStoredProjects, StoredProject } from "../utils/storage";
import { getStoredProjects, StoredProject,setProjectForEditing } from "../utils/storage";
import Link from 'next/link';
import { useRouter } from "next/navigation";
export default function Dashboard() {
const [query, setQuery] = useState("");
const [sideBarOpen, setSidebarOpen] = useState(true);
const [sortOrder, setSortOrder] = useState("newest");
const [projects, setProjects] = useState<StoredProject[]>([]);
const router = useRouter();
useEffect(() => {
const storedProjects = getStoredProjects();
setProjects(storedProjects);
}, []);
const handleRerun = (project: StoredProject) => {
setProjectForEditing(project);
router.push('/formfile');
};
const sortedProjects = [...projects].sort((a, b) => {
const dateA = new Date(a.id).getTime();
const dateB = new Date(b.id).getTime();
@@ -39,15 +46,17 @@ export default function Dashboard() {
};
return (
<div className="flex min-h-screen bg-zinc-50 dark:bg-black font-sans transition-all duration-300">
<div className="flex min-h-screen bg-zinc-50 font-sans transition-all duration-300"
style={{ backgroundColor: '#FFEAE1' }}>
<aside
className={`${sideBarOpen ? "w-64" : "w-16"
} bg-white dark:bg-zinc-900 border-r border-zinc-200 dark:border-zinc-800 transition-all duration-300 flex flex-col`}
} transition-all duration-300 flex flex-col `}
style={{ backgroundColor: 'rgba(153, 164, 138, 0.9)' }}
>
<div className="flex items-center justify-between p-4 border-b border-zinc-200 dark:border-zinc-800">
<div className="flex items-center justify-between p-4">
{sideBarOpen && (
<span className="text-lg font-semibold text-zinc-800 dark:text-zinc-100">
More
SparkBot
</span>
)}
<button
@@ -85,7 +94,7 @@ export default function Dashboard() {
<div className="flex-1 flex flex-col min-w-0">
<header
className="sticky top-0 z-10 backdrop-blur-sm border-b"
className="sticky top-0 z-10 backdrop-blur-sm"
style={{ backgroundColor: 'rgba(153, 164, 138, 0.9)', borderColor: '#8A947E' }} // Top bar background (semi-transparent) and border
>
<div className="max-w-7xl mx-auto px-6 py-4 flex items-center justify-between gap-4">
@@ -134,16 +143,37 @@ export default function Dashboard() {
<section>
<ul className="space-y-3">
{filtered.map((project, index) => (
{filtered.map((project) => (
<li
key={index}
className="flex justify-between p-4 border rounded-md"
style={{ backgroundColor: '#B8C0AD', color: '#222', borderColor: '#A9B29E' }} // Project boxes
key={project.id}
className="flex flex-col sm:flex-row justify-between items-start sm:items-center p-4 border rounded-md"
style={{ backgroundColor: '#B8C0AD', color: '#222', borderColor: '#A9B29E' }}
>
<span>{project.name}</span>
<span className="text-gray-700 text-sm">
Created: {project.createdAt}
</span>
<div className="flex-1 mb-2 sm:mb-0">
<Link
href={`/chatbot?projectId=${encodeURIComponent(project.id)}&projectTitle=${encodeURIComponent(project.title)}`}
className="text-lg font-semibold text-[#173C46] hover:underline"
>
{project.title}
</Link>
<span className="block text-gray-700 text-sm mt-1">
Created: {new Date(project.id).toLocaleString()}
</span>
</div>
<div className="flex gap-2">
<button
onClick={() => handleRerun(project)}
className="px-3 py-1 text-sm font-medium rounded-md shadow hover:opacity-90"
style={{
color: 'white',
backgroundColor: '#EA627F'
}}
>
Re-run
</button>
</div>
</li>
))}
{filtered.length === 0 && (
+10 -10
View File
@@ -26,9 +26,13 @@ const parseAiResponse = (markdownString: string): CardData[] => {
ideaBlocks.forEach((block, index) => {
const titleMatch = block.match(/^(.*?)\n/);
const conceptMatch = block.match(/\*\*Concept:\*\*\s*(.*)/s);
const tracksMatch = block.matchAll(/\*\*(?!Concept|Relevance|Core|Team|Bonus)(.*?):\*\*/g);
const tracks = [...tracksMatch].map(match => match[1].trim());
const conceptMatch = block.match(/\*\*Concept:\*\*\s*([\s\S]*?)(?=\n\*\*|$)/s); // Improved summary regex
// --- THIS IS THE FIX ---
// It now looks for "**Core Technology:**" and splits the technologies by comma
const techMatch = block.match(/\*\*Core Technology:\*\*\s*(.*)/);
const tracks = techMatch ? techMatch[1].split(',').map(t => t.trim()) : ["General"];
// -----------------------
ideas.push({
id: index + 1,
@@ -60,13 +64,9 @@ export default function CardsPage() {
setCards(parsedCards.reverse());
setInitialCards(parsedCards.reverse());
} else {
console.warn("No AI response found. Using hardcoded cards as fallback.");
const hardcodedCards: CardData[] = [
{ id: 1, title: "AI Project Idea 1", summary: "A cool project...", tracks: ["AI", "Web"] },
{ id: 2, title: "AI Project Idea 2", summary: "Another cool project...", tracks: ["Mobile", "Game"] },
];
setCards(hardcodedCards.reverse());
setInitialCards(hardcodedCards.reverse());
// If no AI response is found, we do nothing.
// The 'cards' state will remain empty, showing the "No more cards!" UI.
console.warn("No 'temp_aiResponse' found in localStorage. Displaying empty deck.");
}
setIsLoading(false);
}, []);
+1 -1
View File
@@ -41,7 +41,7 @@ function ChatbotContent() {
const historyRef = useRef<ChatHistory>(history);
const [settings] = useState<ChatSetting>({
temperature: 1,
model: "gemini-2.5-pro",
model: "gemini-2.5-flash-lite",
systemInstructions: "you are a ai helper. Your goal is to expand the project idea and interact with the user on any problem or question they have with the project. You are not allow to code the project for them and stray away from your job"
});
+23 -18
View File
@@ -1,8 +1,10 @@
"use client";
import React, { useState, useEffect, useCallback } from "react";
import { useRouter } from "next/navigation";
import FormPage from './FormPage';
import { ChatSetting } from "../exportType/types";
import { FormData, PROFICIENCY_LEVELS } from './formTypes';
import { saveProject } from "../utils/storage";
// --- Helper function to get default form data ---
const getDefaultFormData = (): FormData => ({
@@ -44,21 +46,21 @@ const getInitialState = () => {
// 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.]`,
model: "gemini-2.5-flash-lite",
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.]`,
};
// ------------------------------------------------
export default function Home() {
const router = useRouter();
const [initialState] = useState(getInitialState);
const [showForm, setShowForm] = useState(initialState.showForm);
const [formData, setFormData] = useState<FormData>(initialState.data);
@@ -75,6 +77,10 @@ export default function Home() {
// Wrapped generateIdeas in useCallback
const generateIdeas = useCallback(async (dataToProcess: FormData) => {
// 1. Data Cleaning
const newProject = saveProject(dataToProcess);
const projectId = newProject.id;
const projectTitle = newProject.title;
const submissionData: FormData = {
...dataToProcess,
tracksStack: dataToProcess.tracksStack.filter(tracks => tracks.trim() !== ''),
@@ -94,8 +100,7 @@ export default function Home() {
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.`;
`;
// 3. API Call
try {
@@ -116,8 +121,11 @@ export default function Home() {
setIdeaResponse(`Error: Failed to generate idea. ${data.error || response.statusText}`);
return;
}
setIdeaResponse(data.response);
localStorage.setItem('temp_aiResponse', data.response);
// Redirect to the cards page with the new project ID and title
router.push(`/cards?projectId=${encodeURIComponent(projectId)}&projectTitle=${encodeURIComponent(projectTitle)}`);
// FIX 2 (Unexpected any): Changed 'any' to 'unknown' and added a type check.
} catch (error: unknown) {
console.error("Request Failed:", error);
@@ -127,10 +135,7 @@ export default function Home() {
}
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
}, []);
}, [router]);
// Replaced useEffect
useEffect(() => {
-35
View File
@@ -1,32 +1,3 @@
<<<<<<< HEAD
=======
/* @import "tailwindcss";
:root {
--background: #ffffff;
--foreground: #171717;
}
:root {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
}
@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
}
body {
background: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
} */
/* 1. Import BOTH fonts in one go */
@import url('https://fonts.googleapis.com/css2?family=Concert+One&family=Pixelify+Sans:wght@400;700&display=swap');
@import "tailwindcss";
@@ -41,15 +12,9 @@ body {
/* 3. Assign fonts to Tailwind's theme */
<<<<<<< HEAD
/* Tell Tailwind to use the variables already defined in app/layout.tsx */
--font-sans: var(--font-sans);
--font-header: var(--font-header);
=======
/* --font-sans is for regular text (Concert One) */
--font-sans: var(--font-concert-one);
--font-header: var(--font-pixelify);
>>>>>>> 44b5a4dc49ea757e9790ba8309edc6b37d764105
--font-mono: var(--font-geist-mono);
}
+32 -25
View File
@@ -1,6 +1,7 @@
import React from "react";
// import Image from "next/image"; // Removed Next.js import
// import Link from "next/link"; // Removed Next.js import
import Link from "next/link"; // Removed Next.js import
export default function Home() {
return (
@@ -21,9 +22,6 @@ export default function Home() {
<Link href="/Dashboard" className="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
Dashboard
</Link>
<Link href="/cards" className="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
Cards
</Link>
</div>
</header>
@@ -35,7 +33,7 @@ export default function Home() {
style={{ color: "#173C46" }}
className="max-w-xs text-5xl font-semibold leading-tight tracking-tight text-black"
>
Welcome! This is HackSpark
Welcome! This is SparkBot
</h1>
<p
style={{ color: "#173C46" }}
@@ -44,12 +42,11 @@ export default function Home() {
Got a hackathon coming up? Tell us your track, your team’s skills, what you’re passionate about, and more — and we’ll provide you with the perfect project idea!{" "}
</p>
<Link
href="/Dashboard"
<button
style={{ backgroundColor: "#173C46", color: "white" }}
className="inline-block px-5 py-2.5 text-lg rounded-md shadow-md font-semibold hover:opacity-90 transition-opacity"
>
<Link href="/dashboard">Get Started</Link>
<Link href="/Dashboard">Get Started</Link>
</button>
</div>
@@ -58,7 +55,12 @@ export default function Home() {
className="w-full max-w-[450px] h-[350px] rounded-xl shadow-md flex items-center justify-center text-white"
style={{ backgroundColor: "#173C46" }}
>
Image Placeholder
<img
src="s1.jpeg"
alt=" Img"
width={350}
height={350}
/>
</div>
</div>
</div>
@@ -81,11 +83,8 @@ export default function Home() {
style={{ backgroundColor: "#B8C0AD" }}
>
<h2 className="text-3xl font-semibold text-black mb-3">
First Text Block
Input your hackathon preferences and team skills to get tailored project ideas.
</h2>
<p className="text-xl text-zinc-800">
This is the content for the first text block. You can write anything here.
</p>
</div>
{/* Box 2 (Image) - Standardized Size */}
<div className="w-full max-w-lg flex justify-center items-center">
@@ -93,7 +92,12 @@ export default function Home() {
className="w-full h-[350px] rounded-xl shadow-md flex items-center justify-center text-zinc-800"
style={{ backgroundColor: "#B8C0AD" }}
>
Image Placeholder
<img
src="s2.jpeg"
alt=" Img"
width={400}
height={400}
/>
</div>
</div>
</div>
@@ -106,7 +110,12 @@ export default function Home() {
className="w-full h-[350px] rounded-xl shadow-md flex items-center justify-center text-zinc-800"
style={{ backgroundColor: "#B8C0AD" }}
>
Image Placeholder
<img
src="s3.jpeg"
alt=" Img"
width={400}
height={400}
/>
</div>
</div>
{/* Box 4 (Text) - Standardized Size */}
@@ -116,12 +125,8 @@ export default function Home() {
style={{ backgroundColor: "#B8C0AD" }}
>
<h2 className="text-3xl font-semibold text-black mb-3">
Second Text Block
Choose between generated project ideas by swiping left or right on the cards.
</h2>
{/* Fixed the </D> typo to </p> */}
<p className="text-xl text-zinc-800">
This is the content for the second text block. You can write anything here.
</p>
</div>
</div>
</div>
@@ -134,11 +139,8 @@ export default function Home() {
style={{ backgroundColor: "#B8C0AD" }}
>
<h2 className="text-3xl font-semibold text-black mb-3">
Third Text Block
Work on your chosen project idea with your team using our integrated chat-based AI assistant.
</h2>
<p className="text-xl text-zinc-800">
This is the content for the third text block. You can write anything here.
</p>
</div>
{/* Box 6 (Image) - Standardized Size */}
<div className="w-full max-w-lg flex justify-center items-center">
@@ -146,7 +148,12 @@ export default function Home() {
className="w-full h-[350px] rounded-xl shadow-md flex items-center justify-center text-zinc-800"
style={{ backgroundColor: "#B8C0AD" }}
>
Image Placeholder
<img
src="s5.jpeg"
alt=" Img"
width={420}
height={450}
/>
</div>
</div>
</div>
+2 -2
View File
@@ -15,8 +15,8 @@ export async function chatToGemini(
settings:ChatSetting
):Promise<string>{
const model = genAI.getGenerativeModel({
model:settings.model || "gemini-2.5-flash",
systemInstruction: settings.systemInstructions || "you are going to create a project idea, explain it in summery first before going to detail",
model:settings.model || "gemini-2.5-flash-lite",
systemInstruction: settings.systemInstructions || "you are going to create a project idea, explain it in summary first before going to detail",
});
const generationConfig:GenerateConfig = {