mirror of
https://github.com/PinkSheep27/SBU-Hackathon.git
synced 2026-10-10 23:40:27 -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 { useRouter } from "next/navigation";
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
export default function Dashboard() {
|
function Dashboard() { // 2. Changed from 'export default function'
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
const [sideBarOpen, setSidebarOpen] = useState(true);
|
const [sideBarOpen, setSidebarOpen] = useState(true);
|
||||||
|
|
||||||
const projects = [
|
const projects = [
|
||||||
{ name: "Alpha Project", createdAt: "2025-01-01" },
|
{ name: "Alpha Project", createdAt: "2025-01-01" },
|
||||||
{ name: "Beta Board", createdAt: "2025-02-15" },
|
{ name: "Beta Board", createdAt: "2025-02-15" },
|
||||||
{ name: "Gamma Tasl", createdAt: "2025-03-10" },
|
{ name: "Gamma Tasl", createdAt: "2025-03-10" },
|
||||||
];
|
];
|
||||||
const filtered = projects.filter((p) =>
|
const filtered = projects.filter((p) =>
|
||||||
p.name.toLowerCase().includes(query.toLowerCase())
|
p.name.toLowerCase().includes(query.toLowerCase())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
return (
|
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 dark:bg-black font-sans transition-all duration-300">
|
||||||
<aside
|
<aside
|
||||||
@@ -100,20 +101,104 @@ export default function Dashboard() {
|
|||||||
<li
|
<li
|
||||||
key={index}
|
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"
|
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>
|
<Link
|
||||||
<span className="text-zinc-500 dark:text-zinc-400 text-sm">
|
href="/"
|
||||||
Created: {project.createdAt}
|
className="hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
|
||||||
</span>
|
>
|
||||||
</li>
|
Home
|
||||||
))}
|
</Link>
|
||||||
{filtered.length === 0 && (
|
<p
|
||||||
<li className="text-zinc-500">No results found</li>
|
className="hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
|
||||||
)}
|
>
|
||||||
</ul>
|
Settings
|
||||||
</section>
|
</p>
|
||||||
</main>
|
{/* 3. Added the Logout link here */}
|
||||||
</div>
|
<Link
|
||||||
</div>
|
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;
|
temperature: number;
|
||||||
topP: number;
|
topP: number;
|
||||||
responseMimeType: string;
|
responseMimeType: string;
|
||||||
|
<<<<<<< HEAD
|
||||||
maxOutputTokens?: number;
|
maxOutputTokens?: number;
|
||||||
|
=======
|
||||||
|
>>>>>>> 44b5a4dc49ea757e9790ba8309edc6b37d764105
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChatSetting {
|
export interface ChatSetting {
|
||||||
|
|||||||
+119
-33
@@ -1,42 +1,54 @@
|
|||||||
// File: Home.tsx (Renamed from form.tsx for export consistency)
|
|
||||||
|
|
||||||
"use client";
|
"use client";
|
||||||
import React, { useState, useEffect, useCallback } from "react";
|
import React, { useState, useEffect, useCallback } from "react";
|
||||||
import FormPage from './FormPage';
|
import FormPage from './FormPage';
|
||||||
import { ChatSetting, CardData } from "@/app/exportType/types";
|
import { ChatSetting } from "../exportType/types";
|
||||||
import { FormData, PROFICIENCY_LEVELS } from './formTypes';
|
import { FormData, PROFICIENCY_LEVELS } from './formTypes';
|
||||||
import SwipeCards from '../cards/page';
|
|
||||||
|
|
||||||
export default function Home() {
|
// --- Helper function to get default form data ---
|
||||||
const [showForm, setShowForm] = useState(true);
|
const getDefaultFormData = (): FormData => ({
|
||||||
const [formData, setFormData] = useState<FormData>(() => {
|
Hackathon: '',
|
||||||
if (typeof window !== 'undefined') {
|
mainTheme: '',
|
||||||
const storedFormData = localStorage.getItem('formData');
|
tracksStack: [''],
|
||||||
if (storedFormData) {
|
students: [{
|
||||||
const parsedData: FormData = JSON.parse(storedFormData);
|
studentName: '',
|
||||||
return parsedData;
|
skills: [{ skillName: '', proficiency: PROFICIENCY_LEVELS[1] }]
|
||||||
}
|
}],
|
||||||
}
|
});
|
||||||
|
|
||||||
|
// --- New Initializer Function ---
|
||||||
|
const getInitialState = () => {
|
||||||
|
if (typeof window === 'undefined') {
|
||||||
return {
|
return {
|
||||||
Hackathon: '',
|
data: getDefaultFormData(),
|
||||||
mainTheme: '',
|
showForm: true,
|
||||||
tracksStack: [''],
|
runEffect: false
|
||||||
students: [{
|
|
||||||
studentName: '',
|
|
||||||
skills: [{ skillName: '', proficiency: PROFICIENCY_LEVELS[1] }]
|
|
||||||
}],
|
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
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>("");
|
// FIX 1 (Memoization): Moved settings OUTSIDE the component.
|
||||||
const [generatedCards, setGeneratedCards] = useState<CardData[]>([]); // New state for parsed cards
|
// This makes it a true constant.
|
||||||
|
const AI_SETTINGS: ChatSetting = {
|
||||||
const [settings] = useState<ChatSetting>({
|
temperature: 1,
|
||||||
temperature: 1,
|
model: "gemini-2.5-pro",
|
||||||
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.
|
||||||
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:
|
The output MUST be in Markdown format and strictly follow this structure:
|
||||||
### Project Idea Title
|
### Project Idea Title
|
||||||
|
<<<<<<< HEAD
|
||||||
**Track** [print out the track you used]
|
**Track** [print out the track you used]
|
||||||
**Concept:** [A concise, single paragraph description of the product and its primary function.]`,
|
**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);
|
console.log("Parsed Cards:", ideas);
|
||||||
return 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) => {
|
const handleFormSubmitFromFormPage = (submittedData: FormData) => {
|
||||||
setFormData(submittedData);
|
setFormData(submittedData);
|
||||||
@@ -77,8 +108,9 @@ The output MUST be in Markdown format and strictly follow this structure:
|
|||||||
generateIdeas(submittedData);
|
generateIdeas(submittedData);
|
||||||
};
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/preserve-manual-memoization
|
// Wrapped generateIdeas in useCallback
|
||||||
const generateIdeas = useCallback(async (dataToProcess: FormData) => {
|
const generateIdeas = useCallback(async (dataToProcess: FormData) => {
|
||||||
|
// 1. Data Cleaning
|
||||||
const submissionData: FormData = {
|
const submissionData: FormData = {
|
||||||
...dataToProcess,
|
...dataToProcess,
|
||||||
tracksStack: dataToProcess.tracksStack.filter(tracks => tracks.trim() !== ''),
|
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),
|
.filter(student => student.studentName.trim() !== '' || student.skills.length > 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 2. Prompt Construction
|
||||||
const prompt = `
|
const prompt = `
|
||||||
Hackathon: ${submissionData.Hackathon}
|
Hackathon: ${submissionData.Hackathon}
|
||||||
Main Theme: ${submissionData.mainTheme}
|
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 =>
|
Team Members and Skills: ${submissionData.students.map(s =>
|
||||||
`${s.studentName || 'Unamed Member'} (Skills: ${s.skills.map(sk => `${sk.skillName} - ${sk.proficiency}`).join(', ')})`
|
`${s.studentName || 'Unamed Member'} (Skills: ${s.skills.map(sk => `${sk.skillName} - ${sk.proficiency}`).join(', ')})`
|
||||||
).join('; ')}
|
).join('; ')}
|
||||||
|
|
||||||
Using the SYSTEM INSTRUCTIONS, generate 5 project ideas based on the data above.`;
|
Using the SYSTEM INSTRUCTIONS, generate 5 project ideas based on the data above.`;
|
||||||
|
|
||||||
|
// 3. API Call
|
||||||
try {
|
try {
|
||||||
setIdeaResponse("Generating ideas... Please wait.");
|
setIdeaResponse("Generating ideas... Please wait.");
|
||||||
|
<<<<<<< HEAD
|
||||||
setGeneratedCards([]); // Clear previous cards
|
setGeneratedCards([]); // Clear previous cards
|
||||||
const response = await fetch("/api/generate-idea", {
|
const response = await fetch("/api/generate-idea", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -116,10 +151,24 @@ The output MUST be in Markdown format and strictly follow this structure:
|
|||||||
|
|
||||||
const data = await response.json();
|
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) {
|
if (data.error || !response.ok) {
|
||||||
console.error("AI Error:", data.error);
|
console.error("AI Error:", data.error);
|
||||||
setIdeaResponse(`Error: Failed to generate idea. ${data.error || response.statusText}`);
|
setIdeaResponse(`Error: Failed to generate idea. ${data.error || response.statusText}`);
|
||||||
return;
|
return;
|
||||||
|
<<<<<<< HEAD
|
||||||
}
|
}
|
||||||
|
|
||||||
setIdeaResponse(data.response);
|
setIdeaResponse(data.response);
|
||||||
@@ -146,10 +195,33 @@ The output MUST be in Markdown format and strictly follow this structure:
|
|||||||
setTimeout(() => setShowForm(false), 0);
|
setTimeout(() => setShowForm(false), 0);
|
||||||
generateIdeas(parsedData);
|
generateIdeas(parsedData);
|
||||||
localStorage.removeItem('formData');
|
localStorage.removeItem('formData');
|
||||||
|
=======
|
||||||
|
>>>>>>> 44b5a4dc49ea757e9790ba8309edc6b37d764105
|
||||||
}
|
}
|
||||||
}
|
setIdeaResponse(data.response);
|
||||||
}, [generateIdeas]);
|
|
||||||
|
|
||||||
|
// 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 (
|
return (
|
||||||
<main className="min-h-screen bg-gray-50 flex items-start justify-center p-4 sm:p-6 font-inter">
|
<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">
|
<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} />
|
<FormPage onFormSubmit={handleFormSubmitFromFormPage} />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
<<<<<<< HEAD
|
||||||
{generatedCards.length > 0 ? (
|
{generatedCards.length > 0 ? (
|
||||||
<SwipeCards initialCards={generatedCards} />
|
<SwipeCards initialCards={generatedCards} />
|
||||||
) : (
|
) : (
|
||||||
@@ -174,6 +247,19 @@ The output MUST be in Markdown format and strictly follow this structure:
|
|||||||
</div>
|
</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
|
<button
|
||||||
onClick={() => setShowForm(true)}
|
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"
|
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";
|
@import "tailwindcss";
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
@@ -11,9 +43,18 @@
|
|||||||
|
|
||||||
/* 3. Assign fonts to Tailwind's theme */
|
/* 3. Assign fonts to Tailwind's theme */
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
/* Tell Tailwind to use the variables already defined in app/layout.tsx */
|
/* Tell Tailwind to use the variables already defined in app/layout.tsx */
|
||||||
--font-sans: var(--font-sans);
|
--font-sans: var(--font-sans);
|
||||||
--font-header: var(--font-header);
|
--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);
|
--font-mono: var(--font-geist-mono);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
|
|||||||
// 1. Import your two new fonts
|
// 1. Import your two new fonts
|
||||||
import { Concert_One, Pixelify_Sans } from "next/font/google";
|
import { Concert_One, Pixelify_Sans } from "next/font/google";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
|
// import { Auth0Provider } from "@auth0/nextjs-auth0";
|
||||||
|
|
||||||
// 2. Set up Concert One as the body/sans font
|
// 2. Set up Concert One as the body/sans font
|
||||||
const concertOne = Concert_One({
|
const concertOne = Concert_One({
|
||||||
|
|||||||
+289
-6
@@ -1,11 +1,31 @@
|
|||||||
"use client";
|
import Image from "next/image";
|
||||||
|
|
||||||
import { useEffect } from 'react';
|
export default function Home() {
|
||||||
import { useRouter } from 'next/navigation';
|
return (
|
||||||
import Image from 'next/image';
|
<div
|
||||||
import ProtectedNavButton from "../components/ProtectedNavButton";
|
style={{ backgroundColor: "#FEEEDF" }}
|
||||||
import Link from 'next/link';
|
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() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black">
|
<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>
|
</main>
|
||||||
</div>
|
</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