mirror of
https://github.com/PinkSheep27/SBU-Hackathon.git
synced 2026-10-10 22:50:26 -04:00
merge bunch of files (mostly page.tsx's and Gemini stuff)
This commit is contained in:
+112
-27
@@ -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 (
|
||||
<div className="flex min-h-screen bg-zinc-50 dark:bg-black font-sans transition-all duration-300">
|
||||
<aside
|
||||
@@ -100,20 +101,104 @@ export default function Dashboard() {
|
||||
<li
|
||||
key={index}
|
||||
className="flex justify-between p-4 bg-white dark:bg-zinc-900 border border-zinc-100 dark:border-zinc-800 rounded-md text-zinc-800 dark:text-zinc-100"
|
||||
=======
|
||||
return (
|
||||
<div className="flex min-h-screen bg-zinc-50 dark:bg-black font-sans transition-all duration-300">
|
||||
<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`}
|
||||
>
|
||||
<div className="flex items-center justify-between p-4 border-b border-zinc-200 dark:border-zinc-800">
|
||||
{sideBarOpen && (
|
||||
<span className="text-lg font-semibold text-zinc-800 dark:text-zinc-100">
|
||||
More
|
||||
</span>
|
||||
)}
|
||||
<button
|
||||
onClick={() => setSidebarOpen(!sideBarOpen)}
|
||||
className="text-zinc-600 dark:text-zinc-300 hover:text-blue-500 transition-colors"
|
||||
title="Toggle Sidebar"
|
||||
>
|
||||
☰
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<nav
|
||||
className={`flex flex-col p-4 gap-3 text-zinc-700 dark:text-zinc-300 transition-opacity duration-200 ${sideBarOpen ? "opacity-100" : "opacity-0"
|
||||
}`}
|
||||
>>>>>>> 44b5a4dc49ea757e9790ba8309edc6b37d764105
|
||||
>
|
||||
<span>{project.name}</span>
|
||||
<span className="text-zinc-500 dark:text-zinc-400 text-sm">
|
||||
Created: {project.createdAt}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
{filtered.length === 0 && (
|
||||
<li className="text-zinc-500">No results found</li>
|
||||
)}
|
||||
</ul>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
<Link
|
||||
href="/"
|
||||
className="hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
|
||||
>
|
||||
Home
|
||||
</Link>
|
||||
<p
|
||||
className="hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
|
||||
>
|
||||
Settings
|
||||
</p>
|
||||
{/* 3. Added the Logout link here */}
|
||||
<Link
|
||||
href="/auth/logout"
|
||||
className="hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
|
||||
>
|
||||
Logout
|
||||
</Link>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<div className="flex-1 flex flex-col min-w-0">
|
||||
<header className="sticky top-0 z-10 bg-white dark:bg-black/80 backdrop-blur-sm border-b border-zinc-200 dark:border-zinc-800">
|
||||
<div className="max-w-7xl mx-auto px-6 py-4 flex items-center justify-between gap-4">
|
||||
<div className="flex-1">
|
||||
<input
|
||||
id="dashboard-search"
|
||||
value={query}
|
||||
onChange={(e) => 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"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => router.push("/dashbaord/create")}
|
||||
className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-md shadow-sm"
|
||||
>
|
||||
Create
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="max-w-7xl w-full mx-auto px-6 py-10">
|
||||
<h1 className="text-2xl font-semibold text-black dark:text-zinc-50 mb-4">
|
||||
Project Names
|
||||
</h1>
|
||||
|
||||
<section>
|
||||
<ul className="space-y-3">
|
||||
{filtered.map((project, index) => (
|
||||
<li
|
||||
key={index}
|
||||
className="flex justify-between p-4 bg-white dark:bg-zinc-900 border border-zinc-100 dark:border-zinc-800 rounded-md text-zinc-800 dark:text-zinc-100"
|
||||
>
|
||||
<span>{project.name}</span>
|
||||
<span className="text-zinc-500 dark:text-zinc-400 text-sm">
|
||||
Created: {project.createdAt}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
{filtered.length === 0 && (
|
||||
<li className="text-zinc-500">No results found</li>
|
||||
)}
|
||||
</ul>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -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[]>(cardData);
|
||||
|
||||
const onSwipe = () => {
|
||||
setCards((prev) => prev.slice(0, prev.length - 1));
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
setCards(cardData);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="grid h-screen w-full place-items-center bg-orange-100"
|
||||
// -gradient-to-tr from-orange-200 to-orange-100
|
||||
style={{
|
||||
backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 32 32' width='32' height='32' fill='none'
|
||||
stroke-width='2' stroke='%23d4d4d4'%3e%3cpath d='M0 .5H31.5V32'/%3e%3c/svg%3e")`,
|
||||
}}
|
||||
>
|
||||
{cards.length > 0 ? (
|
||||
cards.map((card, index) => {
|
||||
const colorWay = COLOR_WAYS[card.id % COLOR_WAYS.length];
|
||||
return (
|
||||
<Card
|
||||
key={card.id}
|
||||
cardData={card}
|
||||
colorWay={colorWay}
|
||||
isFront={index === cards.length - 1}
|
||||
onSwipe={onSwipe}
|
||||
/>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
// ... (empty state)
|
||||
<div className="text-center">
|
||||
<h2 className="text-2xl font-bold text-neutral-700">
|
||||
No more cards!
|
||||
</h2>
|
||||
<p className="mb-4 text-neutral-500">
|
||||
You've swiped through them all.
|
||||
</p>
|
||||
<button
|
||||
onClick={handleReset}
|
||||
className="rounded-md bg-indigo-500 px-4 py-2 font-semibold text-white transition-colors hover:bg-indigo-600"
|
||||
>
|
||||
Reset Deck
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface CardProps {
|
||||
cardData: CardData;
|
||||
colorWay: ColorWay;
|
||||
isFront: boolean;
|
||||
onSwipe: () => void;
|
||||
}
|
||||
|
||||
const Card: React.FC<CardProps> = ({ 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 (
|
||||
<motion.div
|
||||
className="absolute flex h-[48rem] w-[36rem] flex-col justify-between overflow-hidden rounded-2xl p-6 shadow-2xl hover:cursor-grab active:cursor-grabbing"
|
||||
style={{
|
||||
x,
|
||||
rotate,
|
||||
opacity,
|
||||
backgroundColor: colorWay.card,
|
||||
color: colorWay.text, // This is the default text color
|
||||
zIndex: isFront ? 2 : 1,
|
||||
pointerEvents: isFront ? "auto" : "none",
|
||||
}}
|
||||
animate={{
|
||||
scale: isFront ? 1 : 0.95,
|
||||
y: isFront ? 0 : -40,
|
||||
}}
|
||||
transition={{ type: "spring", stiffness: 300, damping: 30 }}
|
||||
drag={isFront ? "x" : false}
|
||||
dragConstraints={{ left: 0, right: 0, top: 0, bottom: 0 }}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
{/* Accept/Reject Badges */}
|
||||
<motion.div
|
||||
className="absolute left-10 top-10 z-10 rounded-2xl border-8 border-green-500 px-10 py-4"
|
||||
style={{ opacity: acceptOpacity, rotate: badgeRotate }}
|
||||
>
|
||||
<h3 className="text-5xl font-bold uppercase text-green-500">Accept</h3>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
className="absolute right-10 top-10 z-10 rounded-2xl border-8 border-red-500 px-10 py-4"
|
||||
style={{ opacity: rejectOpacity, rotate: badgeRotate }}
|
||||
>
|
||||
<h3 className="text-5xl font-bold uppercase text-red-500">Reject</h3>
|
||||
</motion.div>
|
||||
|
||||
{/* Card Content */}
|
||||
<div className="relative z-0">
|
||||
{/* 3. Applied the specific titleColor here */}
|
||||
<h2
|
||||
className="mb-4 text-center text-7xl font-bold"
|
||||
style={{ color: colorWay.titleColor }}
|
||||
>
|
||||
{title}
|
||||
</h2>
|
||||
{/* This summary <p> will inherit colorWay.text from the parent */}
|
||||
<p className="text-center text-3xl font-light">{summary}</p>
|
||||
</div>
|
||||
|
||||
<div className="relative z-0 flex flex-wrap items-center justify-center gap-2">
|
||||
{/* This <h3> will also inherit colorWay.text */}
|
||||
<h3 className="mb-4 w-full text-center text-4xl font-semibold">
|
||||
Tracks
|
||||
</h3>
|
||||
{tracks.map((track) => (
|
||||
<span
|
||||
key={track}
|
||||
className="flex items-center justify-center rounded-full px-8 py-3 text-2xl font-medium"
|
||||
style={{
|
||||
// This logic now correctly contrasts with the main text color
|
||||
backgroundColor:
|
||||
colorWay.text === "#ffffff"
|
||||
? "rgba(255, 255, 255, 0.2)"
|
||||
: "rgba(0, 0, 0, 0.15)",
|
||||
}}
|
||||
>
|
||||
{track}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
export default SwipeCards;
|
||||
@@ -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 (
|
||||
<button type="button" onClick={handleClick} className={className}>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -14,7 +14,10 @@ export interface GenerateConfig {
|
||||
temperature: number;
|
||||
topP: number;
|
||||
responseMimeType: string;
|
||||
<<<<<<< HEAD
|
||||
maxOutputTokens?: number;
|
||||
=======
|
||||
>>>>>>> 44b5a4dc49ea757e9790ba8309edc6b37d764105
|
||||
}
|
||||
|
||||
export interface ChatSetting {
|
||||
|
||||
+119
-33
@@ -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<FormData>(() => {
|
||||
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<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.
|
||||
// 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<FormData>(initialState.data);
|
||||
const [ideaResponse, setIdeaResponse] = useState<string>("");
|
||||
|
||||
// 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 (
|
||||
<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">
|
||||
@@ -157,6 +229,7 @@ The output MUST be in Markdown format and strictly follow this structure:
|
||||
<FormPage onFormSubmit={handleFormSubmitFromFormPage} />
|
||||
) : (
|
||||
<>
|
||||
<<<<<<< HEAD
|
||||
{generatedCards.length > 0 ? (
|
||||
<SwipeCards initialCards={generatedCards} />
|
||||
) : (
|
||||
@@ -174,6 +247,19 @@ The output MUST be in Markdown format and strictly follow this structure:
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
=======
|
||||
<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>
|
||||
>>>>>>> 44b5a4dc49ea757e9790ba8309edc6b37d764105
|
||||
<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"
|
||||
|
||||
@@ -1,3 +1,35 @@
|
||||
<<<<<<< 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');
|
||||
|
||||
>>>>>>> 44b5a4dc49ea757e9790ba8309edc6b37d764105
|
||||
@import "tailwindcss";
|
||||
|
||||
:root {
|
||||
@@ -11,9 +43,18 @@
|
||||
|
||||
/* 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);
|
||||
|
||||
/* We add a new font family for headers (Pixelify Sans) */
|
||||
/* This also creates a 'font-header' utility class */
|
||||
--font-header: var(--font-pixelify);
|
||||
>>>>>>> 44b5a4dc49ea757e9790ba8309edc6b37d764105
|
||||
|
||||
--font-mono: var(--font-geist-mono);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
|
||||
// 1. Import your two new fonts
|
||||
import { Concert_One, Pixelify_Sans } from "next/font/google";
|
||||
import "./globals.css";
|
||||
// import { Auth0Provider } from "@auth0/nextjs-auth0";
|
||||
|
||||
// 2. Set up Concert One as the body/sans font
|
||||
const concertOne = Concert_One({
|
||||
|
||||
+289
-6
@@ -1,11 +1,31 @@
|
||||
"use client";
|
||||
import Image from "next/image";
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import Image from 'next/image';
|
||||
import ProtectedNavButton from "../components/ProtectedNavButton";
|
||||
import Link from 'next/link';
|
||||
export default function Home() {
|
||||
return (
|
||||
<div
|
||||
style={{ backgroundColor: "#FEEEDF" }}
|
||||
className="flex items-center justify-center font-sans "
|
||||
>
|
||||
<header className="absolute top-6 left-8 right-8 flex items-center justify-between text-sm font-medium text-zinc-700 dark:text-zinc-300">
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/next.svg"
|
||||
alt="Next.js logo"
|
||||
width={100}
|
||||
height={20}
|
||||
priority
|
||||
/>
|
||||
<div className="flex items-center gap-4">
|
||||
<a href="/signin" className="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
|
||||
Sign in
|
||||
</a>
|
||||
<a href="/register" className="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
|
||||
Register
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<<<<<<< HEAD
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black">
|
||||
@@ -124,4 +144,267 @@ export default function Page() {
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
=======
|
||||
<main className="flex min-h-screen w-full flex-col justify-between py-32 max-w-[1200px] mx-auto">
|
||||
<div className="flex flex-col sm:flex-row items-center justify-center gap-12 text-left w-full max-w-[1200px] mx-auto">
|
||||
<div className="flex-1 flex flex-col gap-5 justify-center items-start">
|
||||
<h1
|
||||
style={{ color: "#173C46" }}
|
||||
className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50"
|
||||
>
|
||||
Welcome! This is HackSpark
|
||||
</h1>
|
||||
<p
|
||||
style={{ color: "#173C46" }}
|
||||
className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400"
|
||||
>
|
||||
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!{" "}
|
||||
{/* <a
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
className="font-medium text-zinc-950 dark:text-zinc-50"
|
||||
>
|
||||
Templates
|
||||
</a>{" "}
|
||||
or the{" "}
|
||||
<a
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
className="font-medium text-zinc-950 dark:text-zinc-50"
|
||||
>
|
||||
Learning
|
||||
</a>{" "} */}
|
||||
{/* center. */}
|
||||
</p>
|
||||
|
||||
{/* <ProtectedNavButton>Get Started</ProtectedNavButton> */}
|
||||
<button
|
||||
style={{ backgroundColor: "#173C46", color: "white" }}
|
||||
className="px-4 py-1.5 rounded-md shadow-md font-semibold hover:opacity-90 transition-opacity"
|
||||
>
|
||||
{/* {children} */}
|
||||
Get Started
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 flex justify-center">
|
||||
<div
|
||||
className="w-full max-w-[400px] h-[300px] bg-zinc-200 dark:bg-zinc-800 rounded-xl shadow-md flex items-center justify-center text-zinc-500"
|
||||
style={{ backgroundColor: "#173C46" }}
|
||||
>
|
||||
Image Placeholder
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{ backgroundColor: "#DEE5D4" }}
|
||||
className="w-full rounded-xl mt-32"
|
||||
>
|
||||
{/* <section className="mt-32 w-full flex flex-col items-start gap-12"> */}
|
||||
<section className="px-16 py-18 flex flex-col items-start gap-12 max-w-[1200px] mx-auto">
|
||||
{/* <div> */}
|
||||
<h2 className="text-[28px] font-semibold text-black dark:text-zinc-50 leading-9">
|
||||
Guide:
|
||||
</h2>
|
||||
{/* </div> */}
|
||||
|
||||
<div className="flex flex-col sm:flex-row items-center justify-center gap-8 w-full">
|
||||
<div
|
||||
className="w-full max-w-xl rounded-xl h-[200px] p-6 flex flex-col justify-center"
|
||||
style={{ backgroundColor: "#B8C0AD" }}
|
||||
>
|
||||
<h2 className="text-2xl font-semibold text-black dark:text-zinc-50 mb-2">
|
||||
First Text Block
|
||||
</h2>
|
||||
<p className="text-lg text-zinc-600 dark:text-zinc-400">
|
||||
This is the content for the first text block. You can write anything here.
|
||||
</p>
|
||||
</div>
|
||||
<div className="sm:w-1/2 flex justify-center items-center">
|
||||
<div
|
||||
className="w-full max-w-[400px] h-[300px] bg-zinc-200 dark:bg-zinc-800 rounded-xl shadow-md flex items-center justify-center text-zinc-500"
|
||||
style={{ backgroundColor: "#B8C0AD" }}
|
||||
>
|
||||
Image Placeholder
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col sm:flex-row items-center justify-between gap-8 w-full mt-30">
|
||||
<div className="sm:w-1/2 flex justify-center items-center">
|
||||
<div
|
||||
className="w-full max-w-[400px] h-[300px] bg-zinc-200 dark:bg-zinc-800 rounded-xl shadow-md flex items-center justify-center text-zinc-500"
|
||||
style={{ backgroundColor: "#B8C0AD" }}
|
||||
>
|
||||
Image Placeholder
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-full justify-center sm:justify-center">
|
||||
<div
|
||||
className="w-full max-w-xl rounded-xl h-[200px] p-6 flex flex-col justify-center"
|
||||
style={{ backgroundColor: "#B8C0AD" }}
|
||||
>
|
||||
<h2 className="text-2xl font-semibold text-black dark:text-zinc-50 mb-2">
|
||||
Second Text Block
|
||||
</h2>
|
||||
<p className="text-lg text-zinc-600 dark:text-zinc-400">
|
||||
This is the content for the second text block. You can write anything here.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col sm:flex-row items-center justify-center gap-8 w-full mt-30">
|
||||
<div
|
||||
className="w-full max-w-xl rounded-xl h-[200px] p-6 flex flex-col justify-center"
|
||||
style={{ backgroundColor: "#B8C0AD" }}
|
||||
>
|
||||
<h2 className="text-2xl font-semibold text-black dark:text-zinc-50 mb-2">
|
||||
Third Text Block
|
||||
</h2>
|
||||
<p className="text-lg text-zinc-600 dark:text-zinc-400">
|
||||
This is the content for the third text block. You can write anything here.
|
||||
</p>
|
||||
</div>
|
||||
<div className="sm:w-1/2 flex justify-center items-center">
|
||||
<div
|
||||
className="w-full max-w-[400px] h-[300px] bg-zinc-200 dark:bg-zinc-800 rounded-xl shadow-md flex items-center justify-center text-zinc-500"
|
||||
style={{ backgroundColor: "#B8C0AD" }}
|
||||
>
|
||||
Image Placeholder
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
>>>>>>> 44b5a4dc49ea757e9790ba8309edc6b37d764105
|
||||
}
|
||||
|
||||
|
||||
// {/* <div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
|
||||
// <a
|
||||
// className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
|
||||
// href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
// target="_blank"
|
||||
// rel="noopener noreferrer"
|
||||
// >
|
||||
// <Image
|
||||
// className="dark:invert"
|
||||
// src="/vercel.svg"
|
||||
// alt="Vercel logomark"
|
||||
// width={16}
|
||||
// height={16}
|
||||
// />
|
||||
// Deploy Now
|
||||
// </a>
|
||||
// <a
|
||||
// className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
|
||||
// href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
// target="_blank"
|
||||
// rel="noopener noreferrer"
|
||||
// >
|
||||
// Documentation
|
||||
// </a>
|
||||
// </div> */}
|
||||
|
||||
|
||||
// "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 (
|
||||
// <div className="min-h-screen bg-zinc-50 dark:bg-black font-sans">
|
||||
// <header className="sticky top-0 z-10 bg-white dark:bg-black/80 backdrop-blur-sm border-b border-zinc-200 dark:border-zinc-800">
|
||||
// <div className="max-w-7xl mx-auto px-6 py-4 flex items-center justify-between gap-4">
|
||||
// <div className="flex-1">
|
||||
// {/* <label htmlFor="dashboard-search" className="sr-only">
|
||||
// Search
|
||||
// </label> */}
|
||||
// <input
|
||||
// id="dashboard-search"
|
||||
// value={query}
|
||||
// onChange={(e) => 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"
|
||||
// />
|
||||
// </div>
|
||||
|
||||
// <div className="flex items-center gap-2">
|
||||
// {/* <input
|
||||
// type = "text"
|
||||
// value = {newProjectName}
|
||||
// onChange = { (e) => 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"
|
||||
// /> */}
|
||||
// <button
|
||||
// type="button"
|
||||
// // className="ml-4 inline-flex items-center gap-2 bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-md shadow-sm"
|
||||
// // onClick={() => alert("Create clicked — wire up creation flow here.")}
|
||||
// onClick={ () => router.push("/dashbaord/create")}
|
||||
// className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-md shadow-sm"
|
||||
// >
|
||||
// Create
|
||||
// </button>
|
||||
// </div>
|
||||
// </div>
|
||||
// </header>
|
||||
|
||||
// <main className="max-w-7xl mx-auto px-6 py-10">
|
||||
// <h1 className="text-2xl font-semibold text-black dark:text-zinc-50 mb-4">
|
||||
// Project Names
|
||||
// </h1>
|
||||
|
||||
// <section>
|
||||
// <ul className="space-y-3">
|
||||
// {filtered.map((project, index) => (
|
||||
// <li
|
||||
// key={index}
|
||||
// // className="p-4 bg-white dark:bg-zinc-900 border border-zinc-100 dark:border-zinc-800 rounded-md text-zinc-800 dark:text-zinc-100"
|
||||
// className="flex justify-between p-4 bg-white dark:bg-zinc-900 border border-zinc-100 dark:border-zinc-800 rounded-md text-zinc-800 dark:text-zinc-100"
|
||||
// >
|
||||
// <span>{project.name}</span>
|
||||
// <span className="text-zinc-500 dark:text-zinc-400 text-sm">
|
||||
// {/* {project.createdAt.toLocaleDateString()} */}
|
||||
// Created: {project.createdAt}
|
||||
// </span>
|
||||
// </li>
|
||||
// ))}
|
||||
|
||||
// {filtered.length === 0 && (
|
||||
// <li className="text-zinc-500">No results found</li>
|
||||
// )}
|
||||
// </ul>
|
||||
// </section>
|
||||
// </main>
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
@@ -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 (
|
||||
<button type="button" onClick={handleClick} className={className}>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user