diff --git a/next.config.js b/next.config.js
index 7fd39d2f..5404cb26 100644
--- a/next.config.js
+++ b/next.config.js
@@ -6,25 +6,15 @@ const withPWA = require('next-pwa')({
/** @type {import('next').NextConfig} */
const nextConfig = {
- webpack: (config, { dev }) => {
- if (!dev) {
- config.plugins = config.plugins.filter(
- (plugin) => plugin.constructor.name !== 'ESLintWebpackPlugin'
- );
- }
-
- return config;
- },
reactStrictMode: false,
eslint: {
ignoreDuringBuilds: true,
},
experimental: {
scrollRestoration: true,
- esmExternals: true, // Add this to ensure esmExternals is true
+ esmExternals: 'loose',
},
- ignoreDuringBuilds: true,
- transpilePackages: ['react-md-editor']
+ transpilePackages: ['@uiw/react-md-editor', '@uiw/react-markdown-preview'],
};
module.exports = withPWA(removeImports(nextConfig));
\ No newline at end of file
diff --git a/package.json b/package.json
index 684e21e6..fcdb7b99 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
"@fortawesome/react-fontawesome": "^0.2.0",
"@headlessui/react": "^1.7.2",
"@heroicons/react": "^2.0.13",
+ "@monaco-editor/react": "^4.6.0",
"@mui/material": "^5.15.2",
"@mui/x-charts": "^6.18.4",
"@react-oauth/google": "^0.12.1",
@@ -26,7 +27,8 @@
"@tailwindcss/forms": "^0.5.3",
"@tremor/react": "^1.8.1",
"@uiw/react-markdown-editor": "^6.1.1",
- "@uiw/react-md-editor": "3.6.0",
+ "@uiw/react-markdown-preview": "^5.1.3",
+ "@uiw/react-md-editor": "^4.0.5",
"@vercel/analytics": "^1.3.1",
"asciinema-player": "3.6.3",
"autoprefixer": "^10.4.12",
@@ -53,6 +55,7 @@
"puppeteer": "^22.0.0",
"react": "18.2.0",
"react-activity-calendar": "^2.2.11",
+ "react-beautiful-dnd": "^13.1.1",
"react-chartjs-2": "^5.2.0",
"react-circular-progressbar": "^2.1.0",
"react-collapsible": "^2.10.0",
diff --git a/public/loader.webp b/public/loader.webp
new file mode 100644
index 00000000..0dd8747d
Binary files /dev/null and b/public/loader.webp differ
diff --git a/public/whereami.jpeg b/public/whereami.jpeg
new file mode 100644
index 00000000..0b8884b9
Binary files /dev/null and b/public/whereami.jpeg differ
diff --git a/src/components/StandardNav.jsx b/src/components/StandardNav.jsx
index 3b7f37e5..de6173ca 100644
--- a/src/components/StandardNav.jsx
+++ b/src/components/StandardNav.jsx
@@ -314,6 +314,14 @@ export function StandardNav({ guestAllowed, alignCenter = true }) {
+
+ Learn
+
Dashboard
+
+ Learn
+
{
- const fetchData = async () => {
- const response = await fetch(url);
- const data = await response.text();
- setMarkdown(data);
- };
- fetchData();
- }, [url]);
-
- return {markdown} ;
-}
diff --git a/src/components/learn/LearnNav.jsx b/src/components/learn/LearnNav.jsx
deleted file mode 100644
index d1b1a5ff..00000000
--- a/src/components/learn/LearnNav.jsx
+++ /dev/null
@@ -1,114 +0,0 @@
-import { DonutChart } from '@tremor/react';
-import Link from 'next/link';
-import { useState, useEffect } from 'react';
-
-import request from '@/utils/request';
-
-export function LearnNav({ navElements, lessonNum }) {
- const [lessonProgress, setLessonProgress] = useState(null);
-
- useEffect(() => {
- const url = `${process.env.NEXT_PUBLIC_API_URL}/lessons/${lessonNum}/progress`;
- request(url, 'GET', null)
- .then((data) => {
- console.log(data);
- setLessonProgress(data);
- })
- .catch((err) => {
- console.log(err);
- });
- }, []);
-
- const progressArray = [];
- const colorArray = [];
- if (lessonProgress?.sublessons) {
- for (let i = 0; i < lessonProgress?.sublessons.length; i++) {
- progressArray.push({
- name: `Section ${i + 1}`,
- progress:
- lessonProgress.sublessons[i].progresses.length != 0
- ? parseInt(lessonProgress.sublessons[i].progresses[0]?.progress)
- : 0,
- });
- colorArray.push('blue');
- progressArray.push({
- name: `Section ${i + 1}`,
- progress:
- lessonProgress.sublessons[i].progresses.length != 0
- ? 100 -
- parseInt(lessonProgress.sublessons[i].progresses[0]?.progress)
- : 100,
- });
- colorArray.push('stone');
- }
- }
-
- return (
- <>
-
-
-
-
-
-
- %
-
-
Lesson Progress
- {/**
*/}
-
-
-
-
- {navElements[0].title}
-
-
-
-
-
- {navElements[1].title}
-
-
-
-
-
- {navElements[2].title}
-
-
-
-
-
- {navElements[3].title}
-
-
-
-
- >
- );
-}
diff --git a/src/components/learn/LearningModule.jsx b/src/components/learn/LearningModule.jsx
deleted file mode 100644
index 374ba907..00000000
--- a/src/components/learn/LearningModule.jsx
+++ /dev/null
@@ -1,99 +0,0 @@
-import { useState, useEffect } from 'react';
-import { ProgressBar } from '@tremor/react';
-import Link from 'next/link';
-import { useRouter } from 'next/router';
-import request from '@/utils/request';
-
-export function LearningModule({
- lessonId,
- title,
- sections,
- sectionHrefs,
- imgSrc,
- link,
-}) {
- const [lessonProgress, setLessonProgress] = useState(null);
- const router = useRouter();
-
- useEffect(() => {
- const url = `${process.env.NEXT_PUBLIC_API_URL}/lessons/${lessonId}/progress`;
- request(url, 'GET', null)
- .then((data) => {
- setLessonProgress(data);
- })
- .catch((err) => {
- console.log(err);
- });
- }, []);
-
- return (
- <>
-
-
-
- {title}
-
- {lessonProgress ? lessonProgress.totalProgress : 0}%
-
-
-
-
-
-
- {sections[0]}
- {
- router.push(sectionHrefs[0]);
- }}
- className="ml-auto cursor-pointer text-blue-500 hover:text-blue-600"
- >
- View Content →
-
-
-
- {sections[1]}
- {
- router.push(sectionHrefs[1]);
- }}
- className="ml-auto cursor-pointer text-blue-500 hover:text-blue-600"
- >
- View Content →
-
-
-
- {sections[2]}
- {
- router.push(sectionHrefs[2]);
- }}
- className="ml-auto cursor-pointer text-blue-500 hover:text-blue-600"
- >
- Start Task →
-
-
-
- {sections[3]}
- {
- router.push(sectionHrefs[3]);
- }}
- className="ml-auto cursor-pointer text-blue-500 hover:text-blue-600"
- >
- Start Task →
-
-
-
-
-
- >
- );
-}
diff --git a/src/components/learn/MarkDone.jsx b/src/components/learn/MarkDone.jsx
deleted file mode 100644
index 3c95b1f0..00000000
--- a/src/components/learn/MarkDone.jsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import { useState, useEffect } from 'react';
-import Link from 'next/link';
-import request from '@/utils/request';
-
-export function MarkDone({ sublesson, section, href }) {
- const [marked, setMarked] = useState(false);
- const [showPopup, setShowPopup] = useState(false);
- const [sfx, setSfx] = useState(null);
-
- useEffect(() => {
- setSfx(new Audio('../sounds/success.wav'));
- }, []);
-
- const handleSubmit = () => {
- if (sfx) {
- sfx.currentTime = 0; // Reset the audio to the beginning
- sfx.play(); // Play the audio
- }
-
- // Mark Progress
- const url = `${process.env.NEXT_PUBLIC_API_URL}/lessons/sublesson/${sublesson}/progress/${section}`;
- request(url, 'PUT', {})
- .then((data) => {
- setMarked(true);
- setShowPopup(true);
- setTimeout(() => setShowPopup(false), 4000);
- })
- .catch((err) => {
- // Trigger Unauthenticated Popup
- });
- };
-
- return (
- <>
-
-
- Mark Done
-
-
- {showPopup && (
-
- Progress Saved!
-
- )}
- {marked &&
}
- >
- );
-}
diff --git a/src/components/learn/Quiz.jsx b/src/components/learn/Quiz.jsx
deleted file mode 100644
index bdd62c9b..00000000
--- a/src/components/learn/Quiz.jsx
+++ /dev/null
@@ -1,109 +0,0 @@
-import { useState } from 'react';
-import request from '@/utils/request';
-
-export function Quiz({ page, sublesson, quizData }) {
- const [selectedAnswer, setSelectedAnswer] = useState(null);
- const [showPopup, setShowPopup] = useState(false);
- const [showError, setErrorPopup] = useState(false);
-
- const handleAnswerSelection = (event) => {
- setSelectedAnswer(event.target.value);
- };
-
- const handleSubmit = (event) => {
- event.preventDefault();
-
- const solution = quizData ? quizData[page - 1].solution : '';
- const isCorrect = selectedAnswer === solution;
-
- if (isCorrect) {
- console.log('Submission correct!');
- setShowPopup(true);
- setTimeout(() => setShowPopup(false), 4000);
-
- const url = `${process.env.NEXT_PUBLIC_API_URL}/lessons/sublesson/${sublesson}/progress/${page}`;
- request(url, 'PUT', {})
- .catch((err) => {
- // Trigger Unauthenticated Popup
- });
- } else {
- console.log('Submission incorrect!');
- setErrorPopup(true);
- setTimeout(() => setErrorPopup(false), 4000);
- }
- };
-
- const { question, answers } = quizData
- ? quizData[page - 1]
- : { question: '', answers: [''] };
-
- return (
-
-
- Question {page}:
-
-
- {question}
-
-
- {showPopup && (
-
- Correct!
-
- )}
- {showError && (
-
- Incorrect!
-
- )}
-
- );
-}
diff --git a/src/components/learn/QuizPage.jsx b/src/components/learn/QuizPage.jsx
deleted file mode 100644
index ff399791..00000000
--- a/src/components/learn/QuizPage.jsx
+++ /dev/null
@@ -1,76 +0,0 @@
-import { useState, useEffect } from 'react';
-import { Quiz } from './Quiz';
-import { SectionsNav } from './SectionsNav';
-import { useRouter } from 'next/router';
-import Link from 'next/link';
-import { motion } from 'framer-motion';
-
-function QuizPage({ totalQuizPages, sublesson, quizData, nextPage }) {
- const router = useRouter();
- const [page, setPage] = useState(1);
-
- // Update page state when query param changes
- useEffect(() => {
- const queryPage = parseInt(router.query.quizPage);
- if (queryPage && !isNaN(queryPage)) {
- setPage(queryPage);
- }
- }, [router.query.quizPage]);
-
- const handleNext = () => {
- setPage(page + 1);
- };
-
- const handlePrev = () => {
- setPage(page - 1);
- };
-
- const pagePercentage = parseInt(100 / totalQuizPages);
-
- return (
-
-
-
-
- {[...Array(totalQuizPages)].map(
- (_, index) =>
- page === index + 1 && (
-
- )
- )}
-
-
- {page > 1 && (
-
- Back
-
- )}
- {page < totalQuizPages && (
-
- Next
-
- )}
- {page === totalQuizPages && (
-
-
- Go to next task
-
-
- )}
-
-
- );
-}
-
-export default QuizPage;
diff --git a/src/components/learn/SectionsNav.jsx b/src/components/learn/SectionsNav.jsx
deleted file mode 100644
index c27f0d9a..00000000
--- a/src/components/learn/SectionsNav.jsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import { useEffect, useState } from 'react';
-import { ProgressBar } from '@tremor/react';
-import request from '@/utils/request';
-
-export function SectionsNav({ currentPage, cpv, colors, sublesson }) {
- const [lessonProgress, setLessonProgress] = useState(null);
-
- useEffect(() => {
- const url = `${process.env.NEXT_PUBLIC_API_URL}/lessons/sublesson/${sublesson}/progress`;
- request(url, 'GET', null)
- .then((data) => {
- console.log(data);
- setLessonProgress(data);
- })
- .catch((err) => {
- console.log(err);
- });
- }, []);
-
- console.log(lessonProgress);
- if (lessonProgress?.completion) {
- for (let i = 0; i < lessonProgress.completion.length; i++) {
- if (lessonProgress[i]) {
- colors[i] = 'green';
- }
- }
- // colors[currentPage - 1] = "blue";
- }
-
- return (
- <>
-
-
-
- Section Progress
-
-
-
-
- >
- );
-}
diff --git a/src/components/learn/editor/StudioEditor.jsx b/src/components/learn/editor/StudioEditor.jsx
new file mode 100644
index 00000000..c8d0a3fd
--- /dev/null
+++ b/src/components/learn/editor/StudioEditor.jsx
@@ -0,0 +1,1111 @@
+import React, { useState, useEffect } from 'react';
+import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
+import dynamic from 'next/dynamic';
+import { MarkdownViewer } from '@/components/MarkdownViewer';
+import { motion, AnimatePresence } from 'framer-motion';
+
+// Dynamic import of MonacoEditor
+const MonacoEditor = dynamic(() => import('@monaco-editor/react'), {
+ ssr: false
+});
+
+const LANGUAGE_CONFIG = {
+ python: {
+ icon: 'fab fa-python',
+ name: 'Python',
+ monacoLang: 'python'
+ },
+ javascript: {
+ icon: 'fab fa-js',
+ name: 'JavaScript',
+ monacoLang: 'javascript'
+ },
+ cpp: {
+ icon: 'fas fa-code',
+ name: 'C++',
+ monacoLang: 'cpp'
+ },
+ bash: {
+ icon: 'fas fa-terminal',
+ name: 'Bash',
+ monacoLang: 'shell'
+ },
+ shell: {
+ icon: 'fas fa-terminal',
+ name: 'Shell Script',
+ monacoLang: 'shell'
+ }
+};
+
+const TUTORIAL_STEPS = [
+ {
+ id: 1,
+ target: 'editor-container',
+ content: 'Welcome to the Studio Editor! Right-click anywhere to start adding components.',
+ position: 'center'
+ },
+ {
+ id: 2,
+ target: 'markdown-component',
+ content: 'Add Markdown components to write formatted text, documentation, or instructions.',
+ position: 'bottom'
+ },
+ {
+ id: 3,
+ target: 'multiple-choice',
+ content: 'Create multiple choice questions to test knowledge. Don\'t forget to set the correct answer!',
+ position: 'bottom'
+ },
+ {
+ id: 4,
+ target: 'code-component',
+ content: 'Add code components to write examples or create coding challenges.',
+ position: 'bottom'
+ },
+ {
+ id: 5,
+ target: 'drag-handle',
+ content: 'Drag components to reorder them. Build your content in any order you like!',
+ position: 'right'
+ }
+];
+
+const getIconForType = (type) => {
+ switch (type) {
+ case 'markdown':
+ return 'markdown';
+ case 'multiple-choice':
+ return 'list-ul';
+ case 'code':
+ return 'code';
+ default:
+ return 'plus';
+ }
+};
+
+const StudioEditor = ({ initialProject = null }) => {
+ // Initialize pages with either imported project data or default
+ const [pages, setPages] = useState(() => {
+ if (initialProject && initialProject.pages) {
+ return initialProject.pages;
+ }
+ return [{ id: 1, title: 'Page 1', components: [] }];
+ });
+
+ const [currentPageId, setCurrentPageId] = useState(() => {
+ if (initialProject && initialProject.pages && initialProject.pages.length > 0) {
+ return initialProject.pages[0].id;
+ }
+ return 1;
+ });
+
+ const [showPageModal, setShowPageModal] = useState(false);
+ const [newPageTitle, setNewPageTitle] = useState('');
+
+ const currentPage = pages.find(p => p.id === currentPageId) || pages[0];
+ const components = currentPage.components;
+
+ const [menuVisible, setMenuVisible] = useState(false);
+ const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 });
+ const [isSaving, setIsSaving] = useState(false);
+ const [showTutorial, setShowTutorial] = useState(false);
+ const [currentStep, setCurrentStep] = useState(0);
+ const [hasSeenTutorial, setHasSeenTutorial] = useState(false);
+ const [showJsonModal, setShowJsonModal] = useState(false);
+ const [showLoadJsonModal, setShowLoadJsonModal] = useState(false);
+ const [jsonInput, setJsonInput] = useState('');
+ const [jsonError, setJsonError] = useState('');
+ const [showImportProjectModal, setShowImportProjectModal] = useState(false);
+ const [projectJsonInput, setProjectJsonInput] = useState('');
+ const [importError, setImportError] = useState('');
+ const [isLoading, setIsLoading] = useState(true);
+
+ const handleStartFresh = () => {
+ setIsLoading(false);
+ };
+
+ const handleInitialImport = () => {
+ try {
+ const parsedJson = JSON.parse(projectJsonInput);
+ if (!parsedJson.pages || !Array.isArray(parsedJson.pages)) {
+ throw new Error('Invalid project format: missing pages array');
+ }
+
+ setPages(parsedJson.pages);
+ setCurrentPageId(parsedJson.pages[0]?.id || null);
+ setIsLoading(false);
+ setProjectJsonInput('');
+ setImportError('');
+ } catch (error) {
+ setImportError(error.message);
+ }
+ };
+
+ useEffect(() => {
+ // Check if user has seen tutorial before
+ const tutorialSeen = localStorage.getItem('studioEditorTutorialSeen');
+ if (!tutorialSeen) {
+ setShowTutorial(true);
+ localStorage.setItem('studioEditorTutorialSeen', 'true');
+ }
+ }, []);
+
+ const nextTutorialStep = () => {
+ if (currentStep < TUTORIAL_STEPS.length - 1) {
+ setCurrentStep(currentStep + 1);
+ } else {
+ setShowTutorial(false);
+ setHasSeenTutorial(true);
+ }
+ };
+
+ const skipTutorial = () => {
+ setShowTutorial(false);
+ setHasSeenTutorial(true);
+ };
+
+ // Handle right-click to show component menu
+ const handleContextMenu = (e) => {
+ e.preventDefault();
+ setMenuPosition({ x: e.clientX, y: e.clientY });
+ setMenuVisible(true);
+ };
+
+ // Handle click outside to close menu
+ useEffect(() => {
+ const handleClickOutside = () => setMenuVisible(false);
+ document.addEventListener('click', handleClickOutside);
+ return () => document.removeEventListener('click', handleClickOutside);
+ }, []);
+
+ // Handle adding new components
+ const handleAddComponent = (type) => {
+ const newComponent = {
+ id: Date.now(),
+ type,
+ content: '',
+ config: getDefaultConfig(type)
+ };
+
+ setPages(prevPages => prevPages.map(page => {
+ if (page.id === currentPageId) {
+ return {
+ ...page,
+ components: [...page.components, newComponent]
+ };
+ }
+ return page;
+ }));
+ setMenuVisible(false);
+ };
+
+ // Get default configuration based on component type
+ const getDefaultConfig = (type) => {
+ switch (type) {
+ case 'markdown':
+ return { preview: false };
+ case 'multiple-choice':
+ return { options: [], correctAnswer: null };
+ case 'code':
+ return { language: 'python', testCases: [] };
+ default:
+ return {};
+ }
+ };
+
+ // Handle updating component content
+ const handleUpdateComponent = (id, updates) => {
+ setPages(prevPages => prevPages.map(page => {
+ if (page.id === currentPageId) {
+ return {
+ ...page,
+ components: page.components.map(comp =>
+ comp.id === id ? { ...comp, ...updates } : comp
+ )
+ };
+ }
+ return page;
+ }));
+ };
+
+ const insertText = (id, text) => {
+ const component = components.find(comp => comp.id === id);
+ if (!component) return;
+
+ const textarea = document.querySelector(`textarea[data-id="${id}"]`);
+ if (!textarea) return;
+
+ const start = textarea.selectionStart;
+ const end = textarea.selectionEnd;
+ const currentContent = component.content || '';
+ const newContent = currentContent.substring(0, start) + text + currentContent.substring(end);
+
+ handleUpdateComponent(id, { content: newContent });
+ };
+
+ // Handle deleting components
+ const handleDeleteComponent = (id) => {
+ setPages(prevPages => prevPages.map(page => {
+ return {
+ ...page,
+ components: page.components.filter(comp => comp.id !== id)
+ };
+ }));
+ };
+
+ // Handle drag and drop reordering
+ const onDragEnd = (result) => {
+ if (!result.destination) return;
+
+ const items = Array.from(components);
+ const [reorderedItem] = items.splice(result.source.index, 1);
+ items.splice(result.destination.index, 0, reorderedItem);
+
+ setPages(prevPages => prevPages.map(page => {
+ if (page.id === currentPageId) {
+ return {
+ ...page,
+ components: items
+ };
+ }
+ return page;
+ }));
+ };
+
+ // Render different component types
+ const renderComponent = (component) => {
+ switch (component.type) {
+ case 'markdown':
+ return (
+
+
+
+
+
Markdown Editor
+
+
+ {['bold', 'italic', 'heading', 'link', 'code'].map((tool) => (
+ insertText(component.id, getToolMarkdown(tool))}
+ className="hover:bg-white/10 p-1.5 rounded transition-all group"
+ >
+
+
+ ))}
+
+
+ e.stopPropagation()}>
+
+
+
+
+
+
+
Drag to reorder
+
handleDeleteComponent(component.id)}
+ className="bg-red-500/20 hover:bg-red-500/30 text-red-400 hover:text-red-300 px-4 py-1.5 rounded-lg transition-all text-sm font-medium flex items-center space-x-2"
+ >
+
+ Delete
+
+
+
+ );
+ case 'multiple-choice':
+ return (
+
+
+
+
+
Multiple Choice
+
+
+ e.stopPropagation()}>
+
+
+
handleUpdateComponent(component.id, { content: e.target.value })}
+ className="w-full bg-neutral-900/50 text-white p-4 rounded-xl border border-neutral-700/30 focus:border-blue-500/50 focus:ring-2 focus:ring-blue-500/20 transition-all text-sm"
+ placeholder="Enter your question..."
+ />
+
+
+
+ {component.config.options.map((option, index) => (
+
e.stopPropagation()}
+ >
+ {
+ e.stopPropagation();
+ handleUpdateComponent(component.id, {
+ config: { ...component.config, correctAnswer: index }
+ });
+ }}
+ className="flex items-center space-x-2 bg-neutral-900/50 px-4 py-2 rounded-xl border border-neutral-700/30 cursor-pointer group/toggle hover:bg-neutral-800/50 transition-all"
+ >
+
+
+
+
+ Correct Answer
+
+ {component.config.correctAnswer === index && (
+
+
+
+ )}
+
+ e.stopPropagation()}>
+
+
{
+ const newOptions = [...component.config.options];
+ newOptions[index] = e.target.value;
+ handleUpdateComponent(component.id, {
+ config: { ...component.config, options: newOptions }
+ });
+ }}
+ className={`
+ w-full bg-neutral-900/50 text-white p-3 rounded-xl border transition-all text-sm
+ ${component.config.correctAnswer === index
+ ? 'border-green-500/30 focus:border-green-500/50 focus:ring-2 focus:ring-green-500/20'
+ : 'border-neutral-700/30 focus:border-blue-500/50 focus:ring-2 focus:ring-blue-500/20'}
+ `}
+ placeholder={`Option ${index + 1}`}
+ />
+
+ {
+ e.stopPropagation();
+ const newOptions = component.config.options.filter((_, i) => i !== index);
+ let newCorrectAnswer = component.config.correctAnswer;
+ if (index === component.config.correctAnswer) {
+ newCorrectAnswer = null;
+ } else if (index < component.config.correctAnswer) {
+ newCorrectAnswer--;
+ }
+ handleUpdateComponent(component.id, {
+ config: {
+ ...component.config,
+ options: newOptions,
+ correctAnswer: newCorrectAnswer
+ }
+ });
+ }}
+ className="bg-red-500/20 hover:bg-red-500/30 text-red-400 hover:text-red-300 p-2 rounded-lg transition-all"
+ >
+
+
+
+ ))}
+
+
+
+ handleUpdateComponent(component.id, {
+ config: {
+ ...component.config,
+ options: [...component.config.options, '']
+ }
+ })}
+ className="bg-blue-500/20 hover:bg-blue-500/30 text-blue-400 hover:text-blue-300 px-4 py-2 rounded-lg transition-all text-sm font-medium flex items-center space-x-2"
+ >
+
+ Add Option
+
+
+
+
+
Drag to reorder
+
handleDeleteComponent(component.id)}
+ className="bg-red-500/20 hover:bg-red-500/30 text-red-400 hover:text-red-300 px-4 py-1.5 rounded-lg transition-all text-sm font-medium flex items-center space-x-2"
+ >
+
+ Delete
+
+
+ {component.config.correctAnswer === null && component.config.options.length > 0 && (
+
+
+
+ Please select a correct answer
+
+
+ )}
+
+ );
+ case 'code':
+ return (
+
+
+
+
+
Code Editor
+
+
handleUpdateComponent(component.id, {
+ config: { ...component.config, language: e.target.value }
+ })}
+ className="bg-white/10 text-white px-3 py-1 rounded-lg border border-white/10 focus:border-blue-400/50 focus:ring-2 focus:ring-blue-500/20 transition-all text-sm backdrop-blur-md"
+ >
+ {Object.entries(LANGUAGE_CONFIG).map(([key, lang]) => (
+
+ {lang.name}
+
+ ))}
+
+
+
+
+ handleUpdateComponent(component.id, { content: value })}
+ theme="vs-dark"
+ options={{
+ minimap: { enabled: false },
+ fontSize: 14,
+ padding: { top: 16, bottom: 16 },
+ scrollBeyondLastLine: false,
+ renderLineHighlight: 'all',
+ fontFamily: 'JetBrains Mono, monospace',
+ roundedSelection: true,
+ cursorStyle: 'line-thin',
+ cursorBlinking: 'smooth',
+ }}
+ />
+
+
+
+
Drag to reorder
+
handleDeleteComponent(component.id)}
+ className="bg-red-500/20 hover:bg-red-500/30 text-red-400 hover:text-red-300 px-4 py-1.5 rounded-lg transition-all text-sm font-medium flex items-center space-x-2"
+ >
+
+ Delete
+
+
+
+ );
+ default:
+ return null;
+ }
+ };
+
+ // Add this helper function for markdown toolbar
+ const getToolMarkdown = (tool) => {
+ switch (tool) {
+ case 'bold':
+ return '**bold text**';
+ case 'italic':
+ return '*italic text*';
+ case 'heading':
+ return '# Heading';
+ case 'link':
+ return '[link text](url)';
+ case 'code':
+ return '```\ncode block\n```';
+ default:
+ return '';
+ }
+ };
+
+ const JsonViewerModal = () => (
+
+ {showJsonModal && (
+ setShowJsonModal(false)}
+ >
+ e.stopPropagation()}
+ className="bg-neutral-800/90 rounded-xl border border-neutral-700/50 shadow-2xl backdrop-blur-sm w-full max-w-3xl m-4"
+ >
+
+
+
+ Component JSON Data
+
+ setShowJsonModal(false)}
+ className="text-neutral-400 hover:text-white transition-colors"
+ >
+
+
+
+
+
+
+ {JSON.stringify(components, null, 2)}
+
+
+
+
+ This data will be stored in the backend
+
+
{
+ navigator.clipboard.writeText(JSON.stringify(components, null, 2));
+ }}
+ className="bg-blue-500/20 hover:bg-blue-500/30 text-blue-400 hover:text-blue-300 px-4 py-2 rounded-lg transition-all text-sm font-medium flex items-center space-x-2"
+ >
+
+ Copy JSON
+
+
+
+
+
+ )}
+
+ );
+
+ const handleLoadJson = () => {
+ try {
+ const parsedJson = JSON.parse(jsonInput);
+ if (!Array.isArray(parsedJson)) {
+ throw new Error('JSON must be an array of components');
+ }
+
+ // Validate each component has required fields
+ parsedJson.forEach(comp => {
+ if (!comp.type || !comp.id) {
+ throw new Error('Each component must have type and id fields');
+ }
+ });
+
+ setPages(prevPages => prevPages.map(page => {
+ if (page.id === currentPageId) {
+ return {
+ ...page,
+ components: parsedJson
+ };
+ }
+ return page;
+ }));
+ setJsonInput('');
+ setJsonError('');
+ setShowLoadJsonModal(false);
+ } catch (error) {
+ setJsonError(error.message);
+ }
+ };
+
+ const LoadJsonModal = () => (
+
+ {showLoadJsonModal && (
+ setShowLoadJsonModal(false)}
+ >
+ e.stopPropagation()}
+ className="bg-neutral-800/90 rounded-xl border border-neutral-700/50 shadow-2xl backdrop-blur-sm w-full max-w-3xl m-4"
+ >
+
+
+
+ Load Components from JSON
+
+ setShowLoadJsonModal(false)}
+ className="text-neutral-400 hover:text-white transition-colors"
+ >
+
+
+
+
+
+ {jsonError && (
+
+
+ {jsonError}
+
+ )}
+
+ setShowLoadJsonModal(false)}
+ className="px-4 py-2 text-neutral-400 hover:text-white transition-colors text-sm"
+ >
+ Cancel
+
+
+
+ Load Components
+
+
+
+
+
+ )}
+
+ );
+
+ // Add page management functions
+ const handleAddPage = () => {
+ if (!newPageTitle.trim()) return;
+
+ const newPage = {
+ id: Date.now(),
+ title: newPageTitle,
+ components: []
+ };
+
+ setPages(prevPages => [...prevPages, newPage]);
+ setCurrentPageId(newPage.id);
+ setNewPageTitle(''); // Clear the input
+ setShowPageModal(false); // Close modal
+ };
+
+ const handleDeletePage = (pageId) => {
+ if (pages.length <= 1) return; // Don't delete last page
+ setPages(pages.filter(p => p.id !== pageId));
+ if (currentPageId === pageId) {
+ setCurrentPageId(pages[0].id);
+ }
+ };
+
+ const handleRenamePage = (pageId, newTitle) => {
+ setPages(pages.map(page =>
+ page.id === pageId ? { ...page, title: newTitle } : page
+ ));
+ };
+
+ // Add handler for project import
+ const handleImportProject = () => {
+ try {
+ const parsedJson = JSON.parse(projectJsonInput);
+ if (!parsedJson.pages || !Array.isArray(parsedJson.pages)) {
+ throw new Error('Invalid project format: missing pages array');
+ }
+
+ // Validate project structure
+ parsedJson.pages.forEach(page => {
+ if (!page.id || !page.title || !Array.isArray(page.components)) {
+ throw new Error('Invalid page format');
+ }
+ });
+
+ setPages(parsedJson.pages);
+ setCurrentPageId(parsedJson.pages[0]?.id || null);
+ setProjectJsonInput('');
+ setImportError('');
+ setShowImportProjectModal(false);
+ } catch (error) {
+ setImportError(error.message);
+ }
+ };
+
+ // Add handler for project export
+ const handleExportProject = () => {
+ const projectData = {
+ pages,
+ version: "1.0",
+ exportedAt: new Date().toISOString()
+ };
+
+ const jsonString = JSON.stringify(projectData, null, 2);
+
+ // Create and trigger download
+ const blob = new Blob([jsonString], { type: 'application/json' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = `ctfguide-project-${Date.now()}.json`;
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+ };
+
+ // Add Import Project Modal component
+ const ImportProjectModal = () => {
+ if (!showImportProjectModal) return null;
+
+ return (
+ setShowImportProjectModal(false)}
+ >
+
e.stopPropagation()}
+ >
+
+
+
+ Import Project
+
+ setShowImportProjectModal(false)}
+ className="text-neutral-400 hover:text-white transition-colors"
+ >
+
+
+
+
+
+
+ {importError && (
+
+
+ {importError}
+
+ )}
+
+ setShowImportProjectModal(false)}
+ className="px-4 py-2 text-neutral-400 hover:text-white transition-colors text-sm"
+ >
+ Cancel
+
+
+
+ Import Project
+
+
+
+
+
+ );
+ };
+
+ // Add the context menu component
+ const ContextMenu = () => (
+
+ {menuVisible && (
+
+ {['markdown', 'multiple-choice', 'code'].map((type) => (
+ handleAddComponent(type)}
+ >
+
+ {type.replace('-', ' ')}
+
+ ))}
+
+ )}
+
+ );
+
+ const Sidebar = () => (
+
+ {/* Pages Section */}
+
+
+
+
+ Pages
+
+
setShowPageModal(true)}
+ className="bg-blue-500/20 hover:bg-blue-500/30 text-blue-400 p-1.5 rounded-lg transition-all text-sm"
+ >
+
+
+
+
+ {pages.map((page) => (
+
setCurrentPageId(page.id)}
+ >
+ {page.title}
+
+ {
+ e.stopPropagation();
+ const newTitle = prompt('Enter new page title:', page.title);
+ if (newTitle) handleRenamePage(page.id, newTitle);
+ }}
+ className="p-1 hover:text-blue-400 transition-colors"
+ >
+
+
+ {pages.length > 1 && (
+ {
+ e.stopPropagation();
+ if (confirm('Delete this page?')) handleDeletePage(page.id);
+ }}
+ className="p-1 hover:text-red-400 transition-colors"
+ >
+
+
+ )}
+
+
+ ))}
+
+
+
+ {/* Updated JSON Actions Section */}
+
+
+
+ JSON Actions
+
+
+ setShowImportProjectModal(true)}
+ className="w-full bg-green-500/20 hover:bg-green-500/30 text-green-400 px-4 py-2 rounded-lg transition-all text-sm font-medium flex items-center justify-center space-x-2"
+ >
+
+ Import Project
+
+
+
+ Export Project
+
+ setShowJsonModal(true)}
+ className="w-full bg-purple-500/20 hover:bg-purple-500/30 text-purple-400 px-4 py-2 rounded-lg transition-all text-sm font-medium flex items-center justify-center space-x-2"
+ >
+
+ View JSON
+
+
+
+
+ );
+
+ // Add new page modal
+ const NewPageModal = () => {
+ if (!showPageModal) return null; // Don't render if not showing
+
+ return (
+ setShowPageModal(false)}
+ >
+
e.stopPropagation()}
+ >
+
Add New Page
+
setNewPageTitle(e.target.value)}
+ placeholder="Enter page title"
+ className="w-full bg-neutral-900/50 text-white p-3 rounded-xl border border-neutral-700/30 focus:border-blue-500/50 focus:ring-2 focus:ring-blue-500/20 transition-all mb-4"
+ autoFocus
+ />
+
+ {
+ setNewPageTitle('');
+ setShowPageModal(false);
+ }}
+ className="px-4 py-2 text-neutral-400 hover:text-white transition-colors"
+ >
+ Cancel
+
+
+ Add Page
+
+
+
+
+ );
+ };
+
+ useEffect(() => {
+ // Handle any additional initialization needed for imported projects
+ if (initialProject) {
+ // You could add additional validation or data processing here
+ console.log('Project imported successfully');
+ }
+ }, [initialProject]);
+
+ return (
+
+
+
+
+
+
+
+
+ {/* Main content area */}
+
+
+
+ {(provided) => (
+
+ {components.map((component, index) => (
+
+ {(provided) => (
+
+
+
+
+
+
+ {renderComponent(component)}
+
+
+
+ )}
+
+ ))}
+ {provided.placeholder}
+
+ )}
+
+
+
+
+ );
+};
+
+export default StudioEditor;
+
diff --git a/src/components/learn/modules/ModuleLayout.jsx b/src/components/learn/modules/ModuleLayout.jsx
new file mode 100644
index 00000000..9305bb77
--- /dev/null
+++ b/src/components/learn/modules/ModuleLayout.jsx
@@ -0,0 +1,18 @@
+import React from 'react';
+import ModuleCard from './cards/ModuleCard';
+
+const ModuleLayout = ({ className }) => {
+ return (
+
+ );
+};
+
+export default ModuleLayout;
\ No newline at end of file
diff --git a/src/components/learn/modules/UpNext.jsx b/src/components/learn/modules/UpNext.jsx
new file mode 100644
index 00000000..9fb96b16
--- /dev/null
+++ b/src/components/learn/modules/UpNext.jsx
@@ -0,0 +1,15 @@
+import React from 'react';
+import UpNextCard from './cards/UpNextCard';
+const UpNext = ({ className }) => {
+ return (
+
+
Up next for you
+
+
+
+
+
+ );
+};
+
+export default UpNext;
diff --git a/src/components/learn/modules/cards/ModuleCard.jsx b/src/components/learn/modules/cards/ModuleCard.jsx
new file mode 100644
index 00000000..542bdd79
--- /dev/null
+++ b/src/components/learn/modules/cards/ModuleCard.jsx
@@ -0,0 +1,32 @@
+import React from 'react';
+
+const ModuleCard = ({ title, description, image, status, type, completed, active }) => {
+ return (
+ <>
+
+
+ {
+ image &&
+ }
+
{title}
+
{description}
+
+
+
+
+
+
+ >
+ );
+};
+
+export default ModuleCard;
\ No newline at end of file
diff --git a/src/components/learn/modules/cards/UpNextCard.jsx b/src/components/learn/modules/cards/UpNextCard.jsx
new file mode 100644
index 00000000..bd1997d6
--- /dev/null
+++ b/src/components/learn/modules/cards/UpNextCard.jsx
@@ -0,0 +1,45 @@
+import React from 'react';
+
+const UpNextCard = ({ title, description, type, image }) => {
+ if (type === 'lab') {
+ return (
+
+
+
+
+
{title}
+
{description}
+
+
+
+
+
+
+ );
+ }
+
+ if (type === 'task') {
+ return (
+
+
+
+
{title}
+
{description}
+
+
+
+
+
+ );
+ }
+
+ return (
+ Component failure. You did not specify a valid type for the UpNextCard component.
+ )
+};
+
+export default UpNextCard;
diff --git a/src/components/learn/modules/progress/LessonBar.jsx b/src/components/learn/modules/progress/LessonBar.jsx
new file mode 100644
index 00000000..fc96b26e
--- /dev/null
+++ b/src/components/learn/modules/progress/LessonBar.jsx
@@ -0,0 +1,50 @@
+import React from 'react';
+
+const LessonBar = ({ name, progress, type }) => {
+ if (type === "lab") {
+ return (
+
+
+
+ {name}
+
+
+
+ {type.toUpperCase()}
+
+
+ );
+ }
+
+ if (type === "task") {
+ return (
+
+
+
+ {name}
+
+
+
+ {type.toUpperCase()}
+
+
+ );
+ }
+
+ if (type === "final") {
+ return (
+
+
+
+ {name}
+
+
+
+ {type.toUpperCase()}
+
+
+ );
+ }
+};
+
+export default LessonBar;
diff --git a/src/components/learn/shared/ProgressSideBar.jsx b/src/components/learn/shared/ProgressSideBar.jsx
new file mode 100644
index 00000000..d5762107
--- /dev/null
+++ b/src/components/learn/shared/ProgressSideBar.jsx
@@ -0,0 +1,35 @@
+import React from 'react';
+import ModuleCard from '../modules/cards/ModuleCard';
+import LessonBar from '../modules/progress/LessonBar';
+const ProgressSideBar = ({ active, progress }) => {
+ return (
+
+
Module Progress
+
+
{active}
+
{progress}%
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default ProgressSideBar;
\ No newline at end of file
diff --git a/src/pages/challenges/[...id].jsx b/src/pages/challenges/[...id].jsx
index 78202efe..62b4105b 100644
--- a/src/pages/challenges/[...id].jsx
+++ b/src/pages/challenges/[...id].jsx
@@ -1674,7 +1674,7 @@ function CommentsPage({ cache }) {
{comment.role === 'ADMIN' && (
<>
- developer
+ admin
>
)}
{comment.role === 'PRO' && (
diff --git a/src/pages/create.jsx b/src/pages/create.jsx
index d103c52e..4c31dfcd 100644
--- a/src/pages/create.jsx
+++ b/src/pages/create.jsx
@@ -146,19 +146,19 @@ export default function Create() {
try {
switch (selection) {
case 'unverified':
- setTitle('Unverified');
+ setTitle('Unverified Challenges');
setInfoText(
'Please wait for admins to approve unverified challenges!'
);
response = await request(`${process.env.NEXT_PUBLIC_API_URL}/account/challenges?state=unverified`, "GET", null);
break;
case 'pending':
- setTitle('Pending Changes');
+ setTitle('Pending Challenges');
setInfoText('These challenges are awaiting changes!');
response = await request(`${process.env.NEXT_PUBLIC_API_URL}/account/challenges?state=pending`, "GET", null);
break;
case 'published':
- setTitle('Published');
+ setTitle('Published Challenges');
setInfoText(
'These challenges are live! Share them with your friends!'
);
@@ -415,21 +415,21 @@ export default function Create() {
className="block px-4 py-2 text-sm text-white hover:bg-neutral-700"
onClick={async () => fetchChallenges('unverified')}
>
- Unverified
+ Unverified Challenges
fetchChallenges('pending')}
>
- Pending Changes
+ Pending Challenges
fetchChallenges('published')}
>
- Published
+ Published Challenges
@@ -557,53 +557,72 @@ export default function Create() {
-
-
-
-
- Writeup Name
-
-
- Challenge Name
-
-
-
- Last Updated
-
-
- Edit
-
-
-
-
- {writeups.map((writeup) => (
-
-
-
- {writeup.draft &&
- draft
- }
-
- {!writeup.draft &&
- published
- }
+ {/* Cap the height */}
+
+
+
+
+ Writeup Name
+
+
+ Challenge Name
+
+
+
+ Last Updated
+
+
+ Edit
+
+
+
+
+ {currentWriteups.map((writeup) => (
+
+
+
+ {writeup.draft &&
+ draft
+ }
+
+ {!writeup.draft &&
+ published
+ }
+
+ {writeup.title}
+
+ {writeup.challenge.title}
+
+ {new Date(writeup.updatedAt).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}
- {writeup.title}
-
- {writeup.challenge.title}
-
- {new Date(writeup.updatedAt).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}
+
+
+ Edit, {writeup.title}
+
+
+
+ ))}
+
+
+
-
-
- Edit, {writeup.title}
-
-
-
- )) ||
- }
-
-
+
+
+ Previous
+
+ Page {currentPage} of {totalPages}
+
+ Next
+
+
@@ -824,7 +843,7 @@ export default function Create() {
Challenge Notifications
-
+
{notifications.length > 0 ? (
notifications.slice(0, 3).map((notification) => (
diff --git a/src/pages/create/learn/editor.jsx b/src/pages/create/learn/editor.jsx
new file mode 100644
index 00000000..6a2f10ee
--- /dev/null
+++ b/src/pages/create/learn/editor.jsx
@@ -0,0 +1,177 @@
+import React, { useState, useEffect } from 'react';
+import {StandardNav} from '../../../components/StandardNav';
+import StudioEditor from '../../../components/learn/editor/StudioEditor';
+
+const LearnEditor = () => {
+ const [isLoading, setIsLoading] = useState(true);
+ const [projectJsonInput, setProjectJsonInput] = useState('');
+ const [importError, setImportError] = useState('');
+ const [importedProject, setImportedProject] = useState(null);
+ const [isDragging, setIsDragging] = useState(false);
+
+ const handleImportProject = (jsonContent) => {
+ try {
+ const parsedJson = JSON.parse(jsonContent);
+ if (!parsedJson.pages || !Array.isArray(parsedJson.pages)) {
+ throw new Error('Invalid project format: missing pages array');
+ }
+
+ setImportedProject(parsedJson);
+ setIsLoading(false);
+ setProjectJsonInput('');
+ setImportError('');
+ } catch (error) {
+ setImportError(error.message);
+ }
+ };
+
+ const handleDragOver = (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ setIsDragging(true);
+ };
+
+ const handleDragLeave = (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ setIsDragging(false);
+ };
+
+ const handleDrop = (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ setIsDragging(false);
+
+ const file = e.dataTransfer.files[0];
+ if (file && file.type === 'application/json') {
+ const reader = new FileReader();
+ reader.onload = (event) => {
+ try {
+ handleImportProject(event.target.result);
+ } catch (error) {
+ setImportError('Failed to read JSON file');
+ }
+ };
+ reader.readAsText(file);
+ } else {
+ setImportError('Please drop a valid JSON file');
+ }
+ };
+
+ return (
+ <>
+
+ {isLoading ? (
+
+
+
+
+
+
+
+
CTFGuide Studio
+
Create interactive learning experiences
+
+
+
+
+
+
+
+
+
+
+
+
+ Drop your project file here
+
+
+ or paste JSON below
+
+
+
+
+
+
+
+
+ {importError && (
+
+
+ {importError}
+
+ )}
+
+
+
+
+ alpha v0.2
+
+
+
+ setIsLoading(false)}
+ className="text-neutral-400 hover:text-white text-sm transition-colors"
+ >
+ Start Fresh
+
+ handleImportProject(projectJsonInput)}
+ disabled={!projectJsonInput}
+ className={`px-4 py-2 rounded-lg transition-all text-sm flex items-center space-x-2 ${
+ projectJsonInput
+ ? 'bg-gradient-to-r from-blue-500 to-blue-600 text-white hover:from-blue-600 hover:to-blue-700'
+ : 'bg-neutral-800 text-neutral-500 cursor-not-allowed'
+ }`}
+ >
+
+ Import Project
+
+
+
+
+
+
+
+ ) : (
+
+
+
+
Learn Studio v.0.2
+
+
+ Delete
+ Save
+
+
+
+
+ )}
+ >
+ );
+};
+
+export default LearnEditor;
diff --git a/src/pages/leaderboards.jsx b/src/pages/leaderboards.jsx
index c2cd992c..2a126489 100644
--- a/src/pages/leaderboards.jsx
+++ b/src/pages/leaderboards.jsx
@@ -273,7 +273,7 @@ export default function Leaderboard() {
>
{entry.user.username}
{entry.user.role === 'ADMIN' && (
- Developer
+ Admin
)}
{entry.user.role === 'PRO' && (
Pro
diff --git a/src/pages/learn/ch1/activity1.jsx b/src/pages/learn/ch1/activity1.jsx
deleted file mode 100644
index 6f607060..00000000
--- a/src/pages/learn/ch1/activity1.jsx
+++ /dev/null
@@ -1,142 +0,0 @@
-import Head from 'next/head';
-import { useRouter } from 'next/router';
-import { Footer } from '@/components/Footer';
-import { motion } from 'framer-motion';
-
-import { StandardNav } from '@/components/StandardNav';
-import { useEffect, useState } from 'react';
-import { LearnNav } from '@/components/learn/LearnNav';
-import QuizPage from '@/components/learn/QuizPage';
-
-export default function Dashboard() {
- // const [open, setOpen] = useState(true)
- // const [markdown, setMarkdown] = useState("");
- const [quizPage, setQuizPage] = useState(1);
-
- const router = useRouter();
-
- // Get the `quizPage` query param from the URL, default to 1
- useEffect(() => {
- const page = parseInt(router.query.quizPage, 10) || 1;
- if (page !== quizPage) {
- setQuizPage(page);
- }
- }, [router.query.quizPage]);
-
- const quizData = [
- {
- question: 'What is Linux?',
- answers: [
- 'An operating system',
- 'A programming language',
- 'A video game',
- 'A web browser',
- ],
- solution: 'An operating system',
- },
- {
- question: 'Which command is used to list files and directories in Linux?',
- answers: ['pwd', 'ls', 'cd', 'cat'],
- solution: 'ls',
- },
- {
- question:
- 'Which command is used to change the permissions of a file in Linux?',
- answers: ['chmod', 'chown', 'chgrp', 'chmodx'],
- solution: 'chmod',
- },
- {
- question: 'Which command is used to create a new directory in Linux?',
- answers: ['mkdir', 'touch', 'cp', 'mv'],
- solution: 'mkdir',
- },
- {
- question:
- 'Which command is used to search for a specific string in a file in Linux?',
- answers: ['grep', 'find', 'locate', 'whereis'],
- solution: 'grep',
- },
- {
- question: 'Which command is used to remove a directory in Linux?',
- answers: ['rmdir', 'rm', 'mv', 'cp'],
- solution: 'rmdir',
- },
- ];
-
- return (
- <>
-
- Learn - CTFGuide
-
-
-
-
-
-
-
-
- {/* Sidebar */}
-
-
- {/* Main content area */}
-
-
-
-
- Mastery Task
-
-
-
- { router.push('./video1') }} className='bg-neutral-900 hover:bg-blue-800/100 px-4 py-1 text-xl rounded-l-sm'>←
- 3
- { router.push('./dynamic1') }} className='mr-4 bg-neutral-900 hover:bg-blue-800 px-4 py-1 text-xl rounded-r-sm'>→
-
-
-
-
-
-
-
-
-
-
-
- >
- );
-}
diff --git a/src/pages/learn/ch1/dynamic1.jsx b/src/pages/learn/ch1/dynamic1.jsx
deleted file mode 100644
index 0ca4dd29..00000000
--- a/src/pages/learn/ch1/dynamic1.jsx
+++ /dev/null
@@ -1,342 +0,0 @@
-import Head from 'next/head';
-
-import {Footer} from '@/components/Footer';
-
-import {StandardNav} from '@/components/StandardNav';
-import {useEffect, useState} from 'react';
-import {LearnNav} from '@/components/learn/LearnNav';
-import {LearnCore} from '@/components/LearnCore';
-import {MarkDone} from '@/components/learn/MarkDone';
-import {LearnCoreMaster} from "@/components/LearnCoreMaster";
-import {CheckCircleIcon} from "@heroicons/react/20/solid";
-
-export default function Dashboard() {
- const [open, setOpen] = useState(true);
- const [markdown, setMarkdown] = useState('');
- const [terminalUsername, setTerminalUsername] = useState('...');
- const [terminalPassword, setTerminalPassword] = useState('...');
- // Start Terminal
- useEffect(() => {
-
- const fetchTerminalData = async () => {
- try {
- const endPoint = 'https://terminal-gateway.ctfguide.com/createvm';
- const requestOptions = {
- method: 'GET',
- };
- const response = await fetch(endPoint, requestOptions);
- const result = await response.json();
-
- setTerminalUsername(result.username);
- setTerminalPassword(result.password);
- } catch (err) {
- console.log(err);
- setTerminalUsername('Something went wrong.');
- setTerminalPassword('Something went wrong.');
- }
- };
-
- try {
- fetchTerminalData();
- } catch (err) {
- console.log(err);
- setTerminalUsername('Something went wrong.');
- setTerminalPassword('Something went wrong.');
- }
- }, []);
- useEffect(() => {
- const fetchData = async () => {
- const response = await fetch('https://gist.githubusercontent.com/rt2zz/e0a1d6ab2682d2c47746950b84c0b6ee/raw/83b8b4814c3417111b9b9bef86a552608506603e/markdown-sample.md');
- const data = await response.text();
- setMarkdown(data);
- };
- fetchData();
- }, []);
-
- return (<>
-
- Learn - CTFGuide
-
-
-
-
-
-
- {/* Load in markdown from a github url */}
-
-
-
-
Using your terminal
-
@pranavramesh
-
-
- Logging into your terminal.
-
-
- The black box to the right is your terminal. You can login to it
- using the following credentials:
-
-
-
- ctfguide login: {terminalUsername}
-
-
- Password: {terminalPassword}
-
-
-
-
- Let's explore your terminal!
-
-
- You'll notice that there seems to be no user interface for this
- operating system. That's because you need to run commands to use
- this OS. Let's run a command to see what files are in our computer.{' '}
-
-
-
- {terminalUsername}@ctfguide:~$ ls
-
-
-
-
- Looks like there's nothing in this directory! Let's make a folder.{' '}
-
-
-
- {terminalUsername}@ctfguide:~${' '}
- mkdir chapter1
-
-
-
-
-
-
-
-
{
- document.getElementById('2').classList.remove('hidden');
- document.getElementById('1').classList.add('hidden');
- }}
- className="w-full rounded-r-lg py-4 px-10 hover:bg-neutral-700"
- >
-
-
-
-
-
-
-
-
-
-
-
Using your terminal
-
@pranavramesh
-
-
- Let's explore your terminal! (Continued)
-
-
- Nice, you've made a folder. Let's run the ls command to see if it
- shows up now.
-
-
-
- {terminalUsername}@ctfguide:~$ ls
-
-
-
-
- Let's navigate into that folder now!
-
-
-
- {terminalUsername}@ctfguide:~${' '}
- cd chapter1
-
-
-
-
- Cool, we're now in the{' '}
-
- /chapter1
- {' '}
- folder. Let's create a file now!
-
-
-
- Creating files
-
-
-
- To create a file, we use the{' '}
-
- touch
- {' '}
- command. But this command will only create a blank file with the
- name that you supply in the parameter.
-
-
-
- However, lets say you want to make a text file. How would you go
- about doing so? Well, lucky for you there's a pretty handy tool
- called Nano .
-
-
-
-
-
-
-
-
GNU Nano
-
- GNU nano is a text editor for Unix-like operating systems, such
- as Linux. It is designed to be easy to use and user-friendly,
- especially for beginners.
-
-
-
-
-
- Lets create a text file called NanoFun.txt
-
-
-
- {terminalUsername}@ctfguide:~${' '}
- nano NanoFun.txt
-
-
-
-
- This should replace your window with the Nano editor. Your friend,
- Ray, asks you to write his journal entry for him as he broke his
- arms.
-
-
-
-
- GNU nano 5.4
- New Buffer
-
-
-
- I love Linux so much that I replaced my mom's operating system
- with it. She is now very mad at me. :(
-
-
-
-
- To save the file, press{' '}
- Ctrl + X {' '}
- and then press{' '}
- Y {' '}
- to confirm the save.
-
-
-
- You can view a the file by running the command{' '}
- cat /NanoFun.txt
- .
-
-
- {/* bottom footer */}
-
-
-
-
-
{
- document.getElementById('1').classList.remove('hidden');
- document.getElementById('2').classList.add('hidden');
- }}
- >
-
-
-
-
-
{
- document.getElementById('3').classList.remove('hidden');
- document.getElementById('2').classList.add('hidden');
- }}
- >
-
-
-
-
-
-
-
-
-
-
-
- Nice work!
-
-
- You've finished this lesson.
-
-
-
-
-
-
- {/**/}
- {/*
Developer Tools */}
-
- {/*
Terminal Keylogger
*/}
- {/*
*/}
-
- {/*
External Testing Client
*/}
- {/*
*/}
- {/*
*/}
-
-
-
-
- >);
-}
diff --git a/src/pages/learn/ch1/preview.jsx b/src/pages/learn/ch1/preview.jsx
deleted file mode 100644
index 5c3c118f..00000000
--- a/src/pages/learn/ch1/preview.jsx
+++ /dev/null
@@ -1,145 +0,0 @@
-import Head from 'next/head';
-
-import { Footer } from '@/components/Footer';
-
-import { StandardNav } from '@/components/StandardNav';
-import { LearnNav } from '@/components/learn/LearnNav';
-import { MarkDone } from '@/components/learn/MarkDone';
-import { motion } from 'framer-motion';
-import { useState, useEffect, Fragment } from 'react';
-import { Disclosure, Menu, Transition, Dialog } from '@headlessui/react';
-
-
-import { useRouter } from 'next/router';
-
-
-
-export default function Dashboard() {
- const [open, setOpen] = useState(false);
- const router = useRouter();
-
- const [markdown, setMarkdown] = useState('');
-
- return (
- <>
-
- Learn - CTFGuide
-
-
-
-
-
-
-
-
-
-
- console.log("hello")
-
-
-
- {/* Sidebar */}
-
-
- {/* Main content area */}
-
- {/* Load in markdown from a github url */}
-
-
-
- What is Linux
-
-
- { router.push('../learn') }} className='bg-neutral-900 hover:bg-blue-800/100 px-4 py-1 text-xl rounded-l-sm'>←
- 1
-
- { router.push('./video1') }} className='mr-4 bg-neutral-900 hover:bg-blue-800 px-4 py-1 text-xl rounded-r-sm'>→
-
-
-
-
-
-
-
-
-
A brief introduction...
- Linux is a free and{' '}
- setOpen(true)}>
- open source
- {' '}
- operating system. It is developed by Linus Torvalds, a programmer
- and the creator of the Linux operating system. Development of
- Linux originially started in 1991 and has quickly grown to be one
- of the most popular operating systems in the world.
- The syntax of Linux commands are very similar to that of
- Unix. You'll find that in the tech world, that the Linux operating
- system is the most popular choice for servers. Although, Windows
- and MacOS can still be used as servers. A plethora of
- cybersecurity tools are generally designed for Linux. There are
- many Linux Distributions that you can use, the most popular one
- for cybersecurity is Kali Linux. It's advised that if you're new
- to Linux you use a distribution like Ubuntu.
-
-
-
-
Picture of Linus Torwalds, the creator of Linux
-
-
-
-
- Understanding the Linux architecture.
-
- Linux architecture is based on a modular approach. The kernel,
- which is the core of the operating system, manages the resources
- of the computer and provides services to applications. The shell
- is a command line interpreter that allows users to interact with
- the kernel. The hardware consists of the physical components of
- the computer, such as the processor, memory, and storage devices.
- The utilities are programs that provide functions that are not
- directly related to the operation of the computer, such as text
- editors and file managers.
-
-
-
-
-
-
-
-
- >
- );
-}
diff --git a/src/pages/learn/ch1/video1.jsx b/src/pages/learn/ch1/video1.jsx
deleted file mode 100644
index 1cdaef58..00000000
--- a/src/pages/learn/ch1/video1.jsx
+++ /dev/null
@@ -1,157 +0,0 @@
-import Head from 'next/head';
-
-import { Footer } from '@/components/Footer';
-
-import { StandardNav } from '@/components/StandardNav';
-import { useEffect, useState } from 'react';
-import { LearnNav } from '@/components/learn/LearnNav';
-import { MarkDone } from '@/components/learn/MarkDone';
-import { motion } from 'framer-motion';
-import { useRouter } from 'next/router';
-
-export default function Dashboard() {
- const [open, setOpen] = useState(true);
- const [markdown, setMarkdown] = useState('');
- const [terminalUsername, setTerminalUsername] = useState('...');
- const [terminalPassword, setTerminalPassword] = useState('...');
-
- // Start Terminal
- useEffect(() => {
-
- const fetchTerminalData = async () => {
- try {
- const endPoint = 'https://terminal-gateway.ctfguide.com/createvm';
- const requestOptions = {
- method: 'GET',
- };
- const response = await fetch(endPoint, requestOptions);
- const result = await response.json();
-
- setTerminalUsername(result.username);
- setTerminalPassword(result.password);
- } catch (err) {
- console.log(err);
- setTerminalUsername('Something went wrong.');
- setTerminalPassword('Something went wrong.');
- }
- };
-
- try {
- fetchTerminalData();
- } catch (err) {
- console.log(err);
- setTerminalUsername('Something went wrong.');
- setTerminalPassword('Something went wrong.');
- }
- }, []);
- useEffect(() => {
- const fetchData = async () => {
- const response = await fetch(
- 'https://gist.githubusercontent.com/rt2zz/e0a1d6ab2682d2c47746950b84c0b6ee/raw/83b8b4814c3417111b9b9bef86a552608506603e/markdown-sample.md'
- );
- const data = await response.text();
- setMarkdown(data);
- };
- fetchData();
- }, []);
- const router = useRouter();
-
-
- return (
- <>
-
- Learn - CTFGuide
-
-
-
-
-
-
-
-
- {/* Sidebar */}
-
-
- {/* Main content area */}
-
- {/* Load in markdown from a github url */}
-
-
- Command Basics
-
-
-
- { router.push('./preview') }} className='bg-neutral-900 hover:bg-blue-800/100 px-4 py-1 text-xl rounded-l-sm'>←
- 2
- { router.push('./activity1') }} className='mr-4 bg-neutral-900 hover:bg-blue-800 px-4 py-1 text-xl rounded-r-sm'>→
-
-
-
-
-
-
VIDEO
-
-
-
- Terminal (Beta) Login as{' '}
- {terminalUsername} using
- the password{' '}
- {terminalPassword}
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
- );
-}
diff --git a/src/pages/learn/ch2/activity2.jsx b/src/pages/learn/ch2/activity2.jsx
deleted file mode 100644
index 7e136556..00000000
--- a/src/pages/learn/ch2/activity2.jsx
+++ /dev/null
@@ -1,139 +0,0 @@
-import Head from 'next/head';
-import { useRouter } from 'next/router';
-import { Footer } from '@/components/Footer';
-
-import { StandardNav } from '@/components/StandardNav';
-import { useEffect, useState } from 'react';
-import { LearnNav } from '@/components/learn/LearnNav';
-import QuizPage from '@/components/learn/QuizPage';
-
-export default function Dashboard() {
- const [quizPage, setQuizPage] = useState(1);
-
- const router = useRouter();
-
- // Get the `quizPage` query param from the URL, default to 1
- useEffect(() => {
- const page = parseInt(router.query.quizPage, 10) || 1;
- if (page !== quizPage) {
- setQuizPage(page);
- }
- }, [router.query.quizPage]);
-
- const quizData = [
- {
- question: 'What does forensics in CTF competitions involve?',
- answers: [
- 'The process of analyzing digital data to gather evidence or intelligence',
- 'The process of analyzing physical data to gather evidence or intelligence',
- 'The process of analyzing verbal data to gather evidence or intelligence',
- 'None of the above',
- ],
- solution:
- 'The process of analyzing digital data to gather evidence or intelligence',
- },
- {
- question: 'What are some examples of CTF forensics challenges?',
- answers: [
- 'File or disk image analysis',
- 'Network packet analysis',
- 'Memory dump analysis',
- 'All of the above',
- ],
- solution: 'All of the above',
- },
- {
- question: 'What is a hex editor used for in CTF forensics challenges?',
- answers: [
- 'Analyzing and manipulating binary data in files',
- 'Analyzing and manipulating text data in files',
- 'Analyzing and manipulating image data in files',
- 'None of the above',
- ],
- solution: 'Analyzing and manipulating binary data in files',
- },
- {
- question:
- 'What is steganography detection software used for in CTF forensics challenges?',
- answers: [
- 'Detecting hidden messages or data within files',
- 'Detecting hidden passwords within network traffic',
- 'Detecting hidden data within memory dumps',
- 'None of the above',
- ],
- solution: 'Detecting hidden messages or data within files',
- },
- {
- question: 'What is the primary goal of CTF forensics challenges?',
- answers: [
- 'To hack into a system or network',
- 'To extract hidden information or uncover clues',
- 'To create digital artifacts or files',
- 'None of the above',
- ],
- solution: 'To extract hidden information or uncover clues',
- },
- {
- secret:
- 'Yes, the questions and answers are stored on the frontend! You must be a forensics expert! Contact Ray for a shoutout',
- question:
- 'What skills are important for success in CTF forensics challenges?',
- answers: [
- 'Understanding of digital forensics principles and techniques',
- 'Proficiency in using various tools and software for analyzing digital data',
- 'Both A and B',
- 'None of the above',
- ],
- solution: 'Both A and B',
- },
- ];
-
- return (
- <>
-
- What is Forensics? - CTFGuide
-
-
-
-
-
-
-
- What is Forensics?
-
-
- {/* Sidebar */}
-
-
- {/* Main content area */}
-
-
-
-
-
- >
- );
-}
diff --git a/src/pages/learn/ch2/dynamic2.jsx b/src/pages/learn/ch2/dynamic2.jsx
deleted file mode 100644
index c2cbc36c..00000000
--- a/src/pages/learn/ch2/dynamic2.jsx
+++ /dev/null
@@ -1,50 +0,0 @@
-
-import Head from 'next/head';
-
-import { Footer } from '@/components/Footer';
-
-import { StandardNav } from '@/components/StandardNav';
-import { useEffect, useState } from 'react';
-import { LearnNav } from '@/components/learn/LearnNav';
-import { LearnCore } from '@/components/LearnCore2';
-import { MarkDone } from '@/components/learn/MarkDone';
-
-export default function Dashboard() {
- const [open, setOpen] = useState(true);
- const [markdown, setMarkdown] = useState('');
-
- useEffect(() => {
- const fetchData = async () => {
- const response = await fetch(
- 'https://gist.githubusercontent.com/rt2zz/e0a1d6ab2682d2c47746950b84c0b6ee/raw/83b8b4814c3417111b9b9bef86a552608506603e/markdown-sample.md'
- );
- const data = await response.text();
- setMarkdown(data);
- };
- fetchData();
- }, []);
-
- return (
- <>
-
- Learn - CTFGuide
-
-
-
-
-
-
- {/* Load in markdown from a github url */}
-
-
-
-
- >
- );
-}
diff --git a/src/pages/learn/ch2/preview.jsx b/src/pages/learn/ch2/preview.jsx
deleted file mode 100644
index b41e1414..00000000
--- a/src/pages/learn/ch2/preview.jsx
+++ /dev/null
@@ -1,355 +0,0 @@
-import Head from 'next/head';
-
-import { Footer } from '@/components/Footer';
-
-import { StandardNav } from '@/components/StandardNav';
-import { LearnNav } from '@/components/learn/LearnNav';
-import { MarkDone } from '@/components/learn/MarkDone';
-import { motion } from 'framer-motion';
-import { useState, useEffect, Fragment } from 'react';
-import { Disclosure, Menu, Transition, Dialog } from '@headlessui/react';
-
-import { useRouter } from 'next/router';
-
-
-export default function Dashboard() {
- const [open, setOpen] = useState(false);
- const [markdown, setMarkdown] = useState('');
- const router = useRouter();
-
- return (
- <>
-
- What is Forensics? - CTFGuide
-
-
-
-
-
-
-
-
- {/* Sidebar */}
-
-
- {/* Main content area */}
-
- {/* Load in markdown from a github url */}
-
-
- What is Forensics
-
-
- { router.push('../learn') }} className='bg-neutral-900 hover:bg-blue-800/100 px-4 py-1 text-xl rounded-l-sm'>←
- 1
-
- { router.push('./video2') }} className='mr-4 bg-neutral-900 hover:bg-blue-800 px-4 py-1 text-xl rounded-r-sm'>→
-
-
-
-
-
-
Intro: Forensics
- Forensics, in the context of cybersecurity, refers to the process
- of analyzing digital data in order to gather evidence or
- intelligence that can be used in investigations or legal
- proceedings. In particular, forensics in Capture The Flag (CTF)
- competitions involves the examination and analysis of a given set
- of digital artifacts or files to extract hidden information or
- uncover clues that will help the participant solve a particular
- challenge or task. CTF forensics challenges may take a variety of
- forms, such as file or disk image analysis, network packet
- analysis, memory dump analysis, or even steganography. In each
- case, participants are typically given a set of artifacts or data
- to work with, and must use their knowledge of digital forensics
- techniques to uncover hidden information or identify patterns that
- will lead them to the solution.
-
- Examples{' '}
- setOpen(true)}
- >
-
- {' '}
-
- For example, in a file analysis challenge, participants may be
- given a file with a specific file format and asked to extract
- certain information from it. They may use various tools and
- techniques to analyze the file structure, examine metadata, or
- search for hidden data that may be encoded within the file. In a
- network packet analysis challenge, participants may be given a
- network capture file and asked to identify specific packets or
- extract certain information from them, such as passwords or IP
- addresses. To succeed in CTF forensics challenges, participants
- need a strong understanding of digital forensics principles and
- techniques, as well as proficiency in using a range of tools and
- software for analyzing digital data. Some of the most commonly
- used tools in CTF forensics challenges include hex editors,
- forensic analysis software, network analysis tools, and
- steganography detection software. Overall, CTF forensics
- challenges offer an exciting and challenging way to test and
- develop skills in digital forensics and cybersecurity, and provide
- valuable experience for those pursuing careers in these fields.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- setOpen(false)}
- >
- Close panel
-
-
-
-
-
-
-
- Key Examples
-
-
-
-
-
-
- Unlock Hint
-
-
-
-
-
-
-
-
-
-
- Unlock Hint
-
-
-
-
-
-
-
-
-
-
- Upgrade to PRO
-
-
-
-
-
-
- {/* Replace with your content */}
-
-
- Examples in the
- real world:
-
-
-
-
- Identifying and tracing the source of a
- cyberattack: Forensic cybersecurity experts use
- various techniques such as network traffic
- analysis, malware analysis, and log analysis to
- identify and track the source of a cyberattack.
- For instance, if a company's system is hacked,
- forensic analysts may use forensic imaging tools
- to capture the state of the system at the time of
- the breach and then analyze the captured data to
- identify the hacker's IP address and other
- relevant details.
-
-
-
-
- Recovering lost or stolen data: Forensic
- cybersecurity experts can help retrieve lost or
- stolen data by analyzing the computer or device
- used to store the information. They use advanced
- data recovery techniques to retrieve deleted
- files, corrupted data, and other types of lost
- data. For example, if a company's critical data is
- deleted or lost due to a cyber attack, forensic
- analysts can use data recovery tools to recover
- the lost data and help the company recover from
- the breach.
-
-
-
-
- Investigating cybercrime: Forensic cybersecurity
- experts play a vital role in investigating
- cybercrime. They work closely with law enforcement
- agencies to collect and analyze digital evidence
- related to cybercrime. This evidence can be used
- in court to prosecute cybercriminals. For
- instance, forensic analysts may investigate cases
- of identity theft, online fraud, hacking, and
- other cybercrimes by collecting and analyzing
- evidence such as log files, email records, and
- social media activity.
-
-
-
-
-
-
- Tooling Examples:
-
-
-
-
- EnCase Forensic
- {' '}
- - A commercial digital forensics software suite
- used for forensic analysis of computer systems,
- mobile devices, and other electronic devices.
-
-
-
-
- Wireshark {' '}
- - A free and open-source packet analyzer used for
- network troubleshooting, analysis, software and
- communications protocol development, and
- education.
-
-
-
-
-
- Sleuth Kit
- {' '}
- - A collection of free and open-source tools for
- digital forensics investigations on Unix and
- Linux-based systems.
-
-
-
-
- {/* /End replace */}
-
-
-
How do hints work?
-
- Your first hint will only allow you to earn 1/2 of
- the points avaliable for this challenge.
-
-
-
- Your second hint will only allow you to earn 1/3 of
- the points avaliable for this challenge.
-
-
-
- Viewing the answer will simply mark the challenge
- solved for you and not award you any points. This
- feature is only for pro members.
-
-
-
-
-
-
-
-
-
-
-
-
- >
- );
-}
diff --git a/src/pages/learn/ch2/video2.jsx b/src/pages/learn/ch2/video2.jsx
deleted file mode 100644
index f295cc21..00000000
--- a/src/pages/learn/ch2/video2.jsx
+++ /dev/null
@@ -1,111 +0,0 @@
-import Head from 'next/head';
-
-import { Footer } from '@/components/Footer';
-import { StandardNav } from '@/components/StandardNav';
-import { useEffect, useState } from 'react';
-import { LearnNav } from '@/components/learn/LearnNav';
-import { MarkDone } from '@/components/learn/MarkDone';
-import { motion } from 'framer-motion';
-import { useRouter } from 'next/router';
-
-
-export default function Dashboard() {
-
- const router = useRouter();
- const [open, setOpen] = useState(true);
- const [markdown, setMarkdown] = useState('');
-
- useEffect(() => {
- const fetchData = async () => {
- const response = await fetch(
- 'https://gist.githubusercontent.com/rt2zz/e0a1d6ab2682d2c47746950b84c0b6ee/raw/83b8b4814c3417111b9b9bef86a552608506603e/markdown-sample.md'
- );
- const data = await response.text();
- setMarkdown(data);
- };
- fetchData();
- }, []);
-
- return (
- <>
-
- What is Forensics? - CTFGuide
-
-
-
-
-
-
-
- What is Forensics?
-
-
- {/* Sidebar */}
-
-
- {/* Main content area */}
-
- {/* Load in markdown from a github url */}
-
-
- Cyberchef 101
-
-
-
- { router.push('./preview') }} className='bg-neutral-900 hover:bg-blue-800/100 px-4 py-1 text-xl rounded-l-sm'>←
- 2
- { router.push('./dynamic2') }} className='mr-4 bg-neutral-900 hover:bg-blue-800 px-4 py-1 text-xl rounded-r-sm'>→
-
-
-
-
-
-
VIDEO
-
-
-
-
-
-
-
-
-
-
- >
- );
-}
diff --git a/src/pages/learn/ch3/activity3.jsx b/src/pages/learn/ch3/activity3.jsx
deleted file mode 100644
index 406c5de3..00000000
--- a/src/pages/learn/ch3/activity3.jsx
+++ /dev/null
@@ -1,143 +0,0 @@
-import Head from 'next/head';
-import { useRouter } from 'next/router';
-import { Footer } from '@/components/Footer';
-
-import { StandardNav } from '@/components/StandardNav';
-import { useEffect, useState } from 'react';
-import { LearnNav } from '@/components/learn/LearnNav';
-import QuizPage from '@/components/learn/QuizPage';
-
-export default function Dashboard() {
- const [quizPage, setQuizPage] = useState(1);
-
- const router = useRouter();
-
- // Get the `quizPage` query param from the URL, default to 1
- useEffect(() => {
- const page = parseInt(router.query.quizPage, 10) || 1;
- if (page !== quizPage) {
- setQuizPage(page);
- }
- }, [router.query.quizPage]);
-
- const quizData = [
- {
- question: 'What is Cryptography?',
- answers: [
- 'The practice of securing information by transforming it into an unreadable format using various mathematical algorithms and techniques',
- 'The process of decoding encrypted messages',
- 'The process of breaking into a computer system',
- 'The study of secret codes used in ancient times',
- ],
- solution:
- 'The practice of securing information by transforming it into an unreadable format using various mathematical algorithms and techniques',
- },
- {
- question: 'What are the two main types of cryptography?',
- answers: [
- 'Symmetric-key cryptography and asymmetric-key cryptography',
- 'Hashing and salting',
- 'Social engineering and phishing',
- 'Encryption and decryption',
- ],
- solution: 'Symmetric-key cryptography and asymmetric-key cryptography',
- },
- {
- question:
- 'Which type of cryptography involves the use of a single shared secret key?',
- answers: [
- 'Symmetric-key cryptography',
- 'Asymmetric-key cryptography',
- 'Hashing',
- 'None of the above',
- ],
- solution: 'Symmetric-key cryptography',
- },
- {
- question:
- 'Which type of cryptography involves the use of two keys: a public key and a private key?',
- answers: [
- 'Symmetric-key cryptography',
- 'Asymmetric-key cryptography',
- 'Hashing',
- 'None of the above',
- ],
- solution: 'Asymmetric-key cryptography',
- },
- {
- question:
- 'Which cryptographic algorithm is commonly used for encrypting data in transit on the internet?',
- answers: ['RSA', 'AES', 'SSL', 'SHA-256'],
- solution: 'SSL',
- },
- {
- question: 'What is the purpose of a digital signature in cryptography?',
- answers: [
- 'To verify the authenticity and integrity of digital documents',
- 'To encrypt data',
- 'To exchange secret keys between parties',
- 'None of the above',
- ],
- solution: 'To verify the authenticity and integrity of digital documents',
- },
- ];
-
- return (
- <>
-
- Cryptography! - CTFGuide
-
-
-
-
-
-
-
-
-
- Cryptography
-
-
-
-
This lesson will temporarily serve a general overview until our content creation team finishes the new/updated lesson. We aren't exactly pleased with the current state of this learning module.
-
-
-
- {/* Sidebar */}
-
-
- {/* Main content area */}
-
-
-
-
-
- >
- );
-}
diff --git a/src/pages/learn/ch3/dynamic3.jsx b/src/pages/learn/ch3/dynamic3.jsx
deleted file mode 100644
index 07dd8817..00000000
--- a/src/pages/learn/ch3/dynamic3.jsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import Head from 'next/head';
-
-import { Footer } from '@/components/Footer';
-
-import { StandardNav } from '@/components/StandardNav';
-import { useEffect, useState } from 'react';
-import { LearnNav } from '@/components/learn/LearnNav';
-import { LearnCore } from '@/components/LearnCore3';
-import { MarkDone } from '@/components/learn/MarkDone';
-
-export default function Dashboard() {
- const [open, setOpen] = useState(true);
- const [markdown, setMarkdown] = useState('');
-
- useEffect(() => {
- const fetchData = async () => {
- const response = await fetch(
- 'https://gist.githubusercontent.com/rt2zz/e0a1d6ab2682d2c47746950b84c0b6ee/raw/83b8b4814c3417111b9b9bef86a552608506603e/markdown-sample.md'
- );
- const data = await response.text();
- setMarkdown(data);
- };
- fetchData();
- }, []);
-
- return (
- <>
-
- Learn - CTFGuide
-
-
-
-
-
-
- {/* Load in markdown from a github url */}
-
-
-
-
- >
- );
-}
diff --git a/src/pages/learn/ch3/preview.jsx b/src/pages/learn/ch3/preview.jsx
deleted file mode 100644
index 685cd56b..00000000
--- a/src/pages/learn/ch3/preview.jsx
+++ /dev/null
@@ -1,170 +0,0 @@
-import Head from 'next/head';
-
-import { Footer } from '@/components/Footer';
-import { StandardNav } from '@/components/StandardNav';
-import { useState } from 'react';
-import { LearnNav } from '@/components/learn/LearnNav';
-import { MarkDone } from '@/components/learn/MarkDone';
-import { motion } from 'framer-motion';
-import { useRouter } from 'next/router';
-export default function Dashboard() {
- const [open, setOpen] = useState(true);
- const [markdown, setMarkdown] = useState('');
- const router = useRouter();
-
-
- return (
- <>
-
- Cryptography! - CTFGuide
-
-
-
-
-
-
-
-
-
- Cryptography
-
-
-
-
This lesson will temporarily serve a general overview until our content creation team finishes the new/updated lesson. We aren't exactly pleased with the current state of this learning module.
-
-
-
- {/* Sidebar */}
-
-
- {/* Main content area */}
-
- {/* Load in markdown from a github url */}
-
-
- Introduction to Cryptography
-
-
- { router.push('../learn') }} className='bg-neutral-900 hover:bg-blue-800/100 px-4 py-1 text-xl rounded-l-sm'>←
- 1
-
- { router.push('./video3') }} className='mr-4 bg-neutral-900 hover:bg-blue-800 px-4 py-1 text-xl rounded-r-sm'>→
-
-
-
-
-
-
-
- What is Cryptography?
-
-
- Cryptography, in the context of cybersecurity, refers to the
- practice of securing information by transforming it into an
- unreadable format using various mathematical algorithms and
- techniques.
-
-
- Cryptography is widely used in the field of cybersecurity to
- protect sensitive information such as passwords, credit card
- numbers, and other personal or confidential data from
- unauthorized access, interception, or theft. In Capture The Flag
- (CTF) competitions, cryptography challenges often involve the
- use of cryptographic techniques to encrypt or decrypt messages
- or data. Participants are usually given a cipher or encryption
- algorithm, and they must use their knowledge of cryptography to
- crack the code and obtain the hidden information.
-
-
-
- There are two main types of cryptography: symmetric-key
- cryptography and asymmetric-key cryptography.
-
-
-
- Symmetric-key cryptography
-
- Symmetric-key cryptography involves the use of a single shared
- secret key that is used both to encrypt and decrypt data. The same
- key must be known by both the sender and the receiver in order for
- them to securely communicate with each other. Examples of
- symmetric-key algorithms used in CTF challenges include Caesar
- Cipher, Vigenère Cipher, and the Advanced Encryption Standard
- (AES).
-
-
- In the Caeser Cipher, alphabetical values are shifted by a
- certain amount.
-
-
- Asymmetric-key cryptography
-
- Asymmetric-key cryptography, also known as public-key
- cryptography, involves the use of two keys: a public key and a
- private key. The public key is used to encrypt data, while the
- private key is used to decrypt it. The private key must be kept
- secret by the owner, while the public key can be shared with
- anyone. Examples of asymmetric-key algorithms used in CTF
- challenges include RSA and Elliptic Curve Cryptography (ECC).
-
- Other use cases?
-
- In addition to encryption and decryption, cryptography is also
- used for other purposes such as digital signatures, key exchange,
- and authentication. Digital signatures are used to verify the
- authenticity and integrity of digital documents, while key
- exchange is used to securely exchange secret keys between parties.
- Authentication is used to verify the identity of a user or system
- before granting access to protected resources.
-
-
- To succeed in CTF cryptography challenges, participants need a
- strong understanding of cryptographic principles and
- techniques, as well as proficiency in using a range of tools
- and software for analyzing and cracking ciphers. Some of the
- most commonly used tools in CTF cryptography challenges
- include frequency analysis tools, hash cracking tools, and
- cipher identification tools. Overall, CTF cryptography
- challenges offer an exciting and challenging way to test and
- develop skills in cryptography and cybersecurity, and provide
- valuable experience for those pursuing careers in these
- fields.
-
-
-
-
-
-
-
-
- >
- );
-}
diff --git a/src/pages/learn/ch3/video3.jsx b/src/pages/learn/ch3/video3.jsx
deleted file mode 100644
index e4963ddf..00000000
--- a/src/pages/learn/ch3/video3.jsx
+++ /dev/null
@@ -1,116 +0,0 @@
-import Head from 'next/head';
-
-import { Footer } from '@/components/Footer';
-import { StandardNav } from '@/components/StandardNav';
-import { useEffect, useState } from 'react';
-import { LearnNav } from '@/components/learn/LearnNav';
-import { MarkDone } from '@/components/learn/MarkDone';
-import { motion } from 'framer-motion';
-
-export default function Dashboard() {
- const [open, setOpen] = useState(true);
- const [markdown, setMarkdown] = useState('');
-
- useEffect(() => {
- const fetchData = async () => {
- const response = await fetch(
- 'https://gist.githubusercontent.com/rt2zz/e0a1d6ab2682d2c47746950b84c0b6ee/raw/83b8b4814c3417111b9b9bef86a552608506603e/markdown-sample.md'
- );
- const data = await response.text();
- setMarkdown(data);
- };
- fetchData();
- }, []);
-
- return (
- <>
-
- Cryptography! - CTFGuide
-
-
-
-
-
-
-
-
-
- Cryptography
-
-
-
-
This lesson will temporarily serve a general overview until our content creation team finishes the new/updated lesson. We aren't exactly pleased with the current state of this learning module.
-
-
-
- {/* Sidebar */}
-
-
- {/* Main content area */}
-
- {/* Load in markdown from a github url */}
-
-
-
- Cryptography Outline
-
-
-
- { router.push('./preview') }} className='bg-neutral-900 hover:bg-blue-800/100 px-4 py-1 text-xl rounded-l-sm'>←
- 2
- { router.push('./dynamic3') }} className='mr-4 bg-neutral-900 hover:bg-blue-800 px-4 py-1 text-xl rounded-r-sm'>→
-
-
-
-
-
-
-
VIDEO
-
-
-
This is a video sourced from University of Calgary's Information Security Club .
-
-
-
-
-
-
-
- >
- );
-}
diff --git a/src/pages/learn/modules.jsx b/src/pages/learn/modules.jsx
new file mode 100644
index 00000000..5a114d70
--- /dev/null
+++ b/src/pages/learn/modules.jsx
@@ -0,0 +1,61 @@
+import React from 'react';
+import { StandardNav } from '../../components/StandardNav';
+import ProgressSideBar from '../../components/learn/shared/ProgressSideBar';
+import ModuleLayout from '../../components/learn/modules/ModuleLayout';
+import UpNext from '../../components/learn/modules/UpNext';
+import { Footer } from '../../components/Footer';
+import { useState } from 'react';
+const ModulesPage = () => {
+ const [sidebarOpen, setSidebarOpen] = useState(true);
+ return (
+ <>
+
+
+ {/* main layout for learn */}
+
+
+
+ {/* Side bar */}
+ {sidebarOpen && (
+
+
+ setSidebarOpen(!sidebarOpen)} className="relative">
+
+ {sidebarOpen &&
}
+
+
+ )}
+
+
+ {/* Content */}
+
+ {/* Up Next */}
+
+ {!sidebarOpen && (
+ setSidebarOpen(!sidebarOpen)}
+ className="absolute bg-neutral-800 opacity-50 hover:opacity-100 px-2 rounded-r-full py-2 left-0 z-10"
+ >
+
+
+ )}
+
+
+
+ {/* Module Layout */}
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default ModulesPage;
\ No newline at end of file
diff --git a/src/pages/moderation.jsx b/src/pages/moderation.jsx
index 31fa1b8d..b4689c5e 100644
--- a/src/pages/moderation.jsx
+++ b/src/pages/moderation.jsx
@@ -396,7 +396,7 @@ export default function Competitions() {
Challenges Pending Approval
{selectedChallenges.length > 0 && Delete Selected }
- {pendingChallenges.length > 0 ? (
+ {pendingChallenges && pendingChallenges.length > 0 ? (
pendingChallenges.map((challenge) => (
{setSelectedId(challenge.id); setChallengeIsOpen(true);}} className='bg-neutral-800 w-full mb-2 border focus:bg-blue-900 focus:border-blue-500 border-neutral-700 hover:bg-neutral-700/50 cursor-pointer px-2 py-1 flex items-center text-sm'>
Submitted Reports
- {reports.length > 0 ? (
+ {reports && reports.length > 0 ? (
reports.map((report) => (