From 2a36638d715cc50923a096d3d10f32a9fc3abc17 Mon Sep 17 00:00:00 2001 From: phatgg221 <129394719+phatgg221@users.noreply.github.com> Date: Thu, 23 Jan 2025 17:19:37 +0700 Subject: [PATCH 01/36] feat(nova): set up route for challenges --- .../[wsId]/challenges/[challengeId]/page.tsx | 17 +++++++++++++++++ .../(dashboard)/[wsId]/challenges/page.tsx | 12 +++++++++--- pnpm-lock.yaml | 4 ++-- 3 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/page.tsx diff --git a/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/page.tsx b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/page.tsx new file mode 100644 index 0000000000..a4714ca89d --- /dev/null +++ b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/page.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { getChallenges } from '../challenges'; +interface Props{ + params: Promise<{ + challengeId: string; + }>; +} +export default async function page({params}: Props) { + + const {challengeId}= await params; + + return ( +
+ +
+ ) +} diff --git a/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/page.tsx b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/page.tsx index d0a5dd94d7..4dec04b914 100644 --- a/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/page.tsx +++ b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/page.tsx @@ -13,15 +13,21 @@ import { ArrowRight, Star } from 'lucide-react'; import Link from 'next/link'; import { redirect } from 'next/navigation'; -export default async function ChallengesPage() { +interface Props { + params: Promise<{ + wsId: string; + }>; +} +export default async function ChallengesPage({ params }: Props) { const database = await createClient(); const { data: { user }, } = await database.auth.getUser(); - + const { wsId } = await params; if (!user?.id) { redirect('/login'); } + const challenges = getChallenges(); return ( @@ -53,7 +59,7 @@ export default async function ChallengesPage() { + + + + + + + + ); +} diff --git a/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/layout.tsx b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/layout.tsx new file mode 100644 index 0000000000..d66de056cd --- /dev/null +++ b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/layout.tsx @@ -0,0 +1,15 @@ +import Header from './customizedHeader'; +import React from 'react'; + + +interface LayoutProps { + children: React.ReactNode; +} +export default function layout({children} : LayoutProps) { + return <> +
+
+ {children} +
+ ; +} diff --git a/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/page.tsx b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/page.tsx index a4714ca89d..bce0dd8c30 100644 --- a/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/page.tsx +++ b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/page.tsx @@ -1,17 +1,31 @@ -import React from 'react'; import { getChallenges } from '../challenges'; -interface Props{ - params: Promise<{ - challengeId: string; - }>; +import ProblemComponent from './problem-component'; +import TestCaseComponent from './test-case-component'; +import { Card } from '@repo/ui/components/ui/card'; +import React from 'react'; + +interface Props { + params: Promise<{ + challengeId: string; + }>; } -export default async function page({params}: Props) { - const {challengeId}= await params; +export default async function Page({ params }: Props) { + const { challengeId } = await params; return ( -
- +
+ {/* Left side: Problem & Test Cases */} +
+ + +
+ + {/* Right side: Chat Box / Editor */} + +

Chat Box

+

Interact with the problem here...

+
- ) + ); } diff --git a/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/problem-changer.tsx b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/problem-changer.tsx new file mode 100644 index 0000000000..3d115e4622 --- /dev/null +++ b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/problem-changer.tsx @@ -0,0 +1,16 @@ +import { Button } from '@repo/ui/components/ui/button'; +import React from 'react'; + +export default function ProblemChanger() { + return ( +
+ +
1 of 12
+ +
+ ); +} diff --git a/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/problem-component.tsx b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/problem-component.tsx new file mode 100644 index 0000000000..ca64b20995 --- /dev/null +++ b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/problem-component.tsx @@ -0,0 +1,32 @@ +import { Card, CardContent } from '@repo/ui/components/ui/card'; +import React from 'react'; + +export default function ProblemComponent() { + return ( +
+ +

Title

+

Description: The problem statement

+

Example:

+
+          {`Input: s = "hello"\nOutput: "holle"`}
+        
+

Constraints:

+
    +
  • 1 ≤ s.length ≤ 3 * 10⁵
  • +
  • 1 ≤ s.length ≤ 3 * 10⁵
  • +
  • s consists of printable ASCII characters.
  • +
  • 1 ≤ s.length ≤ 3 * 10⁵
  • +
  • s consists of printable ASCII characters.
  • +
  • 1 ≤ s.length ≤ 3 * 10⁵
  • +
  • s consists of printable ASCII characters.
  • +
  • 1 ≤ s.length ≤ 3 * 10⁵
  • +
  • s consists of printable ASCII characters.
  • +
  • 1 ≤ s.length ≤ 3 * 10⁵
  • +
  • s consists of printable ASCII characters.
  • +
  • s consists of printable ASCII characters.
  • +
+
+
+ ); +} diff --git a/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/prompt-component.tsx b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/prompt-component.tsx new file mode 100644 index 0000000000..38b142f806 --- /dev/null +++ b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/prompt-component.tsx @@ -0,0 +1,5 @@ +import React from 'react'; + +export default function PromptComponent() { + return
; +} diff --git a/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/test-case-component.tsx b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/test-case-component.tsx new file mode 100644 index 0000000000..af08e38185 --- /dev/null +++ b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/test-case-component.tsx @@ -0,0 +1,17 @@ +import { Card } from '@repo/ui/components/ui/card'; +import React from 'react'; + +export default function TestCaseComponent() { + return ( +
+ +

Test Case

+
+          In the case of business world, we have to do this to test out the
+          cases askjdnakjscnavkjn askjdn ak akjsnd jnasdknj acsjknakjnvqjnf jas
+          kdn
+        
+
+
+ ); +} diff --git a/apps/nova/src/app/[locale]/(dashboard)/[wsId]/layout.tsx b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/layout.tsx index 0a080f1372..50e8f97829 100644 --- a/apps/nova/src/app/[locale]/(dashboard)/[wsId]/layout.tsx +++ b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/layout.tsx @@ -1,30 +1,41 @@ +'use client'; + import { Sidebar } from '@/components/layout/sidebar'; import { ThemeProvider } from '@/components/theme-provider'; import { Toaster } from '@repo/ui/components/ui/toaster'; -import type { Metadata } from 'next'; import { Inter } from 'next/font/google'; +import { usePathname } from 'next/navigation'; const inter = Inter({ subsets: ['latin'] }); -export const metadata: Metadata = { - title: 'Prompt Engineering Playground', - description: 'Experiment with AI prompts and challenges', -}; - export default function RootLayout({ children, }: { children: React.ReactNode; }) { + const pathName = usePathname(); + + + const SIDEBAR_HIDDEN_ROUTES = ['/login', '/signup']; + const SIDEBAR_HIDDEN_ROUTE_PATTERNS = [ + /^\/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\/challenges\/\d+$/, // Matches UUID/challenges/{id} + ]; + + + const shouldShowSidebar = !( + SIDEBAR_HIDDEN_ROUTES.includes(pathName) || + SIDEBAR_HIDDEN_ROUTE_PATTERNS.some((pattern) => pattern.test(pathName)) + ); + return (
- + {shouldShowSidebar && }
{children}
); -} +} \ No newline at end of file From f8a3c876ba8514bf8ef1a0c924c3e5408aea7b82 Mon Sep 17 00:00:00 2001 From: phatgg221 <129394719+phatgg221@users.noreply.github.com> Date: Mon, 3 Feb 2025 15:39:27 +0700 Subject: [PATCH 03/36] update: update chatbox field --- .../[challengeId]/components/chat-panel.tsx | 9 +++ .../[challengeId]/components/prompt-form.tsx | 58 +++++++++++++++++++ .../[wsId]/challenges/[challengeId]/page.tsx | 10 +--- .../[challengeId]/prompt-component.tsx | 9 ++- 4 files changed, 77 insertions(+), 9 deletions(-) create mode 100644 apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/components/chat-panel.tsx create mode 100644 apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/components/prompt-form.tsx diff --git a/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/components/chat-panel.tsx b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/components/chat-panel.tsx new file mode 100644 index 0000000000..db09e0e9b8 --- /dev/null +++ b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/components/chat-panel.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import { Model } from '@/data/models'; +import { AIChat } from '@/types/db'; + +export default function ChatPanel() { + return
+ +
; +} diff --git a/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/components/prompt-form.tsx b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/components/prompt-form.tsx new file mode 100644 index 0000000000..359ffd50e6 --- /dev/null +++ b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/components/prompt-form.tsx @@ -0,0 +1,58 @@ +'use client'; + +import { Button } from '@repo/ui/components/ui/button'; +import { Input } from '@repo/ui/components/ui/input'; +import React, { useEffect, useRef, useState } from 'react'; + +export default function ChatBox() { + const [messages, setMessages] = useState< + { text: string; sender: 'user' | 'ai' }[] + >([]); + const [input, setInput] = useState(''); + const chatEndRef = useRef(null); + + // Auto-scroll to latest message + useEffect(() => { + chatEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }, [messages]); + + return ( +
+

Chat Box

+

+ Try to figure out the best prompt here... +

+ + {/* Chat Messages - Grows to take up available space */} +
+ {messages.map((msg, index) => ( +
+ {msg.text} +
+ ))} +
+
+ + {/* Chat Input - Fixed at the bottom */} +
+ setInput(e.target.value)} + onKeyDown={(e) => e.key === 'Enter'} + /> + +
+
+ ); +} diff --git a/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/page.tsx b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/page.tsx index bce0dd8c30..dcf103e71d 100644 --- a/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/page.tsx +++ b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/page.tsx @@ -1,7 +1,7 @@ -import { getChallenges } from '../challenges'; +// import { getChallenges } from '../challenges'; import ProblemComponent from './problem-component'; +import PromptComponent from './prompt-component'; import TestCaseComponent from './test-case-component'; -import { Card } from '@repo/ui/components/ui/card'; import React from 'react'; interface Props { @@ -21,11 +21,7 @@ export default async function Page({ params }: Props) {
- {/* Right side: Chat Box / Editor */} - -

