diff --git a/app/Dashboard/page.tsx b/app/Dashboard/page.tsx index dd3e280..f175e89 100644 --- a/app/Dashboard/page.tsx +++ b/app/Dashboard/page.tsx @@ -4,20 +4,21 @@ import React, { useState } from "react"; import { useRouter } from "next/navigation"; import Link from 'next/link'; -export default function Dashboard() { - const router = useRouter(); - const [query, setQuery] = useState(""); - const [sideBarOpen, setSidebarOpen] = useState(true); +function Dashboard() { // 2. Changed from 'export default function' + const router = useRouter(); + const [query, setQuery] = useState(""); + const [sideBarOpen, setSidebarOpen] = useState(true); - const projects = [ - { name: "Alpha Project", createdAt: "2025-01-01" }, - { name: "Beta Board", createdAt: "2025-02-15" }, - { name: "Gamma Tasl", createdAt: "2025-03-10" }, - ]; - const filtered = projects.filter((p) => - p.name.toLowerCase().includes(query.toLowerCase()) - ); + const projects = [ + { name: "Alpha Project", createdAt: "2025-01-01" }, + { name: "Beta Board", createdAt: "2025-02-15" }, + { name: "Gamma Tasl", createdAt: "2025-03-10" }, + ]; + const filtered = projects.filter((p) => + p.name.toLowerCase().includes(query.toLowerCase()) + ); +<<<<<<< HEAD return (
- - ); + + Home + +

+ Settings +

+ {/* 3. Added the Logout link here */} + + Logout + + + + +
+
+
+
+ setQuery(e.target.value)} + placeholder="Search projects, boards or tasks..." + className="w-full max-w-md px-4 py-2 rounded-md border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 text-zinc-900 dark:text-zinc-100 focus:outline-none focus:ring-2 focus:ring-blue-500" + /> +
+ +
+ +
+
+
+ +
+

+ Project Names +

+ +
+
    + {filtered.map((project, index) => ( +
  • + {project.name} + + Created: {project.createdAt} + +
  • + ))} + {filtered.length === 0 && ( +
  • No results found
  • + )} +
