Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions frontend/app/coding/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'use client';

import { useState } from 'react';
import CodeEditorPanel from '@/components/coding/CodeEditorPanel';
import { Language } from '@/lib/types/editor';

export default function CodingPage() {
const [code, setCode] = useState<string>('');
const [language, setLanguage] = useState<Language>('javascript');

const handleCodeChange = (newCode: string) => {
setCode(newCode);
console.log('Code updated:', newCode);
};

const handleLanguageChange = (newLanguage: Language) => {
setLanguage(newLanguage);
console.log('Language changed to:', newLanguage);
};

return (
<div className="min-h-screen w-full bg-slate-950">
<div className="h-screen flex flex-col p-6">
<div className="mb-4">
<h1 className="text-2xl font-bold text-white mb-2">Code Challenge</h1>
<p className="text-slate-400">Write your solution below</p>
</div>

<div className="flex-1 overflow-hidden">
<CodeEditorPanel
initialCode={code || undefined}
language={language}
onCodeChange={handleCodeChange}
onLanguageChange={handleLanguageChange}
/>
</div>

<div className="mt-4 flex gap-3">
<button className="px-6 py-2 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 transition-colors">
Submit
</button>
<button className="px-6 py-2 bg-slate-700 text-white rounded-lg font-medium hover:bg-slate-600 transition-colors">
Reset
</button>
</div>
</div>
</div>
);
}
52 changes: 52 additions & 0 deletions frontend/components/coding/CodeEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import Editor from '@monaco-editor/react';
import { Language } from '@/lib/types/editor';
import { MONACO_LANGUAGE_MAP } from '@/lib/constants/codeTemplates';

interface CodeEditorProps {
code: string;
language: Language;
onChange: (value: string | undefined) => void;
readOnly?: boolean;
}

export default function CodeEditor({
code,
language,
onChange,
readOnly = false,
}: CodeEditorProps) {
return (
<div className="h-full w-full">
<Editor
height="100%"
language={MONACO_LANGUAGE_MAP[language]}
value={code}
onChange={onChange}
theme="vs-dark"
options={{
minimap: { enabled: false },
fontSize: 14,
lineNumbers: 'on',
scrollBeyondLastLine: false,
automaticLayout: true,
tabSize: 2,
wordWrap: 'on',
readOnly,
quickSuggestions: true,
suggestOnTriggerCharacters: true,
acceptSuggestionOnEnter: 'on',
padding: { top: 16, bottom: 16 },
lineDecorationsWidth: 0,
lineNumbersMinChars: 3,
glyphMargin: false,
folding: true,
renderLineHighlight: 'all',
scrollbar: {
verticalScrollbarSize: 10,
horizontalScrollbarSize: 10,
},
}}
/>
</div>
);
}
130 changes: 130 additions & 0 deletions frontend/components/coding/CodeEditorPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
'use client';

import { useState, useEffect } from 'react';
import { Code, Maximize2, ChevronUp } from 'lucide-react';
import { Language } from '@/lib/types/editor';
import { CODE_TEMPLATES } from '@/lib/constants/codeTemplates';
import LanguageSelector from './LanguageSelector';
import CodeEditor from './CodeEditor';

interface CodeEditorPanelProps {
initialCode?: string;
language?: Language;
onCodeChange?: (code: string) => void;
onLanguageChange?: (language: Language) => void;
readOnly?: boolean;
}

