Skip to content

Commit c60c796

Browse files
committed
add the get certificate button
1 parent 9477404 commit c60c796

File tree

1 file changed

+150
-81
lines changed

1 file changed

+150
-81
lines changed

app/components/CodeEditor/CodeEditor.tsx

Lines changed: 150 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import styles from "./CodeEditor.module.css";
44
import ctx from "classnames";
55
import { GeistMono } from "geist/font/mono";
6-
import Editor from "@monaco-editor/react";
6+
import Editor, { Monaco } from "@monaco-editor/react";
77
import { Flex, useColorMode } from "@chakra-ui/react";
88
import { useEffect, useState, useRef } from "react";
99
import MyBtn from "../MyBtn";
@@ -14,34 +14,10 @@ import { useUserSolutionStore, useEditorStore } from "@/lib/stores";
1414
import { sendGAEvent } from "@next/third-parties/google";
1515
import { CodeFile, OutputResult } from "@/lib/types";
1616
import { OutputReducerAction } from "@/lib/reducers";
17+
import CertificateButton from "../CertificateButton/CertificateButton";
1718

18-
export default function CodeEditor({
19-
codeString,
20-
setCodeString,
21-
codeFile,
22-
dispatchOutput,
23-
nextStepPath,
24-
stepIndex,
25-
chapterIndex,
26-
outputResult,
27-
}: {
28-
codeString: string;
29-
setCodeString: (codeString: string) => void;
30-
codeFile: CodeFile;
31-
dispatchOutput: React.Dispatch<OutputReducerAction>;
32-
nextStepPath: string | undefined;
33-
stepIndex: number;
34-
chapterIndex: number;
35-
outputResult: OutputResult;
36-
}) {
37-
const { colorMode } = useColorMode();
38-
const [monaco, setMonaco] = useState<any>(null);
39-
const [isValidating, setIsValidating] = useState(false);
40-
const router = useRouter();
41-
const editorStore = useEditorStore();
42-
const userSolutionStore = useUserSolutionStore();
43-
const editorRef = useRef<any>(null);
44-
19+
// Custom hook for editor theme setup
20+
const useEditorTheme = (monaco: Monaco, colorMode: "dark" | "light") => {
4521
useEffect(() => {
4622
if (monaco) {
4723
monaco.editor.defineTheme("my-theme", {
@@ -55,10 +31,16 @@ export default function CodeEditor({
5531
monaco.editor.setTheme(colorMode === "light" ? "light" : "my-theme");
5632
}
5733
}, [monaco, colorMode]);
34+
};
5835

36+
// Custom hook for keyboard shortcuts
37+
const useValidationShortcut = (
38+
handleValidate: () => void,
39+
codeString: string,
40+
) => {
5941
useEffect(() => {
60-
const handleKeyDown = (event) => {
61-
if (event.key == "Enter" && event.shiftKey) {
42+
const handleKeyDown = (event: KeyboardEvent) => {
43+
if (event.key === "Enter" && event.shiftKey) {
6244
sendGAEvent("event", "buttonClicked", {
6345
value: "Validate (through shortcut)",
6446
});
@@ -71,8 +53,20 @@ export default function CodeEditor({
7153
return () => {
7254
document.removeEventListener("keydown", handleKeyDown);
7355
};
74-
}, [codeString]);
56+
}, [handleValidate, codeString]);
57+
};
7558

59+
// Custom hook for code persistence
60+
const useCodePersistence = (
61+
chapterIndex: number,
62+
stepIndex: number,
63+
codeString: string,
64+
setCodeString: (value: string) => void,
65+
codeFile: CodeFile,
66+
) => {
67+
const userSolutionStore = useUserSolutionStore();
68+
69+
// Load saved code
7670
useEffect(() => {
7771
const savedCode = userSolutionStore.getSavedUserSolutionByLesson(
7872
chapterIndex,
@@ -83,6 +77,7 @@ export default function CodeEditor({
8377
}
8478
}, [chapterIndex, stepIndex]);
8579

80+
// Save code changes
8681
useEffect(() => {
8782
userSolutionStore.saveUserSolutionForLesson(
8883
chapterIndex,
@@ -91,11 +86,104 @@ export default function CodeEditor({
9186
);
9287
}, [codeString, chapterIndex, stepIndex]);
9388

89+
// Initialize code if no saved solutions
9490
useEffect(() => {
95-
if (Object.keys(userSolutionStore.userSolutionsByLesson).length == 0) {
91+
if (Object.keys(userSolutionStore.userSolutionsByLesson).length === 0) {
9692
setCodeString(JSON.stringify(codeFile.code, null, 2));
9793
}
9894
}, [userSolutionStore]);
95+
};
96+
97+
// EditorControls component for the buttons section
98+
const EditorControls = ({
99+
handleValidate,
100+
isValidating,
101+
resetCode,
102+
nextStepPath,
103+
outputResult,
104+
}: {
105+
handleValidate: () => void;
106+
isValidating: boolean;
107+
resetCode: () => void;
108+
nextStepPath: string | undefined;
109+
outputResult: OutputResult;
110+
}) => {
111+
const router = useRouter();
112+
113+
return (
114+
<div className={styles.buttonsWrapper}>
115+
<Flex dir="row" gap="8px" alignItems="end">
116+
<MyBtn
117+
onClick={handleValidate}
118+
variant={
119+
outputResult.validityStatus === "valid" ? "success" : "default"
120+
}
121+
isDisabled={isValidating}
122+
tooltip="Shift + Enter"
123+
>
124+
{isValidating ? "Validating ..." : "Validate"}
125+
</MyBtn>
126+
127+
<MyBtn onClick={resetCode} variant="error">
128+
Reset
129+
</MyBtn>
130+
</Flex>
131+
{nextStepPath ? (
132+
<>
133+
<MyBtn
134+
onClick={() => {
135+
if (nextStepPath) router.push("/" + nextStepPath);
136+
}}
137+
variant={
138+
outputResult.validityStatus === "valid" ? "default" : "success"
139+
}
140+
isDisabled={!!isValidating}
141+
size={outputResult.validityStatus === "valid" ? "sm" : "xs"}
142+
>
143+
Next <span style={{ marginLeft: "4px" }}></span>
144+
<FiChevronRight
145+
color={
146+
outputResult.validityStatus === "valid"
147+
? "white"
148+
: "hsl(var(--success))"
149+
}
150+
/>
151+
</MyBtn>
152+
</>
153+
) : (
154+
<CertificateButton />
155+
)}
156+
</div>
157+
);
158+
};
159+
160+
export default function CodeEditor({
161+
codeString,
162+
setCodeString,
163+
codeFile,
164+
dispatchOutput,
165+
nextStepPath,
166+
stepIndex,
167+
chapterIndex,
168+
outputResult,
169+
}: {
170+
codeString: string;
171+
setCodeString: (codeString: string) => void;
172+
codeFile: CodeFile;
173+
dispatchOutput: React.Dispatch<OutputReducerAction>;
174+
nextStepPath: string | undefined;
175+
stepIndex: number;
176+
chapterIndex: number;
177+
outputResult: OutputResult;
178+
}) {
179+
const { colorMode } = useColorMode();
180+
const [monaco, setMonaco] = useState<any>(null);
181+
const [isValidating, setIsValidating] = useState(false);
182+
const editorStore = useEditorStore();
183+
const editorRef = useRef<any>(null);
184+
185+
// Apply custom hooks
186+
useEditorTheme(monaco, colorMode);
99187

100188
const handleValidate = () => {
101189
setIsValidating(true);
@@ -112,6 +200,28 @@ export default function CodeEditor({
112200
}, 500);
113201
};
114202

203+
useValidationShortcut(handleValidate, codeString);
204+
useCodePersistence(
205+
chapterIndex,
206+
stepIndex,
207+
codeString,
208+
setCodeString,
209+
codeFile,
210+
);
211+
212+
const resetCode = () => {
213+
setCodeString(JSON.stringify(codeFile.code, null, 2));
214+
dispatchOutput({ type: "RESET" });
215+
};
216+
217+
const handleEditorMount = (editor: any, monaco: Monaco) => {
218+
setMonaco(monaco);
219+
220+
editorRef.current = editor;
221+
editorStore.setEditor(editor);
222+
editorStore.setMonaco(monaco);
223+
};
224+
115225
return (
116226
<>
117227
<div className={ctx(styles.codeEditor, GeistMono.className)}>
@@ -128,57 +238,16 @@ export default function CodeEditor({
128238
formatOnPaste: true,
129239
formatOnType: true,
130240
}}
131-
onMount={(editor, monaco) => {
132-
setMonaco(monaco);
133-
editorRef.current = editor;
134-
editorStore.setEditor(editor);
135-
editorStore.setMonaco(monaco);
136-
}}
241+
onMount={handleEditorMount}
137242
/>
138243
</div>
139-
<div className={styles.buttonsWrapper}>
140-
<Flex dir="row" gap="8px" alignItems="end">
141-
<MyBtn
142-
onClick={() => handleValidate()}
143-
variant={
144-
outputResult.validityStatus === "valid" ? "success" : "default"
145-
}
146-
isDisabled={isValidating}
147-
tooltip="Shift + Enter"
148-
>
149-
{isValidating ? "Validating..." : "Validate"}
150-
</MyBtn>
151-
152-
<MyBtn
153-
onClick={() => {
154-
setCodeString(JSON.stringify(codeFile.code, null, 2));
155-
dispatchOutput({ type: "RESET" });
156-
}}
157-
variant="error"
158-
>
159-
Reset
160-
</MyBtn>
161-
</Flex>
162-
<MyBtn
163-
onClick={() => {
164-
if (nextStepPath) router.push("/" + nextStepPath);
165-
}}
166-
variant={
167-
outputResult.validityStatus === "valid" ? "default" : "success"
168-
}
169-
isDisabled={!nextStepPath}
170-
size={outputResult.validityStatus === "valid" ? "sm" : "xs"}
171-
>
172-
Next <span style={{ marginLeft: "4px" }}></span>
173-
<FiChevronRight
174-
color={
175-
outputResult.validityStatus === "valid"
176-
? "white"
177-
: "hsl(var(--success))"
178-
}
179-
/>
180-
</MyBtn>
181-
</div>
244+
<EditorControls
245+
handleValidate={handleValidate}
246+
isValidating={isValidating}
247+
resetCode={resetCode}
248+
nextStepPath={nextStepPath}
249+
outputResult={outputResult}
250+
/>
182251
</>
183252
);
184253
}

0 commit comments

Comments
 (0)