mirror of
https://github.com/PinkSheep27/SBU-Hackathon.git
synced 2026-10-11 00:50:27 -04:00
testing
This commit is contained in:
+83
-30
@@ -1,39 +1,74 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useRouter } from "next/navigation";
|
// import { useRouter } from "next/navigation"; // Removed Next.js import
|
||||||
import Link from 'next/link';
|
// import Link from 'next/link'; // Removed Next.js import
|
||||||
|
|
||||||
export default function Dashboard() {
|
export default function Dashboard() {
|
||||||
const router = useRouter();
|
// const router = useRouter(); // Removed Next.js hook
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
const [sideBarOpen, setSidebarOpen] = useState(true);
|
const [sideBarOpen, setSidebarOpen] = useState(true);
|
||||||
|
// 1. Add state to track sort order
|
||||||
|
const [sortOrder, setSortOrder] = useState("newest"); // 'newest' or 'oldest'
|
||||||
|
|
||||||
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) =>
|
|
||||||
|
// 2. Update sorting logic to use the sortOrder state
|
||||||
|
const sortedProjects = [...projects].sort((a, b) => {
|
||||||
|
// Get numeric timestamps to avoid TypeScript error
|
||||||
|
const dateA = new Date(a.createdAt).getTime();
|
||||||
|
const dateB = new Date(b.createdAt).getTime();
|
||||||
|
|
||||||
|
if (sortOrder === "newest") {
|
||||||
|
return dateB - dateA; // Newest first
|
||||||
|
} else {
|
||||||
|
return dateA - dateB; // Oldest first
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. Filter the *sorted* array based on the query
|
||||||
|
const filtered = sortedProjects.filter((p) =>
|
||||||
p.name.toLowerCase().includes(query.toLowerCase())
|
p.name.toLowerCase().includes(query.toLowerCase())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Helper function to handle navigation for the "Create" button
|
||||||
|
const handleCreateClick = () => {
|
||||||
|
// Use standard window navigation instead of Next.js router
|
||||||
|
// Also corrected typo from "dashbaord" to "dashboard"
|
||||||
|
window.location.href = "/dashboard/create";
|
||||||
|
};
|
||||||
|
|
||||||
|
// 4. Function to toggle the sort order
|
||||||
|
const toggleSortOrder = () => {
|
||||||
|
setSortOrder(currentOrder => (currentOrder === "newest" ? "oldest" : "newest"));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen bg-zinc-50 dark:bg-black font-sans transition-all duration-300">
|
<div
|
||||||
<aside
|
className="flex min-h-screen font-sans transition-all duration-300"
|
||||||
className={`${
|
style={{ backgroundColor: '#FEEEDF' }} // Main background
|
||||||
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`}
|
<aside
|
||||||
|
className={`${sideBarOpen ? "w-64" : "w-16"
|
||||||
|
} border-r transition-all duration-300 flex flex-col`}
|
||||||
|
style={{ backgroundColor: '#99A48A', borderColor: '#8A947E' }} // Sidebar background and border
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="flex items-center justify-between p-4 "
|
||||||
|
style={{ borderColor: '#8A947E' }} // Sidebar border
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between p-4 border-b border-zinc-200 dark:border-zinc-800">
|
|
||||||
{sideBarOpen && (
|
{sideBarOpen && (
|
||||||
<span className="text-lg font-semibold text-zinc-800 dark:text-zinc-100">
|
<span className="text-lg font-semibold text-white">
|
||||||
More
|
More
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
onClick={() => setSidebarOpen(!sideBarOpen)}
|
onClick={() => setSidebarOpen(!sideBarOpen)}
|
||||||
className="text-zinc-600 dark:text-zinc-300 hover:text-blue-500 transition-colors"
|
className="text-white hover:text-gray-200 transition-colors"
|
||||||
title="Toggle Sidebar"
|
title="Toggle Sidebar"
|
||||||
>
|
>
|
||||||
☰
|
☰
|
||||||
@@ -41,33 +76,34 @@ export default function Dashboard() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav
|
<nav
|
||||||
className={`flex flex-col p-4 gap-3 text-zinc-700 dark:text-zinc-300 transition-opacity duration-200 ${
|
className={`flex flex-col p-4 gap-3 text-zinc-700 dark:text-zinc-300 transition-opacity duration-200 ${sideBarOpen ? "opacity-100" : "opacity-0"
|
||||||
sideBarOpen ? "opacity-100" : "opacity-0"
|
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Link
|
<a
|
||||||
href="/"
|
href="/"
|
||||||
className="hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
|
className="hover:text-gray-200 transition-colors"
|
||||||
>
|
>
|
||||||
Home
|
Home
|
||||||
</Link>
|
</a>
|
||||||
<p
|
<p
|
||||||
className="hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
|
className="hover:text-gray-200 transition-colors"
|
||||||
>
|
>
|
||||||
Settings
|
Settings
|
||||||
</p>
|
</p>
|
||||||
{/* 3. Added the Logout link here */}
|
<a
|
||||||
<Link
|
|
||||||
href="/auth/logout"
|
href="/auth/logout"
|
||||||
className="hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
|
className="hover:text-gray-200 transition-colors"
|
||||||
>
|
>
|
||||||
Logout
|
Logout
|
||||||
</Link>
|
</a>
|
||||||
</nav>
|
</nav>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<div className="flex-1 flex flex-col min-w-0">
|
<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">
|
<header
|
||||||
|
className="sticky top-0 z-10 backdrop-blur-sm border-b"
|
||||||
|
style={{ backgroundColor: 'rgba(153, 164, 138, 0.9)', borderColor: '#8A947E' }} // Top bar background (semi-transparent) and border
|
||||||
|
>
|
||||||
<div className="max-w-7xl mx-auto px-6 py-4 flex items-center justify-between gap-4">
|
<div className="max-w-7xl mx-auto px-6 py-4 flex items-center justify-between gap-4">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<input
|
<input
|
||||||
@@ -75,15 +111,17 @@ export default function Dashboard() {
|
|||||||
value={query}
|
value={query}
|
||||||
onChange={(e) => setQuery(e.target.value)}
|
onChange={(e) => setQuery(e.target.value)}
|
||||||
placeholder="Search projects, boards or tasks..."
|
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"
|
className="w-full max-w-md px-4 py-2 rounded-md border text-black focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
style={{ backgroundColor: '#F7F7F7', borderColor: '#D1D1D1' }} // Search bar "off-white"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => router.push("/dashbaord/create")}
|
onClick={handleCreateClick} // Use the new handler
|
||||||
className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-md shadow-sm"
|
className="text-white px-4 py-2 rounded-md shadow-sm hover:opacity-90"
|
||||||
|
style={{ backgroundColor: '#EA627F' }} // Create button
|
||||||
>
|
>
|
||||||
Create
|
Create
|
||||||
</button>
|
</button>
|
||||||
@@ -92,25 +130,40 @@ export default function Dashboard() {
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main className="max-w-7xl w-full mx-auto px-6 py-10">
|
<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">
|
<div className="flex justify-between items-center mb-4">
|
||||||
|
<h1 className="text-2xl font-semibold text-black">
|
||||||
Project Names
|
Project Names
|
||||||
</h1>
|
</h1>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={toggleSortOrder}
|
||||||
|
className="px-3 py-1 text-sm font-medium border rounded-md"
|
||||||
|
style={{
|
||||||
|
color: '#333',
|
||||||
|
backgroundColor: '#F7F7F7', // "off-white"
|
||||||
|
borderColor: '#D1D1D1'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Sort by: {sortOrder === 'newest' ? 'Newest' : 'Oldest'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<ul className="space-y-3">
|
<ul className="space-y-3">
|
||||||
{filtered.map((project, index) => (
|
{filtered.map((project, index) => (
|
||||||
<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 border rounded-md"
|
||||||
|
style={{ backgroundColor: '#B8C0AD', color: '#222', borderColor: '#A9B29E' }} // Project boxes
|
||||||
>
|
>
|
||||||
<span>{project.name}</span>
|
<span>{project.name}</span>
|
||||||
<span className="text-zinc-500 dark:text-zinc-400 text-sm">
|
<span className="text-gray-700 text-sm">
|
||||||
Created: {project.createdAt}
|
Created: {project.createdAt}
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
{filtered.length === 0 && (
|
{filtered.length === 0 && (
|
||||||
<li className="text-zinc-500">No results found</li>
|
<li className="text-gray-600">No results found</li>
|
||||||
)}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
+12
-7
@@ -5,20 +5,25 @@ import { ChatHistory, ChatSetting } from "@/app/exportType/types";
|
|||||||
|
|
||||||
export async function POST(request: Request) {
|
export async function POST(request: Request) {
|
||||||
try {
|
try {
|
||||||
const { userMessage, history , settings } = await request.json() as {
|
const { userMessage, history, settings } = await request.json() as {
|
||||||
userMessage :string;
|
userMessage: string;
|
||||||
history : ChatHistory;
|
history: ChatHistory;
|
||||||
settings : ChatSetting;
|
settings: ChatSetting;
|
||||||
};
|
};
|
||||||
|
|
||||||
const aiResponse = await chatToGemini(userMessage , history, settings);
|
const aiResponse = await chatToGemini(userMessage, history, settings);
|
||||||
return NextResponse.json({response : aiResponse});
|
return NextResponse.json({ response: aiResponse });
|
||||||
}
|
}
|
||||||
catch(error){
|
catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
|
<<<<<<< HEAD
|
||||||
{error: "Error with the model response " + String(error)},
|
{error: "Error with the model response " + String(error)},
|
||||||
{status : 500}
|
{status : 500}
|
||||||
|
=======
|
||||||
|
{ error: "Error with the model response " },
|
||||||
|
{ status: 500 }
|
||||||
|
>>>>>>> ce9e6f34ed31b7bff736cc9ba9a1b8dcdec85f3a
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,18 +6,18 @@ import { ChatSetting } from "@/app/exportType/types";
|
|||||||
export async function POST(request: Request) {
|
export async function POST(request: Request) {
|
||||||
try {
|
try {
|
||||||
const { prompt, settings } = await request.json() as {
|
const { prompt, settings } = await request.json() as {
|
||||||
prompt :string;
|
prompt: string;
|
||||||
settings : ChatSetting;
|
settings: ChatSetting;
|
||||||
};
|
};
|
||||||
|
|
||||||
const aiResponse = await chatToGemini(prompt , [], settings);
|
const aiResponse = await chatToGemini(prompt, [], settings);
|
||||||
return NextResponse.json({response : aiResponse});
|
return NextResponse.json({ response: aiResponse });
|
||||||
}
|
}
|
||||||
catch(error){
|
catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{error: "Error with the model response "},
|
{ error: "Error with the model response " },
|
||||||
{status : 500}
|
{ status: 500 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+137
-47
@@ -1,9 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState } from "react";
|
||||||
import { motion, useMotionValue, useTransform, useAnimation } from "framer-motion";
|
import { motion, useMotionValue, useTransform } from "framer-motion";
|
||||||
import { useRouter } from "next/navigation";
|
|
||||||
|
|
||||||
|
// 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
|
// 1. Updated ColorWay interface
|
||||||
interface ColorWay {
|
interface ColorWay {
|
||||||
card: string;
|
card: string;
|
||||||
@@ -28,31 +29,126 @@ interface CardData {
|
|||||||
tracks: string[];
|
tracks: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const SwipeCards: React.FC<{ initialCards: CardData[] }> = ({ initialCards }) => {
|
const cardData: CardData[] = [
|
||||||
const [cards, setCards] = useState<CardData[]>(initialCards);
|
{
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
// Update cards state when initialCards prop changes
|
id: 1,
|
||||||
useEffect(() => {
|
|
||||||
setCards(initialCards);
|
|
||||||
}, [initialCards]);
|
|
||||||
|
|
||||||
const removeCard = () => {
|
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));
|
setCards((prev) => prev.slice(0, prev.length - 1));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAccept = (cardData: CardData) => {
|
|
||||||
removeCard();
|
|
||||||
// Redirect to chatbot with card data
|
|
||||||
router.push(`/chatbot?title=${encodeURIComponent(cardData.title)}&summary=${encodeURIComponent(cardData.summary)}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleReject = () => {
|
|
||||||
removeCard();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
setCards([]); // Reset to initial cards
|
setCards(cardData);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -74,8 +170,7 @@ const SwipeCards: React.FC<{ initialCards: CardData[] }> = ({ initialCards }) =>
|
|||||||
cardData={card}
|
cardData={card}
|
||||||
colorWay={colorWay}
|
colorWay={colorWay}
|
||||||
isFront={index === cards.length - 1}
|
isFront={index === cards.length - 1}
|
||||||
onAccept={handleAccept}
|
onSwipe={onSwipe}
|
||||||
onReject={handleReject}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
@@ -104,11 +199,10 @@ interface CardProps {
|
|||||||
cardData: CardData;
|
cardData: CardData;
|
||||||
colorWay: ColorWay;
|
colorWay: ColorWay;
|
||||||
isFront: boolean;
|
isFront: boolean;
|
||||||
onAccept: (cardData: CardData) => void;
|
onSwipe: () => void;
|
||||||
onReject: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Card: React.FC<CardProps> = ({ cardData, colorWay, isFront, onAccept, onReject }) => {
|
const Card: React.FC<CardProps> = ({ cardData, colorWay, isFront, onSwipe }) => {
|
||||||
const { title, summary, tracks } = cardData;
|
const { title, summary, tracks } = cardData;
|
||||||
|
|
||||||
const x = useMotionValue(0);
|
const x = useMotionValue(0);
|
||||||
@@ -118,30 +212,20 @@ const Card: React.FC<CardProps> = ({ cardData, colorWay, isFront, onAccept, onRe
|
|||||||
[-250, -100, 0, 100, 250],
|
[-250, -100, 0, 100, 250],
|
||||||
[0, 1, 1, 1, 0],
|
[0, 1, 1, 1, 0],
|
||||||
);
|
);
|
||||||
const controls = useAnimation();
|
|
||||||
|
|
||||||
const acceptOpacity = useTransform(x, [50, 150], [0, 1]);
|
const acceptOpacity = useTransform(x, [50, 150], [0, 1]);
|
||||||
const rejectOpacity = useTransform(x, [-150, -50], [1, 0]);
|
const rejectOpacity = useTransform(x, [-150, -50], [1, 0]);
|
||||||
const badgeRotate = useTransform(x, [-150, 150], [-15, 15]);
|
const badgeRotate = useTransform(x, [-150, 150], [-15, 15]);
|
||||||
|
|
||||||
const handleDragEnd = async (event: MouseEvent | TouchEvent | PointerEvent, info: { offset: { x: number } }) => {
|
const handleDragEnd = (event: MouseEvent | TouchEvent | PointerEvent, info: { offset: { x: number } }) => {
|
||||||
if (info.offset.x > 100) {
|
if (Math.abs(info.offset.x) > 100) {
|
||||||
// Card accepted
|
onSwipe();
|
||||||
await controls.start({ x: 1000, opacity: 0, transition: { duration: 0.1 } }); // Animate off-screen quickly
|
|
||||||
onAccept(cardData); // Then call onAccept which removes the card and redirects
|
|
||||||
} else if (info.offset.x < -100) {
|
|
||||||
// Card rejected
|
|
||||||
await controls.start({ x: -1000, opacity: 0, transition: { duration: 0.1 } }); // Animate off-screen quickly
|
|
||||||
onReject(); // Then call onReject which removes the card
|
|
||||||
} else {
|
|
||||||
// Card not swiped far enough, snap back to center
|
|
||||||
controls.start({ x: 0, rotate: 0, opacity: 1, transition: { type: "spring", stiffness: 300, damping: 30 } });
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
className="absolute flex h-[56rem] w-[42rem] flex-col justify-between overflow-hidden rounded-2xl p-6 shadow-2xl hover:cursor-grab active:cursor-grabbing"
|
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={{
|
style={{
|
||||||
x,
|
x,
|
||||||
rotate,
|
rotate,
|
||||||
@@ -151,7 +235,10 @@ const Card: React.FC<CardProps> = ({ cardData, colorWay, isFront, onAccept, onRe
|
|||||||
zIndex: isFront ? 2 : 1,
|
zIndex: isFront ? 2 : 1,
|
||||||
pointerEvents: isFront ? "auto" : "none",
|
pointerEvents: isFront ? "auto" : "none",
|
||||||
}}
|
}}
|
||||||
animate={controls}
|
animate={{
|
||||||
|
scale: isFront ? 1 : 0.95,
|
||||||
|
y: isFront ? 0 : -40,
|
||||||
|
}}
|
||||||
transition={{ type: "spring", stiffness: 300, damping: 30 }}
|
transition={{ type: "spring", stiffness: 300, damping: 30 }}
|
||||||
drag={isFront ? "x" : false}
|
drag={isFront ? "x" : false}
|
||||||
dragConstraints={{ left: 0, right: 0, top: 0, bottom: 0 }}
|
dragConstraints={{ left: 0, right: 0, top: 0, bottom: 0 }}
|
||||||
@@ -175,20 +262,24 @@ const Card: React.FC<CardProps> = ({ cardData, colorWay, isFront, onAccept, onRe
|
|||||||
<div className="relative z-0">
|
<div className="relative z-0">
|
||||||
{/* 3. Applied the specific titleColor here */}
|
{/* 3. Applied the specific titleColor here */}
|
||||||
<h2
|
<h2
|
||||||
className="mb-4 text-center text-3xl font-bold"
|
className="mb-4 text-center text-7xl font-bold"
|
||||||
style={{ color: colorWay.titleColor }}
|
style={{ color: colorWay.titleColor }}
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</h2>
|
</h2>
|
||||||
{/* This summary <p> will inherit colorWay.text from the parent */}
|
{/* This summary <p> will inherit colorWay.text from the parent */}
|
||||||
<p className="text-center text-xl font-light">{summary}</p>
|
<p className="text-center text-3xl font-light">{summary}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="relative z-0 flex flex-wrap items-center justify-center gap-2">
|
<div className="relative z-0 flex flex-wrap items-center justify-center gap-2">
|
||||||
{tracks.slice(0, 2).map((track, index) => (
|
{/* 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
|
<span
|
||||||
key={track}
|
key={track}
|
||||||
className="flex items-center justify-center rounded-full px-8 py-3 text-sm font-medium"
|
className="flex items-center justify-center rounded-full px-8 py-3 text-2xl font-medium"
|
||||||
style={{
|
style={{
|
||||||
// This logic now correctly contrasts with the main text color
|
// This logic now correctly contrasts with the main text color
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
@@ -198,7 +289,6 @@ const Card: React.FC<CardProps> = ({ cardData, colorWay, isFront, onAccept, onRe
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{track}
|
{track}
|
||||||
{index === 0 && tracks.length > 1 && ", "}
|
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+22
-6
@@ -4,17 +4,24 @@ import { useState, useEffect, useRef, useCallback } from "react";
|
|||||||
import { useSearchParams, useRouter } from "next/navigation";
|
import { useSearchParams, useRouter } from "next/navigation";
|
||||||
import ChatInput from "../utils/component/ui/ChatInput";
|
import ChatInput from "../utils/component/ui/ChatInput";
|
||||||
import MessageWindow from "../utils/component/ui/MessageWindow";
|
import MessageWindow from "../utils/component/ui/MessageWindow";
|
||||||
import { ChatHistory, ChatSetting, Message , MessageRole } from "@/app/exportType/types";
|
import { ChatHistory, ChatSetting, Message, MessageRole } from "@/app/exportType/types";
|
||||||
|
|
||||||
export default function Home(){
|
export default function Home() {
|
||||||
const [history, setHistory] = useState<ChatHistory>([])
|
const [history, setHistory] = useState<ChatHistory>([])
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const historyRef = useRef<ChatHistory>([]); // Create a ref for history
|
const historyRef = useRef<ChatHistory>([]); // Create a ref for history
|
||||||
const [settings] = useState<ChatSetting>({
|
const [settings] = useState<ChatSetting>({
|
||||||
|
<<<<<<< HEAD
|
||||||
temperature:1,
|
temperature:1,
|
||||||
model:"gemini-2.5-pro",
|
model:"gemini-2.5-pro",
|
||||||
systemInstruction:"you are a ai helper. Your goal is to expand the project idea and interact with the user on any problem or question they have with the project. You are not allow to code the project for them and stray away from your job"
|
systemInstruction:"you are a ai helper. Your goal is to expand the project idea and interact with the user on any problem or question they have with the project. You are not allow to code the project for them and stray away from your job"
|
||||||
|
|
||||||
|
=======
|
||||||
|
temperature: 1,
|
||||||
|
model: "gemini-2.5-pro",
|
||||||
|
sysTemInstructions: "you are a ai helper. Your goal is to expand the project idea and interact with the user on any problem or question they have with the project. You are not allow to code the project for them and stray away from your job"
|
||||||
|
|
||||||
|
>>>>>>> ce9e6f34ed31b7bff736cc9ba9a1b8dcdec85f3a
|
||||||
});
|
});
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
@@ -24,18 +31,22 @@ export default function Home(){
|
|||||||
historyRef.current = history; // Keep the ref updated with the latest history
|
historyRef.current = history; // Keep the ref updated with the latest history
|
||||||
}, [history]);
|
}, [history]);
|
||||||
|
|
||||||
const handleSend = useCallback(async(message:string)=>{
|
const handleSend = useCallback(async (message: string) => {
|
||||||
const newUserMessage:Message = {
|
const newUserMessage: Message = {
|
||||||
role:"user",
|
role: "user",
|
||||||
parts:[{text:message}],
|
parts: [{ text: message }],
|
||||||
};
|
};
|
||||||
|
|
||||||
setHistory(prevHistory => {
|
setHistory(prevHistory => {
|
||||||
const currentHistory = [...prevHistory, newUserMessage];
|
const currentHistory = [...prevHistory, newUserMessage];
|
||||||
|
|
||||||
const sendApiRequest = async () => {
|
const sendApiRequest = async () => {
|
||||||
|
<<<<<<< HEAD
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try{
|
try{
|
||||||
|
=======
|
||||||
|
try {
|
||||||
|
>>>>>>> ce9e6f34ed31b7bff736cc9ba9a1b8dcdec85f3a
|
||||||
const response = await fetch("/api/chat", {
|
const response = await fetch("/api/chat", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -136,6 +147,7 @@ export default function Home(){
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col py-32">
|
<div className="flex flex-col py-32">
|
||||||
|
<<<<<<< HEAD
|
||||||
<button
|
<button
|
||||||
onClick={() => router.push('/Dashboard')}
|
onClick={() => router.push('/Dashboard')}
|
||||||
className="absolute top-4 right-4 bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
|
className="absolute top-4 right-4 bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
|
||||||
@@ -144,6 +156,10 @@ export default function Home(){
|
|||||||
</button>
|
</button>
|
||||||
<MessageWindow history={history} isLoading={loading} />
|
<MessageWindow history={history} isLoading={loading} />
|
||||||
<ChatInput onSend={handleSend} onOpenSettings={() => {}} />
|
<ChatInput onSend={handleSend} onOpenSettings={() => {}} />
|
||||||
|
=======
|
||||||
|
<MessageWindow history={history} />
|
||||||
|
<ChatInput onSend={handleSend} onOpenSettings={() => { }} />
|
||||||
|
>>>>>>> ce9e6f34ed31b7bff736cc9ba9a1b8dcdec85f3a
|
||||||
</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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -4,20 +4,21 @@ export type MessagePart = {
|
|||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Message{
|
export interface Message {
|
||||||
role: MessageRole;
|
role: MessageRole;
|
||||||
parts:MessagePart[];
|
parts: MessagePart[];
|
||||||
}
|
}
|
||||||
export type ChatHistory = Message[];
|
export type ChatHistory = Message[];
|
||||||
|
|
||||||
export interface GenerateConfig{
|
export interface GenerateConfig {
|
||||||
temperature : number;
|
temperature: number;
|
||||||
topP : number;
|
topP: number;
|
||||||
responseMimeType : string;
|
responseMimeType: string;
|
||||||
|
maxOutputTokens?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChatSetting{
|
export interface ChatSetting {
|
||||||
temperature :number;
|
temperature: number;
|
||||||
model: string;
|
model: string;
|
||||||
systemInstruction: string;
|
systemInstruction: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||||||
// IMPORTANT: The GEMINI_API_KEY should be an environment variable.
|
// IMPORTANT: The GEMINI_API_KEY should be an environment variable.
|
||||||
const apiKey = process.env.GEMINI_API_KEY;
|
const apiKey = process.env.GEMINI_API_KEY;
|
||||||
|
|
||||||
if(!apiKey){
|
if (!apiKey) {
|
||||||
throw new Error("api key not found");
|
throw new Error("api key not found");
|
||||||
}
|
}
|
||||||
const ai = new GoogleGenerativeAI(apiKey);
|
const ai = new GoogleGenerativeAI(apiKey);
|
||||||
|
|||||||
+117
-21
@@ -1,23 +1,11 @@
|
|||||||
// 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>(() => {
|
|
||||||
if (typeof window !== 'undefined') {
|
|
||||||
const storedFormData = localStorage.getItem('formData');
|
|
||||||
if (storedFormData) {
|
|
||||||
const parsedData: FormData = JSON.parse(storedFormData);
|
|
||||||
return parsedData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
Hackathon: '',
|
Hackathon: '',
|
||||||
mainTheme: '',
|
mainTheme: '',
|
||||||
tracksStack: [''],
|
tracksStack: [''],
|
||||||
@@ -25,9 +13,34 @@ export default function Home() {
|
|||||||
studentName: '',
|
studentName: '',
|
||||||
skills: [{ skillName: '', proficiency: PROFICIENCY_LEVELS[1] }]
|
skills: [{ skillName: '', proficiency: PROFICIENCY_LEVELS[1] }]
|
||||||
}],
|
}],
|
||||||
};
|
});
|
||||||
});
|
|
||||||
|
|
||||||
|
// --- New Initializer Function ---
|
||||||
|
const getInitialState = () => {
|
||||||
|
if (typeof window === 'undefined') {
|
||||||
|
return {
|
||||||
|
data: getDefaultFormData(),
|
||||||
|
showForm: true,
|
||||||
|
runEffect: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const storedFormData = localStorage.getItem('formData');
|
||||||
|
if (storedFormData) {
|
||||||
|
localStorage.removeItem('formData');
|
||||||
|
return {
|
||||||
|
data: JSON.parse(storedFormData) as FormData,
|
||||||
|
showForm: false,
|
||||||
|
runEffect: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
data: getDefaultFormData(),
|
||||||
|
showForm: true,
|
||||||
|
runEffect: false
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
const [ideaResponse, setIdeaResponse] = useState<string>("");
|
const [ideaResponse, setIdeaResponse] = useState<string>("");
|
||||||
const [generatedCards, setGeneratedCards] = useState<CardData[]>([]); // New state for parsed cards
|
const [generatedCards, setGeneratedCards] = useState<CardData[]>([]); // New state for parsed cards
|
||||||
|
|
||||||
@@ -35,13 +48,22 @@ export default function Home() {
|
|||||||
temperature: 1,
|
temperature: 1,
|
||||||
model: "gemini-2.5-flash-lite",
|
model: "gemini-2.5-flash-lite",
|
||||||
systemInstruction: `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.
|
systemInstruction: `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.
|
||||||
|
>>>>>>> ce9e6f34ed31b7bff736cc9ba9a1b8dcdec85f3a
|
||||||
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.]`,
|
||||||
});
|
});
|
||||||
|
|
||||||
const parseAiResponse = (markdownString: string): CardData[] => {
|
const parseAiResponse = (markdownString: string): CardData[] => {
|
||||||
console.log("Raw AI Response:", markdownString);
|
console.log("Raw AI Response:", markdownString);
|
||||||
const ideas: CardData[] = [];
|
const ideas: CardData[] = [];
|
||||||
const ideaBlocks = markdownString.split("### ").filter(block => block.trim() !== "");
|
const ideaBlocks = markdownString.split("### ").filter(block => block.trim() !== "");
|
||||||
@@ -69,7 +91,26 @@ const parseAiResponse = (markdownString: string): CardData[] => {
|
|||||||
});
|
});
|
||||||
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 +118,9 @@ const parseAiResponse = (markdownString: string): CardData[] => {
|
|||||||
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 +132,7 @@ const parseAiResponse = (markdownString: string): CardData[] => {
|
|||||||
.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}
|
||||||
@@ -100,8 +143,10 @@ const parseAiResponse = (markdownString: string): CardData[] => {
|
|||||||
|
|
||||||
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 +161,24 @@ const parseAiResponse = (markdownString: string): CardData[] => {
|
|||||||
|
|
||||||
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 +205,33 @@ const parseAiResponse = (markdownString: string): CardData[] => {
|
|||||||
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 +239,7 @@ const parseAiResponse = (markdownString: string): CardData[] => {
|
|||||||
<FormPage onFormSubmit={handleFormSubmitFromFormPage} />
|
<FormPage onFormSubmit={handleFormSubmitFromFormPage} />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
<<<<<<< HEAD
|
||||||
{generatedCards.length > 0 ? (
|
{generatedCards.length > 0 ? (
|
||||||
<SwipeCards initialCards={generatedCards} />
|
<SwipeCards initialCards={generatedCards} />
|
||||||
) : (
|
) : (
|
||||||
@@ -174,6 +257,19 @@ const parseAiResponse = (markdownString: string): CardData[] => {
|
|||||||
</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"
|
||||||
|
|||||||
+41
-1
@@ -1,6 +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 */
|
/* 1. Import BOTH fonts in one go */
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Concert+One&family=Pixelify+Sans:wght@400;700&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Concert+One&family=Pixelify+Sans:wght@400;700&display=swap');
|
||||||
|
|
||||||
|
>>>>>>> 44b5a4dc49ea757e9790ba8309edc6b37d764105
|
||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
@@ -14,12 +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 */
|
||||||
|
--font-sans: var(--font-sans);
|
||||||
|
--font-header: var(--font-header);
|
||||||
|
=======
|
||||||
/* --font-sans is for regular text (Concert One) */
|
/* --font-sans is for regular text (Concert One) */
|
||||||
--font-sans: var(--font-concert-one);
|
--font-sans: var(--font-concert-one);
|
||||||
|
|
||||||
/* We add a new font family for headers (Pixelify Sans) */
|
/* We add a new font family for headers (Pixelify Sans) */
|
||||||
/* This also creates a 'font-header' utility class */
|
/* This also creates a 'font-header' utility class */
|
||||||
--font-header: var(--font-pixelify);
|
--font-header: var(--font-pixelify);
|
||||||
|
>>>>>>> 44b5a4dc49ea757e9790ba8309edc6b37d764105
|
||||||
|
|
||||||
--font-mono: var(--font-geist-mono);
|
--font-mono: var(--font-geist-mono);
|
||||||
}
|
}
|
||||||
@@ -40,6 +75,11 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 5. (Optional but helpful) Apply header font to h-tags by default */
|
/* 5. (Optional but helpful) Apply header font to h-tags by default */
|
||||||
h1, h2, h3, h4, h5, h6 {
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
font-family: var(--font-header);
|
font-family: var(--font-header);
|
||||||
}
|
}
|
||||||
+1
-2
@@ -1,9 +1,8 @@
|
|||||||
// app/layout.tsx
|
|
||||||
|
|
||||||
import type { Metadata } from "next";
|
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({
|
||||||
|
|||||||
+390
-91
@@ -1,5 +1,7 @@
|
|||||||
"use client";
|
import Image from "next/image";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
@@ -7,8 +9,36 @@ import ProtectedNavButton from "../components/ProtectedNavButton";
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { style } from 'framer-motion/client';
|
import { style } from 'framer-motion/client';
|
||||||
|
|
||||||
|
=======
|
||||||
|
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
|
||||||
|
|
||||||
|
src="/logo.svg"
|
||||||
|
alt="logo.js logo"
|
||||||
|
width={100}
|
||||||
|
height={20}
|
||||||
|
priority
|
||||||
|
/>
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<a href="/dashboard" className="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
|
||||||
|
Dashboard
|
||||||
|
</a>
|
||||||
|
<a href="/cards" className="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
|
||||||
|
Cards
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
>>>>>>> ce9e6f34ed31b7bff736cc9ba9a1b8dcdec85f3a
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
|
<<<<<<< HEAD
|
||||||
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">
|
||||||
<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 background-blue-300">
|
<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 background-blue-300">
|
||||||
@@ -29,101 +59,370 @@ export default function Page() {
|
|||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
=======
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black">
|
||||||
|
<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=""
|
||||||
|
src="/logo.svg"
|
||||||
|
alt="logo"
|
||||||
|
width={100}
|
||||||
|
height={20}
|
||||||
|
priority
|
||||||
|
/>
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<Link href="/auth/login" className="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
|
||||||
|
Sign in
|
||||||
|
</Link>
|
||||||
|
<Link href="/auth/logout" className="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
|
||||||
|
Log out
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
>>>>>>> ce9e6f34ed31b7bff736cc9ba9a1b8dcdec85f3a
|
||||||
|
|
||||||
<main className="flex min-h-screen w-full flex-col justify-between py-32 px-16 bg-white dark:bg-black">
|
<main className="flex min-h-screen w-full flex-col justify-between py-32 px-16 bg-white dark:bg-black">
|
||||||
<div className="flex flex-col sm:flex-row items-start justify-between gap-12 text-left w-full">
|
<div className="flex flex-col sm:flex-row items-start justify-between gap-12 text-left w-full">
|
||||||
|
|
||||||
<div className="flex-1 flex flex-col gap-5 justify-center">
|
|
||||||
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
|
|
||||||
(project name)
|
|
||||||
</h1>
|
|
||||||
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
|
|
||||||
Looking for a starting point or more instructions? Head over to{" "}
|
|
||||||
<Link
|
|
||||||
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
|
|
||||||
</Link>{" "}
|
|
||||||
or the{" "}
|
|
||||||
<Link
|
|
||||||
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
|
|
||||||
</Link>{" "}
|
|
||||||
center.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<ProtectedNavButton>Get Started</ProtectedNavButton>
|
<div className="flex-1 flex flex-col gap-5 justify-center">
|
||||||
</div>
|
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
|
||||||
|
(project name)
|
||||||
|
</h1>
|
||||||
|
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
|
||||||
|
Looking for a starting point or more instructions? Head over to{" "}
|
||||||
|
<Link
|
||||||
|
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
|
||||||
|
</Link>{" "}
|
||||||
|
or the{" "}
|
||||||
|
<Link
|
||||||
|
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
|
||||||
|
</Link>{" "}
|
||||||
|
center.
|
||||||
|
</p>
|
||||||
|
|
||||||
<div className="flex-1 flex justify-center">
|
<ProtectedNavButton>Get Started</ProtectedNavButton>
|
||||||
<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">
|
</div>
|
||||||
Image Placeholder
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<section className="mt-32 w-full flex flex-col items-start gap-12">
|
<div className="flex-1 flex justify-center">
|
||||||
<div>
|
<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">
|
||||||
<h2 className="text-[28px] font-semibold text-black dark:text-zinc-50 leading-9">
|
Image Placeholder
|
||||||
Guide:
|
</div>
|
||||||
</h2>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col sm:flex-row items-start justify-between gap-8 w-full">
|
<section className="mt-32 w-full flex flex-col items-start gap-12">
|
||||||
<div className="w-full max-w-xl">
|
<div>
|
||||||
<h2 className="text-2xl font-semibold text-black dark:text-zinc-50 mb-2">
|
<h2 className="text-[28px] font-semibold text-black dark:text-zinc-50 leading-9">
|
||||||
First Text Block
|
Guide:
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-lg text-zinc-600 dark:text-zinc-400">
|
</div>
|
||||||
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-[300px] h-[200px] bg-zinc-200 dark:bg-zinc-800 rounded-xl shadow-md flex items-center justify-center text-zinc-500">
|
|
||||||
Image Placeholder
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-col sm:flex-row items-start justify-between gap-8 w-full">
|
|
||||||
<div className="sm:w-1/2 flex justify-center items-center">
|
|
||||||
<div className="w-full max-w-[300px] h-[200px] bg-zinc-200 dark:bg-zinc-800 rounded-xl shadow-md flex items-center justify-center text-zinc-500">
|
|
||||||
Image Placeholder
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex w-full justify-end">
|
<div className="flex flex-col sm:flex-row items-start justify-between gap-8 w-full">
|
||||||
<div className="w-full max-w-xl">
|
<div className="w-full max-w-xl">
|
||||||
<h2 className="text-2xl font-semibold text-black dark:text-zinc-50 mb-2">
|
<h2 className="text-2xl font-semibold text-black dark:text-zinc-50 mb-2">
|
||||||
Second Text Block
|
First Text Block
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-lg text-zinc-600 dark:text-zinc-400">
|
<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.
|
This is the content for the first text block. You can write anything here.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="sm:w-1/2 flex justify-center items-center">
|
||||||
</div>
|
<div className="w-full max-w-[300px] h-[200px] bg-zinc-200 dark:bg-zinc-800 rounded-xl shadow-md flex items-center justify-center text-zinc-500">
|
||||||
|
Image Placeholder
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col sm:flex-row items-start justify-between gap-8 w-full">
|
<div className="flex flex-col sm:flex-row items-start justify-between gap-8 w-full">
|
||||||
<div className="w-full max-w-xl">
|
<div className="sm:w-1/2 flex justify-center items-center">
|
||||||
<h2 className="text-2xl font-semibold text-black dark:text-zinc-50 mb-2">
|
<div className="w-full max-w-[300px] h-[200px] bg-zinc-200 dark:bg-zinc-800 rounded-xl shadow-md flex items-center justify-center text-zinc-500">
|
||||||
Third Text Block
|
Image Placeholder
|
||||||
</h2>
|
</div>
|
||||||
<p className="text-lg text-zinc-600 dark:text-zinc-400">
|
</div>
|
||||||
This is the content for the third text block. You can write anything here.
|
|
||||||
</p>
|
<div className="flex w-full justify-end">
|
||||||
</div>
|
<div className="w-full max-w-xl">
|
||||||
<div className="sm:w-1/2 flex justify-center items-center">
|
<h2 className="text-2xl font-semibold text-black dark:text-zinc-50 mb-2">
|
||||||
<div className="w-full max-w-[300px] h-[200px] bg-zinc-200 dark:bg-zinc-800 rounded-xl shadow-md flex items-center justify-center text-zinc-500">
|
Second Text Block
|
||||||
Image Placeholder
|
</h2>
|
||||||
</div>
|
<p className="text-lg text-zinc-600 dark:text-zinc-400">
|
||||||
</div>
|
This is the content for the second text block. You can write anything here.
|
||||||
</div>
|
</p>
|
||||||
</section>
|
</div>
|
||||||
</main>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
|
<div className="flex flex-col sm:flex-row items-start justify-between gap-8 w-full">
|
||||||
|
<div className="w-full max-w-xl">
|
||||||
|
<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-[300px] h-[200px] bg-zinc-200 dark:bg-zinc-800 rounded-xl shadow-md flex items-center justify-center text-zinc-500">
|
||||||
|
Image Placeholder
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</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!{" "}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<button
|
||||||
|
style={{ backgroundColor: "#173C46", color: "white" }}
|
||||||
|
className="px-4 py-1.5 rounded-md shadow-md font-semibold hover:opacity-90 transition-opacity"
|
||||||
|
>
|
||||||
|
<Link href="/dashboard">Get Started</Link>
|
||||||
|
</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>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
@@ -77,8 +77,7 @@ export default function ChatInput({ onSend, onOpenSettings }: ChatInputProps) {
|
|||||||
<button
|
<button
|
||||||
className={`
|
className={`
|
||||||
p-2 rounded-full transition-all
|
p-2 rounded-full transition-all
|
||||||
${
|
${message.trim()
|
||||||
message.trim()
|
|
||||||
? "bg-purple-600 text-white hover:bg-purple-700"
|
? "bg-purple-600 text-white hover:bg-purple-700"
|
||||||
: "bg-gray-200 text-gray-400 cursor-not-allowed"
|
: "bg-gray-200 text-gray-400 cursor-not-allowed"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { useRef, useEffect } from "react";
|
import { useRef, useEffect } from "react";
|
||||||
import { ChatHistory } from "@/app/exportType/types";
|
import { ChatHistory } from "@/app/exportType/types";
|
||||||
import {User, Bot} from "lucide-react"
|
import { User, Bot } from "lucide-react"
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
interface MessageWindowProps{
|
interface MessageWindowProps{
|
||||||
history :ChatHistory;
|
history :ChatHistory;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
@@ -10,22 +11,30 @@ interface MessageWindowProps{
|
|||||||
|
|
||||||
export default function MessageWindow({history, isLoading}:MessageWindowProps){
|
export default function MessageWindow({history, isLoading}:MessageWindowProps){
|
||||||
const messageEndRef = useRef<HTMLDivVElement>(null);
|
const messageEndRef = useRef<HTMLDivVElement>(null);
|
||||||
|
=======
|
||||||
|
interface MessageWindowProps {
|
||||||
|
history: ChatHistory
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() =>{
|
export default function MessageWindow({ history }: MessageWindowProps) {
|
||||||
messageEndRef.current?.scrollIntoView({behavior:"smooth"});
|
const messageEndRef = useRef<HTMLDivElement>(null);
|
||||||
|
>>>>>>> ce9e6f34ed31b7bff736cc9ba9a1b8dcdec85f3a
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
messageEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
||||||
}, [history]);
|
}, [history]);
|
||||||
|
|
||||||
return(
|
return (
|
||||||
<div className="flex-1 p-3 overflow-y-auto">
|
<div className="flex-1 p-3 overflow-y-auto">
|
||||||
<div className="max-w-3xl mx-auto">
|
<div className="max-w-3xl mx-auto">
|
||||||
{history.map((msg,index)=>{
|
{history.map((msg, index) => {
|
||||||
const isUser = msg.role === "user"
|
const isUser = msg.role === "user"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className={`flex ${isUser ? "justify-end" : "justify-start"}`}>
|
className={`flex ${isUser ? "justify-end" : "justify-start"}`}>
|
||||||
{!isUser &&(
|
{!isUser && (
|
||||||
<div className="mr-2">
|
<div className="mr-2">
|
||||||
<div className="flex items-center justify-center w-8 h-8 rounded-full bg-gray">
|
<div className="flex items-center justify-center w-8 h-8 rounded-full bg-gray">
|
||||||
<Bot size={15} className="text-gray-700">
|
<Bot size={15} className="text-gray-700">
|
||||||
@@ -50,6 +59,7 @@ export default function MessageWindow({history, isLoading}:MessageWindowProps){
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<<<<<<< HEAD
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
@@ -69,7 +79,14 @@ export default function MessageWindow({history, isLoading}:MessageWindowProps){
|
|||||||
)}
|
)}
|
||||||
{/* Invisible element to help scroll to bottom */}
|
{/* Invisible element to help scroll to bottom */}
|
||||||
<div ref={messageEndRef} />
|
<div ref={messageEndRef} />
|
||||||
|
=======
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
{/* Invisible element to help scroll to bottom */}
|
||||||
|
<div ref={messageEndRef} />
|
||||||
</div>
|
</div>
|
||||||
|
>>>>>>> ce9e6f34ed31b7bff736cc9ba9a1b8dcdec85f3a
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
import { GoogleGenerativeAI } from "@google/generative-ai";
|
import { GoogleGenerativeAI } from "@google/generative-ai";
|
||||||
import { ChatHistory, GenerateConfig,ChatSetting } from "../exportType/types";
|
import { ChatHistory, GenerateConfig, ChatSetting } from "../exportType/types";
|
||||||
|
|
||||||
const apiKey = process.env.GEMINI_API_KEY;
|
const apiKey = process.env.GEMINI_API_KEY;
|
||||||
|
|
||||||
if(!apiKey){
|
if (!apiKey) {
|
||||||
throw new Error("api key not found");
|
throw new Error("api key not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
const genAI = new GoogleGenerativeAI(apiKey);
|
const genAI = new GoogleGenerativeAI(apiKey);
|
||||||
|
|
||||||
export async function chatToGemini(
|
export async function chatToGemini(
|
||||||
|
<<<<<<< HEAD
|
||||||
userMessage:string ,
|
userMessage:string ,
|
||||||
history:ChatHistory,
|
history:ChatHistory,
|
||||||
settings:ChatSetting
|
settings:ChatSetting
|
||||||
@@ -25,6 +26,23 @@ export async function chatToGemini(
|
|||||||
responseMimeType: "text/plain",
|
responseMimeType: "text/plain",
|
||||||
maxOutputTokens: 1900, // Increased to allow for longer responses
|
maxOutputTokens: 1900, // Increased to allow for longer responses
|
||||||
}
|
}
|
||||||
|
=======
|
||||||
|
userMessage: string,
|
||||||
|
history: ChatHistory,
|
||||||
|
settings: ChatSetting
|
||||||
|
): Promise<string> {
|
||||||
|
const model = genAI.getGenerativeModel({
|
||||||
|
model: settings.model || "gemini-2.5-flash",
|
||||||
|
systemInstruction: settings.sysTemInstructions || "you are going to create a project idea, explain it in summery first before going to detail",
|
||||||
|
});
|
||||||
|
>>>>>>> ce9e6f34ed31b7bff736cc9ba9a1b8dcdec85f3a
|
||||||
|
|
||||||
|
const generationConfig: GenerateConfig = {
|
||||||
|
temperature: settings.temperature || 1,
|
||||||
|
topP: 0.9,
|
||||||
|
responseMimeType: "text/plain",
|
||||||
|
maxOutputTokens: 1900, // Increased to allow for longer responses
|
||||||
|
}
|
||||||
|
|
||||||
const chatSession = model.startChat({
|
const chatSession = model.startChat({
|
||||||
generationConfig,
|
generationConfig,
|
||||||
@@ -35,8 +53,8 @@ export async function chatToGemini(
|
|||||||
const result = await chatSession.sendMessage(userMessage);
|
const result = await chatSession.sendMessage(userMessage);
|
||||||
return result.response.text();
|
return result.response.text();
|
||||||
}
|
}
|
||||||
catch(error){
|
catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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