export default function CodeEditorPanel({
initialCode,
language: initialLanguage = 'javascript',
onCodeChange,
onLanguageChange,
readOnly = false,
}: CodeEditorPanelProps) {
const [language, setLanguage] = useState<Language>(initialLanguage);
const [code, setCode] = useState<string>(
initialCode || CODE_TEMPLATES[initialLanguage]
);
const [autoComplete, setAutoComplete] = useState(true);
const [isSaved, setIsSaved] = useState(true);

useEffect(() => {
if (initialCode !== undefined) {
setCode(initialCode);
}
}, [initialCode]);

useEffect(() => {
if (initialLanguage) {
setLanguage(initialLanguage);
}
}, [initialLanguage]);

const handleLanguageChange = (newLanguage: Language) => {
setLanguage(newLanguage);
setCode(CODE_TEMPLATES[newLanguage]);
setIsSaved(false);
onLanguageChange?.(newLanguage);
};

const handleCodeChange = (value: string | undefined) => {
const newCode = value || '';
setCode(newCode);
setIsSaved(false);
onCodeChange?.(newCode);

setTimeout(() => {
setIsSaved(true);
}, 1000);
};

return (
<div className="flex flex-col h-full bg-[#0f1e35] rounded-lg overflow-hidden">
<div className="flex items-center justify-between px-4 py-3 bg-[#1a2d4a] border-b border-[#2a3f5f]">
<div className="flex items-center gap-2">
<Code size={18} className="text-blue-400" />
<h2 className="text-white font-semibold">Code</h2>
</div>
<div className="flex items-center gap-3">
<Maximize2 size={16} className="text-gray-400 cursor-pointer hover:text-white transition-colors" />
<ChevronUp size={16} className="text-gray-400 cursor-pointer hover:text-white transition-colors" />
</div>
</div>

<div className="flex items-center justify-between px-4 py-3 bg-[#0f1e35] border-b border-[#1a2d4a]">
<LanguageSelector
selectedLanguage={language}
onLanguageChange={handleLanguageChange}
/>
<div className="flex items-center gap-4">
<label className="flex items-center gap-2 cursor-pointer">
<span className="text-sm text-gray-300">Auto Complete</span>
<button
onClick={() => setAutoComplete(!autoComplete)}
className={`relative w-11 h-6 rounded-full transition-colors ${
autoComplete ? 'bg-blue-500' : 'bg-gray-600'
}`}
>
<span
className={`absolute top-1 left-1 w-4 h-4 bg-white rounded-full transition-transform ${
autoComplete ? 'translate-x-5' : 'translate-x-0'
}`}
/>
</button>
</label>
<button className="p-1.5 hover:bg-[#1a2d4a] rounded transition-colors">
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
className="text-gray-400"
>
<path
d="M13 8a5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5 5 5 0 0 1 5 5z"
stroke="currentColor"
strokeWidth="1.5"
/>
</svg>
</button>
</div>
</div>

<div className="flex-1 overflow-hidden">
<CodeEditor
code={code}
language={language}
onChange={handleCodeChange}
readOnly={readOnly}
/>
</div>

<div className="px-4 py-2 bg-[#0f1e35] border-t border-[#1a2d4a]">
<span className="text-xs text-gray-400">
{isSaved ? 'Saved' : 'Saving...'}
</span>
</div>
</div>
);
}
35 changes: 35 additions & 0 deletions frontend/components/coding/LanguageSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { ChevronDown } from 'lucide-react';
import { Language } from '@/lib/types/editor';
import { LANGUAGE_LABELS } from '@/lib/constants/codeTemplates';

interface LanguageSelectorProps {
selectedLanguage: Language;
onLanguageChange: (language: Language) => void;
}

const languages: Language[] = ['javascript', 'python', 'cpp', 'csharp'];

export default function LanguageSelector({
selectedLanguage,
onLanguageChange,
}: LanguageSelectorProps) {
return (
<div className="relative">
<select
value={selectedLanguage}
onChange={(e) => onLanguageChange(e.target.value as Language)}
className="appearance-none bg-[#1e3a5f] text-white px-4 py-2 pr-10 rounded-md cursor-pointer hover:bg-[#2a4770] transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm font-medium"
>
{languages.map((lang) => (
<option key={lang} value={lang}>
{LANGUAGE_LABELS[lang]}
</option>
))}
</select>
<ChevronDown
className="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none text-white"
size={16}
/>
</div>
);
}
35 changes: 35 additions & 0 deletions frontend/lib/constants/codeTemplates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Language } from '../types/editor';

export const CODE_TEMPLATES: Record<Language, string> = {
javascript: `function reverseWords(sentence) {
// your code here
}`,
python: `def reverse_words(sentence):
# your code here
pass`,
cpp: `#include <string>
using namespace std;

string reverseWords(string sentence) {
// your code here
}`,
csharp: `public class Solution {
public string ReverseWords(string sentence) {
// your code here
}
}`,
};

export const LANGUAGE_LABELS: Record<Language, string> = {
javascript: 'JavaScript',
python: 'Python',
cpp: 'C++',
csharp: 'C#',
};

export const MONACO_LANGUAGE_MAP: Record<Language, string> = {
javascript: 'javascript',
python: 'python',
cpp: 'cpp',
csharp: 'csharp',
};
19 changes: 19 additions & 0 deletions frontend/lib/types/editor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export type Language = 'javascript' | 'python' | 'cpp' | 'csharp';

export interface EditorState {
code: string;
language: Language;
}

export interface CodeEditorProps {
initialCode?: string;
language?: Language;
onCodeChange?: (code: string) => void;
onLanguageChange?: (language: Language) => void;
readOnly?: boolean;
}

export interface LanguageSelectorProps {
selectedLanguage: Language;
onLanguageChange: (language: Language) => void;
}
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"lint": "eslint"
},
"dependencies": {
"@monaco-editor/react": "^4.7.0",
"@radix-ui/react-avatar": "^1.1.11",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-tabs": "^1.1.13",
Expand Down
Loading