+
+
+
+ + ); } \ No newline at end of file diff --git a/app/components/Cards.tsx b/app/components/Cards.tsx new file mode 100644 index 0000000..322b377 --- /dev/null +++ b/app/components/Cards.tsx @@ -0,0 +1,300 @@ +"use client"; + +import React, { useState } from "react"; +import { motion, useMotionValue, useTransform } from "framer-motion"; + +// Removed local fallback JSX declaration — use the official React type definitions instead: +// install @types/react and @types/react-dom so JSX.IntrinsicElements is provided by those packages. +// 1. Updated ColorWay interface +interface ColorWay { + card: string; + text: string; + titleColor: string; // Added title color +} + +// 2. Updated COLOR_WAYS array +const COLOR_WAYS: ColorWay[] = [ + { card: "#FEEEDF", text: "#173C46", titleColor: "#173C46" }, + { card: "#FEEEDF", text: "#EA627F", titleColor: "#173C46" }, // Example: Title is dark, text is pink + { card: "#DEE5D4", text: "#173C46", titleColor: "#EA627F" }, + { card: "#DEE5D4", text: "#EA627F", titleColor: "#173C46" }, + { card: "#B8C0AD", text: "#173C46", titleColor: "#173C46" }, + { card: "#B8C0AD", text: "#173C46", titleColor: "#EA627F" }, +]; + +interface CardData { + id: number; + title: string; + summary: string; + tracks: string[]; +} + +const cardData: CardData[] = [ + { + + id: 1, + + title: " WEBSITE", + + summary: + + "FFind out what makes GoonMAXXER unique.With this innovative website you will be able to explore various features and functionalities that set GoonMAXXER apart from the rest.", + + tracks: ["Gemini", "Auth0", "VibeCODING", "SmellyMaxxing", "shitposting"], + + }, + + { + + id: 2, + + title: "Intro to React", + + summary: + + "Learn the fundamentals of React, including components, state, and props.", + + tracks: ["React", "JS", "Frontend"], + + }, + + { + + id: 3, + + title: "State Management", + + summary: + + "Explore complex state solutions with Zustand, Redux, and Context API.", + + tracks: ["React", "Zustand", "State"], + + }, + + { + + id: 4, + + title: "Next.js 14", + + summary: "Master the app router, server actions, and data fetching.", + + tracks: ["Next.js", "React", "Fullstack"], + + }, + + { + + id: 5, + + title: "UI/UX Principles", + + summary: + + "A complete guide to user-centric design, prototyping, and testing.", + + tracks: ["Design", "UI", "UX"], + + }, + + { + + id: 6, + + title: "Node.js Essentials", + + summary: + + "Build scalable, high-performance backend applications with Node.js.", + + tracks: ["Node", "Backend", "JS"], + + }, + + { + + id: 7, + + title: "TypeScript", + + summary: + + "Add static typing to your JavaScript projects for robust, large-scale apps.", + + tracks: ["TypeScript", "JS", "DevOps"], + + }, + + { + + id: 8, + + title: "Framer Motion", + + summary: "Create production-ready animations for your React applications.", + + tracks: ["React", "Animation", "Framer"], + + }, + +].reverse(); + +const SwipeCards: React.FC = () => { + const [cards, setCards] = useState(cardData); + + const onSwipe = () => { + setCards((prev) => prev.slice(0, prev.length - 1)); + }; + + const handleReset = () => { + setCards(cardData); + }; + + return ( +
+ {cards.length > 0 ? ( + cards.map((card, index) => { + const colorWay = COLOR_WAYS[card.id % COLOR_WAYS.length]; + return ( + + ); + }) + ) : ( + // ... (empty state) +
+

+ No more cards! +

+

+ You've swiped through them all. +

+ +
+ )} +
+ ); +}; + +interface CardProps { + cardData: CardData; + colorWay: ColorWay; + isFront: boolean; + onSwipe: () => void; +} + +const Card: React.FC = ({ cardData, colorWay, isFront, onSwipe }) => { + const { title, summary, tracks } = cardData; + + const x = useMotionValue(0); + const rotate = useTransform(x, [-250, 250], [-25, 25]); + const opacity = useTransform( + x, + [-250, -100, 0, 100, 250], + [0, 1, 1, 1, 0], + ); + + const acceptOpacity = useTransform(x, [50, 150], [0, 1]); + const rejectOpacity = useTransform(x, [-150, -50], [1, 0]); + const badgeRotate = useTransform(x, [-150, 150], [-15, 15]); + + const handleDragEnd = (event: MouseEvent | TouchEvent | PointerEvent, info: { offset: { x: number } }) => { + if (Math.abs(info.offset.x) > 100) { + onSwipe(); + } + }; + + return ( + + {/* Accept/Reject Badges */} + +

Accept

+
+ +

Reject

+
+ + {/* Card Content */} +
+ {/* 3. Applied the specific titleColor here */} +

+ {title} +

+ {/* This summary

will inherit colorWay.text from the parent */} +

{summary}

+
+ +
+ {/* This

will also inherit colorWay.text */} +

+ Tracks +

+ {tracks.map((track) => ( + + {track} + + ))} +
+
+ ); + +}; + +export default SwipeCards; \ No newline at end of file diff --git a/app/components/ProtectedNavButton.tsx b/app/components/ProtectedNavButton.tsx new file mode 100644 index 0000000..5a5c48b --- /dev/null +++ b/app/components/ProtectedNavButton.tsx @@ -0,0 +1,35 @@ +"use client"; + +import React from "react"; +import { useRouter } from "next/navigation"; + +export default function ProtectedNavButton({ + children = "Go to Dashboard", + className = "mt-4 inline-block w-fit px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-md shadow-md transition-colors", +}: { + children?: React.ReactNode; + className?: string; +}) { + const router = useRouter(); + + const handleClick = () => { + // Simple client-side auth check. + // Assumption: if localStorage has 'token' (or 'auth'), user is signed in. + // Adjust key to match your real auth storage (cookie, localStorage key, etc.). + const token = typeof window !== "undefined" ? localStorage.getItem("token") : null; + + if (token) { + // The app route folder is named `Dashboard` (capital D). + // Use the same path casing to avoid 404s on Next.js routes. + router.push("/Dashboard"); + } else { + router.push("/auth/login"); + } + }; + + return ( + + ); +} diff --git a/app/exportType/types.ts b/app/exportType/types.ts index 7281483..d60f588 100644 --- a/app/exportType/types.ts +++ b/app/exportType/types.ts @@ -14,7 +14,10 @@ export interface GenerateConfig { temperature: number; topP: number; responseMimeType: string; +<<<<<<< HEAD maxOutputTokens?: number; +======= +>>>>>>> 44b5a4dc49ea757e9790ba8309edc6b37d764105 } export interface ChatSetting { diff --git a/app/formfile/page.tsx b/app/formfile/page.tsx index 613c7b3..a4abdf2 100644 --- a/app/formfile/page.tsx +++ b/app/formfile/page.tsx @@ -1,42 +1,54 @@ -// 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 "@/app/exportType/types"; +import { ChatSetting } 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(() => { - if (typeof window !== 'undefined') { - const storedFormData = localStorage.getItem('formData'); - if (storedFormData) { - const parsedData: FormData = JSON.parse(storedFormData); - return parsedData; - } - } +// --- Helper function to get default form data --- +const getDefaultFormData = (): FormData => ({ + Hackathon: '', + mainTheme: '', + tracksStack: [''], + students: [{ + studentName: '', + skills: [{ skillName: '', proficiency: PROFICIENCY_LEVELS[1] }] + }], +}); + +// --- New Initializer Function --- +const getInitialState = () => { + if (typeof window === 'undefined') { return { - Hackathon: '', - mainTheme: '', - tracksStack: [''], - students: [{ - studentName: '', - skills: [{ skillName: '', proficiency: PROFICIENCY_LEVELS[1] }] - }], + 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(""); - const [generatedCards, setGeneratedCards] = useState([]); // New state for parsed cards - - const [settings] = useState({ - 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. +// 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 +<<<<<<< HEAD **Track** [print out the track you used] **Concept:** [A concise, single paragraph description of the product and its primary function.]`, }); @@ -70,6 +82,25 @@ The output MUST be in Markdown format and strictly follow this structure: console.log("Parsed Cards:", ideas); return ideas; }; +======= +**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.]`, +}; +>>>>>>> 44b5a4dc49ea757e9790ba8309edc6b37d764105 + +// ------------------------------------------------ + +export default function Home() { + + const [initialState] = useState(getInitialState); + const [showForm, setShowForm] = useState(initialState.showForm); + const [formData, setFormData] = useState(initialState.data); + const [ideaResponse, setIdeaResponse] = useState(""); + + // The 'settings' state [useState] is now removed from here. const handleFormSubmitFromFormPage = (submittedData: FormData) => { setFormData(submittedData); @@ -77,8 +108,9 @@ The output MUST be in Markdown format and strictly follow this structure: generateIdeas(submittedData); }; - // eslint-disable-next-line react-hooks/preserve-manual-memoization + // Wrapped generateIdeas in useCallback const generateIdeas = useCallback(async (dataToProcess: FormData) => { + // 1. Data Cleaning const submissionData: FormData = { ...dataToProcess, tracksStack: dataToProcess.tracksStack.filter(tracks => tracks.trim() !== ''), @@ -90,6 +122,7 @@ The output MUST be in Markdown format and strictly follow this structure: .filter(student => student.studentName.trim() !== '' || student.skills.length > 0), }; + // 2. Prompt Construction const prompt = ` Hackathon: ${submissionData.Hackathon} Main Theme: ${submissionData.mainTheme} @@ -97,11 +130,13 @@ The output MUST be in Markdown format and strictly follow this structure: 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 { setIdeaResponse("Generating ideas... Please wait."); +<<<<<<< HEAD setGeneratedCards([]); // Clear previous cards const response = await fetch("/api/generate-idea", { method: "POST", @@ -116,10 +151,24 @@ The output MUST be in Markdown format and strictly follow this structure: const data = await response.json(); +======= + + 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 + }), + }); + + const data = await response.json(); +>>>>>>> 44b5a4dc49ea757e9790ba8309edc6b37d764105 if (data.error || !response.ok) { console.error("AI Error:", data.error); setIdeaResponse(`Error: Failed to generate idea. ${data.error || response.statusText}`); return; +<<<<<<< HEAD } setIdeaResponse(data.response); @@ -146,10 +195,33 @@ The output MUST be in Markdown format and strictly follow this structure: setTimeout(() => setShowForm(false), 0); generateIdeas(parsedData); localStorage.removeItem('formData'); +======= +>>>>>>> 44b5a4dc49ea757e9790ba8309edc6b37d764105 } - } - }, [generateIdeas]); + 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 + }, []); + + // Replaced useEffect + useEffect(() => { + if (initialState.runEffect) { + generateIdeas(initialState.data); + } + }, [initialState, generateIdeas]); + + // --- JSX Rendering --- return (
@@ -157,6 +229,7 @@ The output MUST be in Markdown format and strictly follow this structure: ) : ( <> +<<<<<<< HEAD {generatedCards.length > 0 ? ( ) : ( @@ -174,6 +247,19 @@ The output MUST be in Markdown format and strictly follow this structure:
)} +======= +

+ Generated Project Idea 💡 +

+
+

Generated Ideas:

+
') }} + /> + {ideaResponse === "" &&

Submit the form to generate project ideas.

} +
+>>>>>>> 44b5a4dc49ea757e9790ba8309edc6b37d764105
); +======= +
+
+
+

