Skip to content

Commit be6422e

Browse files
authored
Merge pull request #89 from CrackCode-dev/vidun-backup2
update submit button in code editor.
2 parents 0859c31 + cd25fb3 commit be6422e

11 files changed

Lines changed: 175 additions & 41 deletions

File tree

crackcode/client/src/components/codeEditor/EditorToolbar.jsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,12 @@ const EditorToolbar = () => {
6363
};
6464

6565
const response = await axios.post('/codeEditor/submit', {
66-
questionId: currentProblem?.problemId || currentProblem?._id,
66+
questionId: currentProblem?.problemId || currentProblem?.id,
6767
code: code,
68-
languageId: languageMap[language] || 71
68+
languageId: languageMap[language] || 71,
69+
sourceArea: currentProblem?.sourceArea || 'learn_page',
70+
collectionName: currentProblem?.collectionName || null,
71+
testCases: currentProblem?.testCases || []
6972
});
7073

7174
const { data } = response.data;

crackcode/client/src/components/home/ChallengesThisWeek.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ export default function ChallengesThisWeek() {
131131
onClick={() => {
132132
if (challenge.raw) {
133133
const targetId = challenge.transformed?.problemId || challenge.id
134-
navigate(`/code-editor/${targetId}`, { state: { question: challenge.raw, language: 'javascript' } })
134+
navigate(`/code-editor/${targetId}`, { state: { question: challenge.raw, language: 'javascript', sourceArea: 'weekly_challenge', collectionName: 'challengeJavascriptQ' } })
135135
}
136136
}}
137137
>

crackcode/client/src/components/home/FullWidthChallenges.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,9 @@ function FullWidthChallenges() {
112112
if (caseItem.raw && (caseItem.transformed?.problemId || caseItem.id)) {
113113
const targetId = caseItem.transformed?.problemId || caseItem.id
114114
// pass raw DB object so the editor hook can call transformProblemData
115-
navigate(`/code-editor/${targetId}`, { state: { question: caseItem.raw, language: 'python', sourceArea: 'home_challenge' } })
115+
navigate(`/code-editor/${targetId}`, { state: { question: caseItem.raw, language: 'python', sourceArea: 'home_challenge', collectionName: 'challengePythonQ' } })
116116
} else if (caseItem.transformed && caseItem.transformed.problemId) {
117-
navigate(`/code-editor/${caseItem.transformed.problemId}`, { state: { question: caseItem.transformed, language: 'python', sourceArea: 'home_challenge' } })
117+
navigate(`/code-editor/${caseItem.transformed.problemId}`, { state: { question: caseItem.transformed, language: 'python', sourceArea: 'home_challenge', collectionName: 'challengePythonQ' } })
118118
} else {
119119
// fallback: open editor without preloaded question
120120
navigate('/code-editor')

crackcode/client/src/components/home/RecommendedChallenges.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ export default function RecommendedChallenges() {
173173
<Lock className='w-3 h-3' />
174174
{challenge.timeEstimate}
175175
</div>
176-
<Button variant='secondary' size='sm' icon={ArrowRight} iconPosition='right' onClick={(e) => { e.stopPropagation(); if (challenge.raw) navigate(`/code-editor/${challenge.id}`, { state: { question: challenge.raw, language: 'cpp' } }) }}>
176+
<Button variant='secondary' size='sm' icon={ArrowRight} iconPosition='right' onClick={(e) => { e.stopPropagation(); if (challenge.raw) navigate(`/code-editor/${challenge.id}`, { state: { question: challenge.raw, language: 'cpp', sourceArea: 'recommended_challenge', collectionName: 'challengeCppQ' } }) }}>
177177
Start Challenge
178178
</Button>
179179
</div>

crackcode/client/src/features/codeEditor/hooks/useProblemData.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { useState, useEffect } from 'react';
22
import { useEditor } from '../../../context/codeEditor/EditorContext';
33
import { fetchProblemByLanguage, transformProblemData } from '../../../services/api/questionService';
44

5-
export const useProblemData = (problemId, preloadedQuestion = null, sourceArea = 'learn_page') => {
5+
export const useProblemData = (problemId, preloadedQuestion = null, sourceArea = 'learn_page', collectionName = null) => {
66
const { setCurrentProblem, setCode, setLoading, language, setLanguage, languageLocked, setLanguageLocked } = useEditor();
77
const [error, setError] = useState(null);
88

@@ -20,8 +20,9 @@ export const useProblemData = (problemId, preloadedQuestion = null, sourceArea =
2020
setLoading(true);
2121
setError(null);
2222
const transformed = transformProblemData(preloadedQuestion, language);
23-
// Add sourceArea to transformed problem for reward system
23+
// Add sourceArea and collectionName to transformed problem for reward system
2424
transformed.sourceArea = sourceArea;
25+
transformed.collectionName = collectionName;
2526
setCurrentProblem(transformed);
2627
const variant = preloadedQuestion.variants?.find((v) => v.language === language)
2728
|| preloadedQuestion.variants?.[0];

crackcode/client/src/pages/caselog/CaseLogMainPage.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,11 @@ const CaseLogMainPage = () => {
156156
if (caseItem.raw && (caseItem.transformed?.problemId || caseItem.id)) {
157157
const targetId = caseItem.transformed?.problemId || caseItem.id;
158158
navigate(`/code-editor/${targetId}`, {
159-
state: { question: caseItem.raw, language: 'python', sourceArea: 'case_log' }
159+
state: { question: caseItem.raw, language: 'python', sourceArea: 'case_log', collectionName: 'caseLog' }
160160
});
161161
} else if (caseItem.transformed && caseItem.transformed.problemId) {
162162
navigate(`/code-editor/${caseItem.transformed.problemId}`, {
163-
state: { question: caseItem.transformed, language: 'python', sourceArea: 'case_log' }
163+
state: { question: caseItem.transformed, language: 'python', sourceArea: 'case_log', collectionName: 'caseLog' }
164164
});
165165
} else {
166166
navigate('/code-editor');

crackcode/client/src/pages/codeEditor/CodeEditorPage.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ const CodeEditorContent = () => {
1111
const location = useLocation();
1212
const navigate = useNavigate();
1313
const preloadedQuestion = location.state?.question || null;
14-
const sourceArea = location.state?.sourceArea || 'learn_page'; // NEW: Get sourceArea from navigation
15-
const { error, setLanguage, setLanguageLocked } = useProblemData(problemId, preloadedQuestion, sourceArea);
14+
const sourceArea = location.state?.sourceArea || 'learn_page';
15+
const collectionName = location.state?.collectionName || null;
16+
const { error, setLanguage, setLanguageLocked } = useProblemData(problemId, preloadedQuestion, sourceArea, collectionName);
1617

1718
useEffect(() => {
1819
if (location.state?.language) {

crackcode/server/server.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,9 @@ import progressRoutes from "./src/modules/Career Map/progress/progress.routes.js
212212

213213
const app = express();
214214

215-
// If the app is running behind a proxy/load-balancer (nginx, cloud LB),
216-
// enable trust proxy so req.secure and x-forwarded-* headers are populated.
217-
app.set('trust proxy', true);
215+
// If the app is running behind a single proxy/load-balancer (nginx, cloud LB),
216+
// trust one hop so req.secure and x-forwarded-* headers are populated.
217+
app.set('trust proxy', 1);
218218

219219
const __filename = fileURLToPath(import.meta.url);
220220
const __dirname = path.dirname(__filename);
@@ -261,6 +261,7 @@ const allowedOrigins = [
261261
process.env.CLIENT_URL,
262262
...(process.env.CLIENT_URLS ? process.env.CLIENT_URLS.split(",") : []),
263263
]
264+
.filter(Boolean)
264265
.map((origin) => origin.trim())
265266
.filter(Boolean);
266267

crackcode/server/src/modules/codeEditor/codeEditor.submit.controller.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { submitSolutionService } from "./codeEditor.submit.service.js";
55
export const submitSolution = async (req, res) => {
66
try {
77
const userId = req.user?._id || req.userId;
8-
const { questionId, code, languageId } = req.body;
8+
const { questionId, code, languageId, sourceArea, testCases, collectionName } = req.body;
99

1010
// Validate inputs
1111
if (!questionId) {
@@ -36,14 +36,17 @@ export const submitSolution = async (req, res) => {
3636
});
3737
}
3838

39-
console.log(`📤 Submit called - userId: ${userId}, questionId: ${questionId}`);
39+
console.log(`📤 Submit called - userId: ${userId}, questionId: ${questionId}, sourceArea: ${sourceArea}, collectionName: ${collectionName}, testCasesProvided: ${testCases?.length || 0}`);
4040

4141
// Call submit service
4242
const result = await submitSolutionService({
4343
userId,
4444
questionId,
4545
code,
46-
languageId
46+
languageId,
47+
sourceArea: sourceArea || 'learn_page',
48+
collectionName: collectionName || null,
49+
preloadedTestCases: testCases || []
4750
});
4851

4952
// Return response to frontend

crackcode/server/src/modules/codeEditor/codeEditor.submit.service.js

Lines changed: 137 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -60,60 +60,174 @@ const findQuestionInLearnCollections = async (problemId) => {
6060
return null;
6161
};
6262

63+
// Helper: Search for question in caseLog collection
64+
const findQuestionInCaseLog = async (problemId) => {
65+
try {
66+
console.log(` ↳ Searching in caseLog collection for problemId: "${problemId}"`);
67+
68+
// Search by problemId first (in case caseLog documents have this field)
69+
let question = await mongoose.connection.db
70+
.collection('caseLog')
71+
.findOne({ problemId });
72+
73+
if (question) {
74+
console.log(` ✅ Found in caseLog collection by problemId`);
75+
return question;
76+
}
77+
78+
// Try searching by _id if it's a valid MongoDB ObjectId
79+
if (problemId.match(/^[0-9a-f]{24}$/i)) {
80+
try {
81+
console.log(` ↳ CaseLog: Trying to find by _id (ObjectId): "${problemId}"`);
82+
const objId = new mongoose.Types.ObjectId(problemId);
83+
question = await mongoose.connection.db
84+
.collection('caseLog')
85+
.findOne({ _id: objId });
86+
87+
if (question) {
88+
console.log(` ✅ Found in caseLog collection by _id (ObjectId)`);
89+
return question;
90+
}
91+
} catch (objIdErr) {
92+
console.log(` ⚠️ CaseLog ObjectId conversion failed: ${objIdErr.message}`);
93+
}
94+
95+
// Fallback: try searching with string _id
96+
try {
97+
console.log(` ↳ CaseLog: Trying to find by _id (string): "${problemId}"`);
98+
question = await mongoose.connection.db
99+
.collection('caseLog')
100+
.findOne({ _id: problemId });
101+
102+
if (question) {
103+
console.log(` ✅ Found in caseLog collection by _id (string)`);
104+
return question;
105+
}
106+
} catch (strIdErr) {
107+
console.log(` ⚠️ CaseLog string _id search failed: ${strIdErr.message}`);
108+
}
109+
}
110+
111+
console.log(` ❌ CaseLog: Document not found with problemId: "${problemId}"`);
112+
} catch (err) {
113+
console.log(` ⚠️ CaseLog collection search failed:`, err.message);
114+
}
115+
return null;
116+
};
117+
63118
// Submit solution and award rewards only on first-time completion
64119
export const submitSolutionService = async ({
65120
userId,
66121
questionId,
67122
code,
68-
languageId
123+
languageId,
124+
sourceArea = 'learn_page',
125+
collectionName = null,
126+
preloadedTestCases = []
69127
}) => {
70128
try {
71129
// Debug: Log received identifiers
72-
console.log("📝 Submit received - userId:", userId, "questionId:", questionId);
130+
console.log("📝 Submit received - userId:", userId, "questionId:", questionId, "sourceArea:", sourceArea, "collectionName:", collectionName, "preloadedTestCases:", preloadedTestCases.length);
73131

74132
// Step 1: Validate user exists
75133
const user = await User.findById(userId);
76134
if (!user) {
77135
throw new Error("User not found");
78136
}
79137

80-
// Step 2: Validate question exists search learn collections first, then main Question collection
81-
console.log(`🔍 Searching for question with problemId: "${questionId}"`);
82-
83-
// Try learn collections first (where most questions live)
84-
let question = await findQuestionInLearnCollections(questionId);
85-
86-
// If not found in learn collections, try main Question collection
138+
let testCases = [];
139+
let question = null;
140+
141+
console.log(`📊 TEST CASES ANALYSIS:`);
142+
console.log(` - preloadedTestCases.length: ${preloadedTestCases?.length || 0}`);
143+
console.log(` - preloadedTestCases: ${JSON.stringify(preloadedTestCases?.slice(0, 1))}`);
144+
console.log(` - collectionName provided: ${collectionName ? 'YES - ' + collectionName : 'NO'}`);
145+
console.log(` - sourceArea: ${sourceArea}`);
146+
147+
console.log(`🔍 Searching for question with questionId: "${questionId}"`);
148+
149+
if (collectionName) {
150+
console.log(` → Direct lookup in collection: "${collectionName}"`);
151+
console.log(` → Searching for problemId: ${questionId}`);
152+
question = await mongoose.connection.db
153+
.collection(collectionName)
154+
.findOne({ problemId: questionId });
155+
156+
console.log(` → Result from problemId search: ${question ? 'FOUND' : 'NOT FOUND'}`);
157+
158+
if (!question && questionId.match(/^[0-9a-f]{24}$/i)) {
159+
try {
160+
const objId = new mongoose.Types.ObjectId(questionId);
161+
console.log(` → Trying ObjectId search for: ${objId}`);
162+
question = await mongoose.connection.db
163+
.collection(collectionName)
164+
.findOne({ _id: objId });
165+
console.log(` → Result from ObjectId search: ${question ? 'FOUND' : 'NOT FOUND'}`);
166+
} catch (err) {
167+
console.log(` → ObjectId conversion failed: ${err.message}`);
168+
console.log(` → Trying string _id search for: ${questionId}`);
169+
question = await mongoose.connection.db
170+
.collection(collectionName)
171+
.findOne({ _id: questionId });
172+
console.log(` → Result from string _id search: ${question ? 'FOUND' : 'NOT FOUND'}`);
173+
}
174+
}
175+
176+
if (question) {
177+
console.log(` ✅ Found in collection: ${collectionName}, has test_cases: ${question.test_cases ? question.test_cases.length : 0}`);
178+
} else {
179+
console.log(` ❌ Not found in collection: ${collectionName}, attempting fallback...`);
180+
}
181+
} else {
182+
console.log(`⚠️ WARNING: collectionName is NULL! This means frontend didn't pass it.`);
183+
}
184+
185+
if (!question && sourceArea === 'case_log') {
186+
console.log(` → Fallback: Searching caseLog collection`);
187+
question = await findQuestionInCaseLog(questionId);
188+
}
189+
87190
if (!question) {
88-
console.log(` ↳ Not found in learn collections, trying main Question collection`);
191+
console.log(` → Fallback: Searching learn collections`);
192+
question = await findQuestionInLearnCollections(questionId);
193+
}
194+
195+
if (!question) {
196+
console.log(` ↳ Fallback: Trying main Question collection`);
89197
question = await Question.findOne({ problemId: questionId });
90198
if (question) {
91199
console.log(` ✅ Found in main Question collection`);
92200
}
93201
}
94-
95-
// Last resort: try finding by _id (MongoDB ID)
202+
96203
if (!question && questionId.match(/^[0-9a-f]{24}$/i)) {
97-
console.log(` ↳ Not found by problemId, trying _id: "${questionId}"`);
204+
console.log(` ↳ Fallback: Trying by MongoDB _id`);
98205
question = await Question.findById(questionId);
99206
if (question) {
100207
console.log(` ✅ Found by MongoDB _id`);
101208
}
102209
}
103-
104-
console.log(`✅ Question found:`, question ? `${question.problemId} (${question.original?.title})` : "NOT FOUND");
105-
if (!question) {
106-
throw new Error("Question not found");
210+
211+
if (preloadedTestCases && preloadedTestCases.length > 0) {
212+
console.log(`✅ Using preloaded test cases from frontend (${preloadedTestCases.length} cases)`);
213+
testCases = preloadedTestCases;
214+
} else {
215+
console.log(`⚠️ WARNING: preloadedTestCases is EMPTY or NULL. Will use question document test cases.`);
107216
}
108217

109-
// Step 3: Get test cases from question
110-
let testCases = question.test_cases || [];
111218
if (testCases.length === 0) {
112-
throw new Error("No test cases found for this question");
219+
testCases = question?.test_cases || question?.testCases || [];
113220
}
114221

222+
const questionDifficulty = question?.difficulty || null;
223+
224+
console.log(`📦 Test cases found: ${testCases.length}`);
115225
console.log(`📦 Raw test cases from DB:`, JSON.stringify(testCases.slice(0, 1), null, 2));
116226

227+
if (testCases.length === 0) {
228+
throw new Error("No test cases found for this question");
229+
}
230+
117231
// Normalize test cases: ensure they have the right field names
118232
testCases = testCases.map(tc => ({
119233
input: tc.input || tc.input_data || "",
@@ -137,7 +251,7 @@ export const submitSolutionService = async ({
137251
console.log(` Test cases:`, JSON.stringify(visibleTestCases, null, 2));
138252
// Record this attempt (creates progress entry if first time)
139253
try {
140-
await incrementAttempt(userId, questionId, 'coding', question.difficulty || null, languageName);
254+
await incrementAttempt(userId, questionId, 'coding', questionDifficulty, languageName);
141255
} catch (err) {
142256
console.warn('Could not increment attempt in progress service:', err.message);
143257
}
@@ -180,7 +294,7 @@ export const submitSolutionService = async ({
180294
console.log(`ℹ️ Question already completed by user ${userId}`);
181295
// Ensure progress record marks solved=true and has metadata
182296
try {
183-
await markQuestionSolved(userId, questionId, 'coding', 'code_editor', passedCount, visibleTestCases.length, question.difficulty || null, languageName);
297+
await markQuestionSolved(userId, questionId, 'coding', 'code_editor', passedCount, visibleTestCases.length, questionDifficulty, languageName);
184298
} catch (err) {
185299
console.warn('Could not mark question solved on alreadyCompleted path:', err.message);
186300
}
@@ -192,7 +306,7 @@ export const submitSolutionService = async ({
192306
}
193307

194308
// Step 7: First-time completion - award rewards
195-
const rewardConfig = getRewardConfig("coding", question.difficulty);
309+
const rewardConfig = getRewardConfig("coding", questionDifficulty);
196310
const earnedXP = rewardConfig.xp || 25;
197311
const earnedTokens = rewardConfig.tokens || 10;
198312

0 commit comments

Comments
 (0)