From 24b929168c90832364a7119195f5842991880417 Mon Sep 17 00:00:00 2001 From: Greg Konush <12027037+gregkonush@users.noreply.github.com> Date: Sun, 1 Dec 2024 00:14:38 -0800 Subject: [PATCH] feat: add practice action button --- pnpm-lock.yaml | 41 +-- services/ecran/package.json | 1 + services/ecran/src/app/layout.tsx | 26 +- services/ecran/src/app/practice/page.tsx | 46 +-- services/ecran/src/app/problems/columns.tsx | 21 ++ .../ecran/src/components/feedback-button.tsx | 10 +- .../ecran/src/components/practice-view.tsx | 303 ++++++++++++------ 7 files changed, 262 insertions(+), 186 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f85f2cc9..22199f6c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -113,6 +113,9 @@ importers: '@tanstack/react-table': specifier: 8.20.5 version: 8.20.5(react-dom@19.0.0-rc-7670501b-20241124(react@19.0.0-rc-7670501b-20241124))(react@19.0.0-rc-7670501b-20241124) + '@tanstack/react-virtual': + specifier: 3.10.9 + version: 3.10.9(react-dom@19.0.0-rc-7670501b-20241124(react@19.0.0-rc-7670501b-20241124))(react@19.0.0-rc-7670501b-20241124) '@temporalio/activity': specifier: 1.11.5 version: 1.11.5 @@ -2270,8 +2273,8 @@ packages: react: '>=16.8' react-dom: '>=16.8' - '@tanstack/react-virtual@3.10.8': - resolution: {integrity: sha512-VbzbVGSsZlQktyLrP5nxE+vE1ZR+U0NFAWPbJLoG2+DKPwd2D7dVICTVIIaYlJqX1ZCEnYDbaOpmMwbsyhBoIA==} + '@tanstack/react-virtual@3.10.9': + resolution: {integrity: sha512-OXO2uBjFqA4Ibr2O3y0YMnkrRWGVNqcvHQXmGvMu6IK8chZl3PrDxFXdGZ2iZkSrKh3/qUYoFqYe+Rx23RoU0g==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -2280,8 +2283,8 @@ packages: resolution: {integrity: sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg==} engines: {node: '>=12'} - '@tanstack/virtual-core@3.10.8': - resolution: {integrity: sha512-PBu00mtt95jbKFi6Llk9aik8bnR3tR/oQP1o3TSi+iG//+Q2RTIzCEgKkHG8BB86kxMNW6O8wku+Lmi+QFR6jA==} + '@tanstack/virtual-core@3.10.9': + resolution: {integrity: sha512-kBknKOKzmeR7lN+vSadaKWXaLS0SZZG+oqpQ/k80Q6g9REn6zRHS/ZYdrIzHnpHgy/eWs00SujveUN/GJT2qTw==} '@temporalio/activity@1.11.5': resolution: {integrity: sha512-IHvaADrKhxYyhuybnMt5JX3rxK94DstENwO2jq5qjsS2p5jr1qjU72pRDUSXAha1endqgsyjqIyxqrEOsqzuyg==} @@ -6539,7 +6542,7 @@ snapshots: '@floating-ui/react': 0.26.24(react-dom@19.0.0-rc-7670501b-20241124(react@19.0.0-rc-7670501b-20241124))(react@19.0.0-rc-7670501b-20241124) '@react-aria/focus': 3.18.2(react@19.0.0-rc-7670501b-20241124) '@react-aria/interactions': 3.22.2(react@19.0.0-rc-7670501b-20241124) - '@tanstack/react-virtual': 3.10.8(react-dom@19.0.0-rc-7670501b-20241124(react@19.0.0-rc-7670501b-20241124))(react@19.0.0-rc-7670501b-20241124) + '@tanstack/react-virtual': 3.10.9(react-dom@19.0.0-rc-7670501b-20241124(react@19.0.0-rc-7670501b-20241124))(react@19.0.0-rc-7670501b-20241124) react: 19.0.0-rc-7670501b-20241124 react-dom: 19.0.0-rc-7670501b-20241124(react@19.0.0-rc-7670501b-20241124) @@ -7646,15 +7649,15 @@ snapshots: react: 19.0.0-rc-7670501b-20241124 react-dom: 19.0.0-rc-7670501b-20241124(react@19.0.0-rc-7670501b-20241124) - '@tanstack/react-virtual@3.10.8(react-dom@19.0.0-rc-7670501b-20241124(react@19.0.0-rc-7670501b-20241124))(react@19.0.0-rc-7670501b-20241124)': + '@tanstack/react-virtual@3.10.9(react-dom@19.0.0-rc-7670501b-20241124(react@19.0.0-rc-7670501b-20241124))(react@19.0.0-rc-7670501b-20241124)': dependencies: - '@tanstack/virtual-core': 3.10.8 + '@tanstack/virtual-core': 3.10.9 react: 19.0.0-rc-7670501b-20241124 react-dom: 19.0.0-rc-7670501b-20241124(react@19.0.0-rc-7670501b-20241124) '@tanstack/table-core@8.20.5': {} - '@tanstack/virtual-core@3.10.8': {} + '@tanstack/virtual-core@3.10.9': {} '@temporalio/activity@1.11.5': dependencies: @@ -8945,8 +8948,8 @@ snapshots: '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.7.2) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.0) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.10.0(eslint@8.57.0) eslint-plugin-react: 7.36.1(eslint@8.57.0) eslint-plugin-react-hooks: 5.0.0(eslint@8.57.0) @@ -8965,48 +8968,48 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.0): + eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint@8.57.0): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.3.7 enhanced-resolve: 5.17.1 eslint: 8.57.0 - eslint-module-utils: 2.11.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.0))(eslint@8.57.0) + eslint-module-utils: 2.11.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.8.1 is-bun-module: 1.2.1 is-glob: 4.0.3 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) transitivePeerDependencies: - '@typescript-eslint/parser' - eslint-import-resolver-node - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.11.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.0))(eslint@8.57.0): + eslint-module-utils@2.11.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.7.2) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint@8.57.0) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.0))(eslint@8.57.0): + eslint-module-utils@2.12.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.7.2) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint@8.57.0) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -9017,7 +9020,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.0))(eslint@8.57.0) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 diff --git a/services/ecran/package.json b/services/ecran/package.json index 34218033..d97338de 100644 --- a/services/ecran/package.json +++ b/services/ecran/package.json @@ -55,6 +55,7 @@ "@tanstack/react-query": "5.62.0", "@tanstack/react-query-devtools": "5.62.0", "@tanstack/react-table": "8.20.5", + "@tanstack/react-virtual": "3.10.9", "@temporalio/activity": "1.11.5", "@temporalio/client": "1.11.5", "@temporalio/worker": "1.11.5", diff --git a/services/ecran/src/app/layout.tsx b/services/ecran/src/app/layout.tsx index f60470a5..7f4fa313 100644 --- a/services/ecran/src/app/layout.tsx +++ b/services/ecran/src/app/layout.tsx @@ -78,45 +78,45 @@ export default async function RootLayout({ children }: { children: React.ReactNo -
+
{children}
diff --git a/services/ecran/src/app/practice/page.tsx b/services/ecran/src/app/practice/page.tsx index 0e898e68..0db12df4 100644 --- a/services/ecran/src/app/practice/page.tsx +++ b/services/ecran/src/app/practice/page.tsx @@ -1,6 +1,6 @@ 'use client' -import { useReducer, useCallback, useEffect, useMemo } from 'react' +import { useReducer, useCallback, useEffect } from 'react' import { useQuery } from '@tanstack/react-query' import { useRouter, useSearchParams } from 'next/navigation' import PracticeView from '@/components/practice-view' @@ -36,38 +36,6 @@ def greet(name): print(greet("World")) `.trim() -const defaultProblem = - `Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target. - -You may assume that each input would have exactly one solution, and you may not use the same element twice. - -You can return the answer in any order. - - - -Example 1: - -Input: nums = [2,7,11,15], target = 9 -Output: [0,1] -Explanation: Because nums[0] + nums[1] == 9, we return [0, 1]. -Example 2: - -Input: nums = [3,2,4], target = 6 -Output: [1,2] -Example 3: - -Input: nums = [3,3], target = 6 -Output: [0,1] - - -Constraints: - -2 <= nums.length <= 104 --109 <= nums[i] <= 109 --109 <= target <= 109 -Only one valid answer exists. -`.trim() - type State = { code: string language: string @@ -76,7 +44,6 @@ type State = { isHovered: boolean hint: string isLoadingHint: boolean - activeTab: string selectedProblem: Problem | null error: string | null } @@ -89,7 +56,6 @@ type Action = | { type: 'SET_IS_HOVERED'; payload: boolean } | { type: 'SET_HINT'; payload: string } | { type: 'SET_IS_LOADING_HINT'; payload: boolean } - | { type: 'SET_ACTIVE_TAB'; payload: string } | { type: 'SET_SELECTED_PROBLEM'; payload: Problem | null } | { type: 'SET_ERROR'; payload: string | null } @@ -101,7 +67,6 @@ const initialState: State = { isHovered: false, hint: '', isLoadingHint: false, - activeTab: 'description', selectedProblem: null, error: null, } @@ -122,8 +87,6 @@ function reducer(state: State, action: Action): State { return { ...state, hint: action.payload } case 'SET_IS_LOADING_HINT': return { ...state, isLoadingHint: action.payload } - case 'SET_ACTIVE_TAB': - return { ...state, activeTab: action.payload } case 'SET_SELECTED_PROBLEM': return { ...state, selectedProblem: action.payload } case 'SET_ERROR': @@ -237,7 +200,6 @@ export default function PracticeContainer() { const problem = problemId ? problems.find((p: Problem) => p.id === problemId) || problems[0] : problems[0] dispatch({ type: 'SET_SELECTED_PROBLEM', payload: problem }) - dispatch({ type: 'SET_ACTIVE_TAB', payload: 'description' }) dispatch({ type: 'SET_CODE', payload: problem.codeTemplates[state.language] || getDefaultCode(state.language), @@ -248,7 +210,6 @@ export default function PracticeContainer() { const handleProblemChange = useCallback( (problem: Problem) => { dispatch({ type: 'SET_SELECTED_PROBLEM', payload: problem }) - dispatch({ type: 'SET_ACTIVE_TAB', payload: 'description' }) dispatch({ type: 'SET_CODE', payload: problem.codeTemplates[state.language] || getDefaultCode(state.language), @@ -266,10 +227,6 @@ export default function PracticeContainer() { dispatch({ type: 'SET_IS_HOVERED', payload: false }) }, []) - const onActiveTabChange = useCallback((tab: string) => { - dispatch({ type: 'SET_ACTIVE_TAB', payload: tab }) - }, []) - return ( ) } diff --git a/services/ecran/src/app/problems/columns.tsx b/services/ecran/src/app/problems/columns.tsx index ad427d88..d72781ec 100644 --- a/services/ecran/src/app/problems/columns.tsx +++ b/services/ecran/src/app/problems/columns.tsx @@ -3,6 +3,8 @@ import type { ColumnDef } from '@tanstack/react-table' import Link from 'next/link' import { Badge } from '@/components/ui/badge' +import { Button } from '@/components/ui/button' +import { Play } from 'lucide-react' import { cn } from '@/lib/utils' import type { Problem } from './types' @@ -59,4 +61,23 @@ export const columns: ColumnDef[] = [ ) }, }, + { + id: 'actions', + cell: ({ row }) => { + const { id } = row.original + return ( + + ) + }, + }, ] diff --git a/services/ecran/src/components/feedback-button.tsx b/services/ecran/src/components/feedback-button.tsx index 9f8c6994..06481578 100644 --- a/services/ecran/src/components/feedback-button.tsx +++ b/services/ecran/src/components/feedback-button.tsx @@ -15,16 +15,16 @@ export default function FeedbackButton({ userId }: { userId?: string }) { - + Provide Feedback diff --git a/services/ecran/src/components/practice-view.tsx b/services/ecran/src/components/practice-view.tsx index 263fc00d..d800cca6 100644 --- a/services/ecran/src/components/practice-view.tsx +++ b/services/ecran/src/components/practice-view.tsx @@ -1,8 +1,9 @@ -import React, { useMemo, useCallback } from 'react' +import React, { useMemo, useCallback, useState } from 'react' import { Button } from '@/components/ui/button' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card' -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' +import { Sheet, SheetContent, SheetTrigger, SheetTitle } from '@/components/ui/sheet' +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip' import Editor from '@/components/editor' import { motion } from 'framer-motion' import { cn } from '@/lib/utils' @@ -10,8 +11,123 @@ import { ScrollArea, ScrollBar } from '@/components/ui/scroll-area' import { Separator } from '@/components/ui/separator' import { LoadingDots } from '@/components/loading-dots' import { HintSkeleton } from '@/components/hint-skeleton' +import { Menu } from 'lucide-react' +import { Badge } from '@/components/ui/badge' +import { useVirtualizer } from '@tanstack/react-virtual' import type { Problem } from '@/app/problems/types' +const ITEM_HEIGHT = 42 // Height of each item including separator + +const ProblemItem = React.memo( + ({ problem, isSelected, onClick }: { problem: Problem; isSelected: boolean; onClick: () => void }) => ( +
+ + +
+ ), +) +ProblemItem.displayName = 'ProblemItem' + +const VirtualizedProblemsList = React.memo( + ({ + problems, + selectedProblem, + onProblemChange, + onClose, + }: { + problems: Problem[] + selectedProblem: Problem | null + onProblemChange: (problem: Problem) => void + onClose: () => void + }) => { + const parentRef = React.useRef(null) + + const virtualizer = useVirtualizer({ + count: problems.length, + getScrollElement: () => parentRef.current, + estimateSize: () => ITEM_HEIGHT, + overscan: 5, + }) + + const handleProblemSelect = useCallback( + (problem: Problem) => { + onProblemChange(problem) + onClose() + }, + [onProblemChange, onClose], + ) + + return ( +
+
+ {virtualizer.getVirtualItems().map((virtualItem) => { + const problem = problems[virtualItem.index] + if (!problem) return null + + return ( +
+ handleProblemSelect(problem)} + /> +
+ ) + })} +
+
+ ) + }, +) +VirtualizedProblemsList.displayName = 'VirtualizedProblemsList' + type PracticeViewProps = { code: string language: string @@ -20,7 +136,6 @@ type PracticeViewProps = { isHovered: boolean hint: string isLoadingHint: boolean - activeTab: string selectedProblem: Problem | null problems: Problem[] isLoadingProblems: boolean @@ -31,7 +146,6 @@ type PracticeViewProps = { onProblemChange: (problem: Problem) => void onHoverStart: () => void onHoverEnd: () => void - onActiveTabChange: (tab: string) => void } const sparkleVariants = { @@ -52,44 +166,6 @@ const sparkleVariants = { }, } -const ProblemsList = React.memo( - ({ - problems, - selectedProblem, - onProblemChange, - }: { - problems: Problem[] - selectedProblem: Problem | null - onProblemChange: (problem: Problem) => void - }) => { - return ( - -
-
    - {problems.map((problem: Problem, index: number) => ( -
  • - - {index < problems.length - 1 && } -
  • - ))} -
-
- -
- ) - }, -) -ProblemsList.displayName = 'ProblemsList' - export default function PracticeView({ code, language, @@ -98,7 +174,6 @@ export default function PracticeView({ isHovered, hint, isLoadingHint, - activeTab, selectedProblem, problems, isLoadingProblems, @@ -109,8 +184,9 @@ export default function PracticeView({ onProblemChange, onHoverStart, onHoverEnd, - onActiveTabChange, }: PracticeViewProps) { + const [isOpen, setIsOpen] = useState(false) + const description = useMemo(() => { if (selectedProblem?.description) { return selectedProblem.description @@ -129,30 +205,31 @@ export default function PracticeView({ onHint() }, [onHint]) + const handleClose = useCallback(() => { + setIsOpen(false) + }, []) + return (
-
-
-

Practice

-
-
-
+
+
+
- + + + + + + +
+
+ Problems +
+
+ {isLoadingProblems ? ( +
+ +
+ ) : ( + + )} +
+
+
+
-
-
-
-
+
+
-
- - - Problem Description - Select Problem - - - - -
- {selectedProblem?.descriptionHtml ? ( -
- dangerouslySetInnerHTML={{ __html: selectedProblem.descriptionHtml }} - /> - ) : ( -
{description}
- )} -
- - - - - {isLoadingProblems ? ( -
- -
- ) : ( - - )} -
- +
+
+ {selectedProblem && ( +
+

{selectedProblem.title}

+ +
+ )} + +
+ {selectedProblem?.descriptionHtml ? ( +
+ dangerouslySetInnerHTML={{ __html: selectedProblem.descriptionHtml }} + /> + ) : ( +
+ {description} +
+ )} +
+ + +
Executing code...

) : output ? ( -
{output}
+
{output}
) : (

Click execute to see the output.

)}