+ Welcome! This is HackSpark +

+

+ 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!{" "} + {/* +                Templates +              {" "} +              or the{" "} +               +                Learning +              {" "} */} + {/* center. */} +

+ + {/* Get Started */} + +
+ +
+
+ Image Placeholder +
+
+
+ +
+ {/*
*/} +
+ {/*
*/} +

+ Guide: +

+ {/*
*/} + +
+
+

+ First Text Block +

+

+ This is the content for the first text block. You can write anything here. +

+
+
+
+ Image Placeholder +
+
+
+ +
+
+
+ Image Placeholder +
+
+
+
+

+ Second Text Block +

+

+ This is the content for the second text block. You can write anything here. +

+
+
+
+ +
+
+

+ Third Text Block +

+

+ This is the content for the third text block. You can write anything here. +

+
+
+
+ Image Placeholder +
+
+
+
+
+
+ + ); +>>>>>>> 44b5a4dc49ea757e9790ba8309edc6b37d764105 } + + +// {/* */} + + +// "use client"; + +// import React, { use, useState } from "react"; +// import { useRouter } from "next/navigation"; + +// export default function Dashboard() { +//   const router = useRouter();   +//   const[query, setQuery] = useState(""); +//   // const [projects, setProjects] = useState( [ +//   //     { id: 1, name: "Alpha Project", createdAt: new Date("2025-01-01")}, +//   //     { id: 2, name: "Beta Board", createdAt: new Date("2025-02-15")}, +//   //     { id: 3, name: "Gamma Task", createdAt: new Date("2025-03-10")} +//   // ]); +//   // const [newProjectName, setNewProjectName] = useState(""); +//   // const filtered = projects.filter( (p) => p.name.toLowerCase().includes(query.toLowerCase()) ); +//   // const handleCreate = () => { +//   //   if(!newProjectName.trim()) return; +//   //   const newProject = { +//   //     id: projects.length+1, name: newProjectName, createdAt: new Date(), +//   //   }; +//   // }; +//   const projects = [ +//     {name: "Alpha Project", createdAt: "2025-01-01"}, +//     {name: "Beta Board", createdAt: "2025-02-15"}, +//     {name: "Gamma Tasl", createdAt: "2025-03-10"}, +//   ] +//   const filtered = projects.filter( +//     (p) => p.name.toLowerCase().includes(query.toLowerCase()) +//   ); + +//  return ( +//    
+//      
+//        
+//          
+//            {/* */} +//             setQuery(e.target.value)} +//              placeholder="Search projects, boards or tasks..." +//              className="w-full max-w-md px-4 py-2 rounded-md border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 text-zinc-900 dark:text-zinc-100 focus:outline-none focus:ring-2 focus:ring-blue-500" +//            /> +//          
+ +//          
+//            {/* setNewProjectName(e.target.value)} +//               placeholder="New Project Name" +//               className="px-3 py-2 rounded-md border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-900  text-zinc-900 dark:text-zinc-100 focus:outline-none focus:ring-2 focus:ring-blue-500" +//             /> */} +//              +//          
+//        
+//      
+ +//      
+//        