Chat Box

-

Interact with the problem here...

-
+
); } diff --git a/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/prompt-component.tsx b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/prompt-component.tsx index 38b142f806..912cad8f59 100644 --- a/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/prompt-component.tsx +++ b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/prompt-component.tsx @@ -1,5 +1,10 @@ +import { Card } from '@repo/ui/components/ui/card'; import React from 'react'; - +import ChatBox from './components/prompt-form'; export default function PromptComponent() { - return
; + return ( + + + + ); } From b4dbe546c01ff213b7f6240290bd62bd1d873cee Mon Sep 17 00:00:00 2001 From: phatgg221 <129394719+phatgg221@users.noreply.github.com> Date: Mon, 3 Feb 2025 16:25:31 +0700 Subject: [PATCH 04/36] feat: update problem set, numbers of problems --- .../[challengeId]/customizedHeader.tsx | 11 ++- .../challenges/[challengeId]/layout.tsx | 2 +- .../[wsId]/challenges/[challengeId]/page.tsx | 60 +++++++++--- .../[challengeId]/problem-changer.tsx | 22 ++++- .../[wsId]/challenges/challenges.ts | 97 +++++++++++++++---- 5 files changed, 149 insertions(+), 43 deletions(-) diff --git a/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/customizedHeader.tsx b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/customizedHeader.tsx index 22a96a5b85..997c005b6f 100644 --- a/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/customizedHeader.tsx +++ b/apps/nova/src/app/[locale]/(dashboard)/[wsId]/challenges/[challengeId]/customizedHeader.tsx @@ -7,8 +7,13 @@ import Image from 'next/image'; import Link from 'next/link'; import React from 'react'; import { Suspense } from 'react'; - -export default function CustomizedHeader() { +interface Props { + proNum: number; + currentProblem: number; + onNext: () => void; + onPrev: () => void; + } +export default function CustomizedHeader({ proNum, currentProblem, onNext, onPrev }: Props) { return (