+//          Project Names +//        

+ +//        
+//          
    +//            {filtered.map((project, index) => ( +//              
  • +//                {project.name} +//                  +//                   {/* {project.createdAt.toLocaleDateString()} */} +//                   Created: {project.createdAt} +//                  +//              
  • +//            ))} + +//            {filtered.length === 0 && ( +//              
  • No results found
  • +//            )} +//          
+//        
+//      
+//    
+//  ); +// } \ No newline at end of file diff --git a/components/ProtectedNavButton.tsx b/components/ProtectedNavButton.tsx deleted file mode 100644 index 34fa0e7..0000000 --- a/components/ProtectedNavButton.tsx +++ /dev/null @@ -1,33 +0,0 @@ -"use client"; - -import React from "react"; -import { useRouter } from "next/navigation"; - -export default function ProtectedNavButton({ - children = "Go to Dashboard", - className = "mt-4 inline-block w-fit px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-md shadow-md transition-colors", -}: { - children?: React.ReactNode; - className?: string; -}) { - const router = useRouter(); - - const handleClick = () => { - // Simple client-side auth check. - // Assumption: if localStorage has 'token' (or 'auth'), user is signed in. - // Adjust key to match your real auth storage (cookie, localStorage key, etc.). - const token = typeof window !== "undefined" ? localStorage.getItem("token") : null; - - if (token) { - router.push("/dashboard"); - } else { - router.push("/auth/login"); - } - }; - - return ( - - ); -}