├── .dockerignore ├── .env.example ├── .eslintrc.cjs ├── .gitignore ├── AUTHORS.md ├── CODE-OF-CONDUCT.md ├── Dockerfile ├── LICENSE ├── README.md ├── index.html ├── jsconfig.json ├── package-lock.json ├── package.json ├── public ├── assets │ └── css │ │ ├── CreateChallenge.css │ │ ├── chatui.css │ │ └── leaderboards.css └── robots.txt ├── src ├── App.jsx ├── api.js ├── components │ ├── Faq.jsx │ ├── HelpCenter.jsx │ ├── LinkifyText.jsx │ ├── LoadingIcon.jsx │ ├── MetaComponent.jsx │ ├── ModalVideo.jsx │ ├── Preloader.jsx │ ├── RequireAuth.jsx │ ├── RequireDefinedAuth.jsx │ ├── RequireNoAuth.jsx │ ├── ScrollToTop.jsx │ ├── Socials.jsx │ └── Star.jsx ├── context │ ├── AuthProvider.jsx │ └── SignalRProvider.jsx ├── data │ ├── achievements.js │ ├── archives.js │ ├── campuses.js │ ├── contactLinks.js │ ├── courses.js │ ├── faq.js │ ├── features.js │ ├── footerLinks.js │ ├── herostory.js │ ├── learningPaths.js │ ├── links.js │ ├── menu.js │ ├── organization.js │ ├── socialLinks.js │ ├── teams.js │ ├── tesimonials.js │ ├── testimonials.js │ ├── topCategories.js │ └── whyourcourse.js ├── features │ ├── about │ │ ├── components │ │ │ ├── DevFounder.jsx │ │ │ ├── HeroStory.jsx │ │ │ ├── MissionVision.jsx │ │ │ ├── OurApproach.jsx │ │ │ └── OurStory.jsx │ │ └── index.js │ ├── achievements │ │ ├── components │ │ │ ├── AchievementsDetails.jsx │ │ │ └── AchievementsOne.jsx │ │ └── index.js │ ├── admin │ │ ├── audits │ │ │ ├── components │ │ │ │ ├── AuditsList.jsx │ │ │ │ └── AuditsQueryInput.jsx │ │ │ └── index.js │ │ ├── blacklist │ │ │ ├── components │ │ │ │ ├── AddEmailToBlackList.jsx │ │ │ │ └── BlacklistedEmails.jsx │ │ │ └── index.js │ │ ├── categories │ │ │ ├── components │ │ │ │ ├── CategoriesList.jsx │ │ │ │ └── CreateCategory.jsx │ │ │ └── index.js │ │ ├── challenges │ │ │ ├── components │ │ │ │ ├── ChallengeArtifacts.jsx │ │ │ │ ├── ChallengeHintUsages.jsx │ │ │ │ ├── ChallengeHints.jsx │ │ │ │ ├── ChallengeSolves.jsx │ │ │ │ ├── ChallengesList.jsx │ │ │ │ ├── ChallengesQueryInput.jsx │ │ │ │ ├── CreateChallenge.jsx │ │ │ │ └── EditChallenge.jsx │ │ │ └── index.js │ │ ├── configurations │ │ │ ├── components │ │ │ │ ├── CertificationIssuer.jsx │ │ │ │ ├── ChallengesLocked.jsx │ │ │ │ ├── IsCertificationEnabled.jsx │ │ │ │ ├── IsTurnstileEnabled.jsx │ │ │ │ ├── PublicLeaderboardCount.jsx │ │ │ │ └── SubmissionsAllowed.jsx │ │ │ └── index.js │ │ ├── keys │ │ │ ├── components │ │ │ │ ├── AccessKeysList.jsx │ │ │ │ └── CreateAccessKey.jsx │ │ │ └── index.js │ │ ├── submissions │ │ │ ├── components │ │ │ │ ├── LeaderboardGraph.jsx │ │ │ │ ├── RecalculateLeaderboardsButton.jsx │ │ │ │ └── UserRanksAdmin.jsx │ │ │ └── index.js │ │ └── users │ │ │ ├── components │ │ │ ├── AddCertificateButton.jsx │ │ │ ├── DeleteUserButton.jsx │ │ │ ├── DownloadCertificateButton.jsx │ │ │ ├── GeneratePasswordResetLinkButton.jsx │ │ │ ├── GenerateUserStatsButton.jsx │ │ │ ├── HideUserOnLeaderboardsButton.jsx │ │ │ ├── ResetCertificateButton.jsx │ │ │ ├── ShowUserOnLeaderboardsButton.jsx │ │ │ ├── UserDetails.jsx │ │ │ ├── UserEvaluations.jsx │ │ │ ├── UserGraph.jsx │ │ │ ├── UserHintUsages.jsx │ │ │ ├── UserSolves.jsx │ │ │ ├── UsersList.jsx │ │ │ ├── UsersQueryInput.jsx │ │ │ └── VerifyUserButton.jsx │ │ │ └── index.js │ ├── authentication │ │ ├── components │ │ │ ├── AccountHasVerified.jsx │ │ │ ├── AccountHasbeenCreated.jsx │ │ │ ├── AccountVerificationFailed.jsx │ │ │ ├── AccountVerificationLoading.jsx │ │ │ ├── ForgotPassword.jsx │ │ │ ├── LoginForm.jsx │ │ │ ├── PasswordResetCompleted.jsx │ │ │ ├── PasswordResetSent.jsx │ │ │ ├── ResetPassword.jsx │ │ │ ├── SignUpForm.jsx │ │ │ ├── VerifyCode.jsx │ │ │ └── VerifyEmail.jsx │ │ ├── index.js │ │ └── layout │ │ │ ├── AuthImageMove.jsx │ │ │ └── HeaderAuth.jsx │ ├── campuses │ │ ├── components │ │ │ └── CampusesBranch.jsx │ │ └── index.js │ ├── challengeDetails │ │ ├── components │ │ │ ├── ChallengeDetails.jsx │ │ │ ├── ChallengeDetailsArtifacts.jsx │ │ │ ├── ChallengeDetailsHints.jsx │ │ │ ├── ChallengeDetailsOld.jsx │ │ │ ├── ChallengeInfoCard.jsx │ │ │ └── RecentSolvers.jsx │ │ └── index.js │ ├── challenges │ │ ├── components │ │ │ ├── CategoryFilter.jsx │ │ │ ├── CategoryMobileFilter.jsx │ │ │ ├── ChallengesList.jsx │ │ │ ├── ChallengesPagination.jsx │ │ │ ├── ChallengesSortBy.jsx │ │ │ ├── ChallengesSortOrder.jsx │ │ │ ├── ExcludeSolvesFilter.jsx │ │ │ ├── ExcludeSolvesMobileFilter.jsx │ │ │ ├── FilterButton.jsx │ │ │ └── SidebarFilters.jsx │ │ ├── data │ │ │ └── challengesFilterOptions.js │ │ ├── index.js │ │ └── layout │ │ │ ├── ChallengesContainer.jsx │ │ │ └── ChallengesHeader.jsx │ ├── chatbot │ │ ├── components │ │ │ ├── ChatHeader.jsx │ │ │ ├── ChatInterface.jsx │ │ │ ├── MessageInput.jsx │ │ │ └── Result.jsx │ │ └── index.js │ ├── contact │ │ ├── components │ │ │ ├── Contact.jsx │ │ │ ├── DiscussionForum.jsx │ │ │ ├── MapComponent.jsx │ │ │ └── NotFound.jsx │ │ └── index.js │ ├── home │ │ ├── components │ │ │ ├── Achievements.jsx │ │ │ ├── CampusesTag.jsx │ │ │ ├── Features.jsx │ │ │ ├── FindLearningPath.jsx │ │ │ ├── HomeHero.jsx │ │ │ ├── LearningFeatures.jsx │ │ │ ├── Testimonials.jsx │ │ │ ├── TopCategories.jsx │ │ │ └── WhyPWNEU.jsx │ │ └── index.js │ ├── leaderboards │ │ ├── components │ │ │ ├── LeaderboardGraph.jsx │ │ │ ├── LeaderboardsLoading.jsx │ │ │ ├── NoLeaderboards.jsx │ │ │ └── UserRanks.jsx │ │ ├── index.js │ │ └── layout │ │ │ └── LeaderboardOverview.jsx │ ├── ownership │ │ ├── components │ │ │ ├── PrivacyPolicyTabs.jsx │ │ │ └── TermsAndConditionTabs.jsx │ │ └── index.js │ ├── profile │ │ ├── components │ │ │ ├── UserOverviewOld.jsx │ │ │ ├── UserProfile copy.md │ │ │ ├── UserProfileEvaluation.css │ │ │ ├── UserProfileEvaluationOld.jsx │ │ │ ├── UserProfileGraphOld.jsx │ │ │ ├── UserProfileHintUsagesOld.jsx │ │ │ ├── UserProfileOld.jsx │ │ │ └── UserProfileSolvesOld.jsx │ │ ├── data │ │ │ ├── dashboard.js │ │ │ └── platformoverview.js │ │ └── index.js │ └── user-profile │ │ ├── components │ │ ├── Charts.jsx │ │ ├── PieCharts.jsx │ │ ├── UserChangePassword.jsx │ │ ├── UserProfileCertify.jsx │ │ ├── UserProfileDetails.jsx │ │ ├── UserProfileEvaluation.jsx │ │ ├── UserProfileGraph.jsx │ │ ├── UserProfileOverview.jsx │ │ ├── UserProfileStatsReport.jsx │ │ └── UserReportOld.jsx │ │ ├── data │ │ ├── cardboards.js │ │ ├── certificate.js │ │ ├── hintusage.js │ │ ├── sidebarprofile.js │ │ └── solveoverview.js │ │ ├── index.js │ │ └── layout │ │ ├── UserProfileCertificate.jsx │ │ ├── UserProfileDashboard.jsx │ │ ├── UserProfileHintUsages.jsx │ │ ├── UserProfileSettings.jsx │ │ ├── UserProfileSidebar.jsx │ │ └── UserProfileSolves.jsx ├── hooks │ ├── useAuth.js │ └── useSignalR.js ├── index.jsx ├── layout │ ├── components │ │ ├── FooterLinks.jsx │ │ ├── Links.jsx │ │ ├── Menu.jsx │ │ ├── MobileFooter.jsx │ │ └── MobileMenu.jsx │ ├── footers │ │ ├── Footer.jsx │ │ └── FooterProfile.jsx │ └── headers │ │ ├── Header.jsx │ │ ├── HeaderAdmin.jsx │ │ └── HeaderProfile.jsx ├── pages │ ├── achievements │ │ ├── AchievementDetailsPage.jsx │ │ ├── AchievementsListPage.jsx │ │ └── index.js │ ├── admin │ │ ├── AccessKeysPage.jsx │ │ ├── AdminPage.jsx │ │ ├── AuditsPage.jsx │ │ ├── BlacklistPage.jsx │ │ ├── CategoriesPage.jsx │ │ ├── ChallengeDetailsAdminPage.jsx │ │ ├── ChallengesAdminPage.jsx │ │ ├── ConfigurationsPage.jsx │ │ ├── LeaderboardsAdminPage.jsx │ │ ├── UserDetailsPage.jsx │ │ ├── UsersPage.jsx │ │ └── index.js │ ├── authentication │ │ ├── ForgotPasswordPage.jsx │ │ ├── LoginPage.jsx │ │ ├── ResetPasswordPage.jsx │ │ ├── SignupPage.jsx │ │ ├── VerifyEmailPage.jsx │ │ └── index.js │ ├── chatbot │ │ ├── ChatBotPage.jsx │ │ └── index.js │ ├── contact │ │ ├── ContactPage.jsx │ │ ├── DiscussionForumsPage.jsx │ │ ├── HelpCenterPage.jsx │ │ └── index.js │ ├── main │ │ ├── CampusesPage.jsx │ │ ├── HomePage.jsx │ │ ├── MissionVisionPage.jsx │ │ ├── NotFoundPage.jsx │ │ ├── OurStoryPage.jsx │ │ ├── WhoWeArePage.jsx │ │ └── index.js │ ├── ownership │ │ ├── PrivacyPolicyPage.jsx │ │ ├── TermsAndConditionsPage.jsx │ │ └── index.js │ ├── play │ │ ├── ArchivesPage.jsx │ │ ├── ChallengeDetailsPage.jsx │ │ ├── ChallengesPage.jsx │ │ ├── LeaderboardsPage.jsx │ │ └── index.js │ ├── profile │ │ ├── CertifyPage.jsx │ │ ├── UserProfilePage.jsx │ │ ├── index.js │ │ └── readme.txt │ └── user-profile │ │ ├── UserCertifyPage.jsx │ │ ├── UserDashboardPage.jsx │ │ ├── UserHintUsagePage.jsx │ │ ├── UserSettingsPage.jsx │ │ ├── UserSolveOverviewPage.jsx │ │ ├── UserSummaryPage.jsx │ │ └── index.js └── styles │ ├── index.jsx │ └── shape-rendering.jsx ├── vercel.json ├── vite.config.js └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .vscode 3 | .dockerignore 4 | .gitignore 5 | .env 6 | config 7 | build 8 | node_modules 9 | docker-compose.yaml 10 | Dockerfile 11 | README.md -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | VITE_TURNSTILE_SITE_KEY=SampleTurnstileKey 2 | VITE_API_TARGET=http://localhost:37100/ -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | "eslint:recommended", 6 | "plugin:react/recommended", 7 | "plugin:react/jsx-runtime", 8 | "plugin:react-hooks/recommended", 9 | ], 10 | ignorePatterns: ["dist", ".eslintrc.cjs"], 11 | parserOptions: { ecmaVersion: "latest", sourceType: "module" }, 12 | settings: { react: { version: "18.2" } }, 13 | plugins: ["react-refresh"], 14 | rules: { 15 | "react-refresh/only-export-components": [ 16 | "warn", 17 | { allowConstantExport: true }, 18 | ], 19 | "react/prop-types": "off", 20 | "no-console": "warn", 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | .env 15 | 16 | # Editor directories and files 17 | .vscode/* 18 | !.vscode/extensions.json 19 | .idea 20 | .DS_Store 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw? 26 | yarn.lock 27 | yarn.lock 28 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | David King Roderos 2 | Sharon Grace Hangaan -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-alpine 2 | 3 | ARG VITE_TURNSTILE_SITE_KEY 4 | ENV VITE_TURNSTILE_SITE_KEY=$VITE_TURNSTILE_SITE_KEY 5 | 6 | WORKDIR /app 7 | 8 | COPY package.json . 9 | 10 | RUN npm install 11 | 12 | RUN npm i -g serve 13 | 14 | COPY . . 15 | 16 | RUN npm run build 17 | 18 | EXPOSE 3000 19 | 20 | CMD [ "serve", "-s", "dist" ] 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PWNEU CTF Platform 2 | 3 | This is the frontend source code of the PWNEU CTF Platform. Some of the assets were removed. 4 | 5 | ### License 6 | 7 | This project is licensed under the [Apache 2.0 License](LICENSE). -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | New Era University 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["es6", "dom"], // Add this line 4 | "paths": { 5 | "@/*": ["./src/*"] 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ctf-platform", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@emotion/react": "^11.11.1", 14 | "@emotion/styled": "^11.11.0", 15 | "@fortawesome/fontawesome-free": "^6.6.0", 16 | "@fortawesome/fontawesome-svg-core": "^6.4.2", 17 | "@fortawesome/free-regular-svg-icons": "^6.4.2", 18 | "@fortawesome/free-solid-svg-icons": "^6.4.2", 19 | "@fortawesome/react-fontawesome": "^0.2.0", 20 | "@fullcalendar/core": "^6.1.9", 21 | "@fullcalendar/daygrid": "^6.1.9", 22 | "@fullcalendar/react": "^6.1.9", 23 | "@marsidev/react-turnstile": "^1.0.2", 24 | "@microsoft/signalr": "^8.0.7", 25 | "@mui/material": "^5.14.15", 26 | "@popperjs/core": "2.11.8", 27 | "@vitejs/plugin-react": "^4.3.1", 28 | "animate.css": "^4.1.1", 29 | "aos": "^2.3.4", 30 | "axios": "^1.7.7", 31 | "bootstrap": "^5.3.3", 32 | "chart.js": "^4.4.4", 33 | "dotenv": "^16.4.5", 34 | "echarts-for-react": "^3.0.2", 35 | "gsap": "^3.12.2", 36 | "jspdf": "^2.5.2", 37 | "prop-types": "^15.8.1", 38 | "react": "^18.3.1", 39 | "react-bootstrap": "^2.10.5", 40 | "react-calendar": "^4.6.1", 41 | "react-chartjs-2": "^5.2.0", 42 | "react-dom": "^18.3.1", 43 | "react-helmet-async": "^1.3.0", 44 | "react-icons": "^5.3.0", 45 | "react-modal-video": "^2.0.1", 46 | "react-particles": "^2.12.2", 47 | "react-router-dom": "^6.17.0", 48 | "react-scrollbars-custom": "^4.1.1", 49 | "react-toastify": "^10.0.5", 50 | "recharts": "^2.9.0", 51 | "sass": "^1.69.5", 52 | "svg": "^0.1.0", 53 | "swiper": "^8.0.0", 54 | "tsparticles": "^2.12.0" 55 | }, 56 | "devDependencies": { 57 | "@types/react": "^18.2.15", 58 | "@types/react-dom": "^18.2.7", 59 | "@vitejs/plugin-react-swc": "^3.3.2", 60 | "eslint": "^8.45.0", 61 | "eslint-plugin-react": "^7.32.2", 62 | "eslint-plugin-react-hooks": "^4.6.0", 63 | "eslint-plugin-react-refresh": "^0.4.3", 64 | "vite": "^4.5.5" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /public/assets/css/CreateChallenge.css: -------------------------------------------------------------------------------- 1 | .tags-container, 2 | .flags-container { 3 | margin-top: 10px; 4 | display: flex; 5 | flex-wrap: wrap; 6 | } 7 | 8 | .tag-item, 9 | .flag-item { 10 | background-color: #e2e6ea; 11 | border-radius: 15px; 12 | padding: 5px 10px; 13 | margin-right: 10px; 14 | margin-bottom: 10px; 15 | display: flex; 16 | align-items: center; 17 | } 18 | 19 | .tag-item Button, 20 | .flag-item Button { 21 | margin-left: 5px; 22 | } 23 | -------------------------------------------------------------------------------- /public/assets/css/chatui.css: -------------------------------------------------------------------------------- 1 | .chatbot { 2 | display: flex; 3 | justify-content: center; 4 | align-items: center; 5 | height: 75vh; 6 | } 7 | 8 | .chat-container { 9 | width: 90%; 10 | max-width: 1300px; 11 | height: 60vh; 12 | display: flex; 13 | flex-direction: column; 14 | background-color: rgba(82, 79, 79, 0.336); 15 | overflow: hidden; 16 | } 17 | 18 | .message-list { 19 | flex-grow: 1; 20 | padding: 40px; 21 | overflow-y: auto; 22 | } 23 | 24 | .message { 25 | margin-bottom: 50px; 26 | } 27 | 28 | .message.user { 29 | text-align: right; 30 | } 31 | 32 | .message.bot { 33 | text-align: left; 34 | } 35 | 36 | .message p { 37 | padding: 10px; 38 | border-radius: 25px; 39 | background-color: #020703; 40 | color: rgb(255, 255, 255); 41 | } 42 | 43 | .message.bot p { 44 | background-color: #00000000; 45 | } 46 | 47 | .message-input { 48 | padding: 30px; 49 | background-color: rgba(0, 0, 0, 0.3); 50 | display: flex; 51 | justify-content: space-between; 52 | } 53 | 54 | .message-input input { 55 | width: 100%; 56 | padding: 10px; 57 | background-color: rgba(0, 0, 0, 0.5); 58 | color: white; 59 | border: none; 60 | border-radius: 10px; 61 | } 62 | 63 | .message-input button { 64 | background-color: #213502b7; 65 | color: white; 66 | border: none; 67 | 68 | border-radius: 3px; 69 | cursor: pointer; 70 | } 71 | 72 | .message-input button:hover { 73 | background-color: #ececec; 74 | color: rgb(0, 0, 0); 75 | } 76 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /api/ 3 | Disallow: /hubs/ 4 | Disallow: /assets/ 5 | Allow: / 6 | 7 | # PWNEU{w38_cR@w13rs} -------------------------------------------------------------------------------- /src/api.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | // Axios instance for the entire application 4 | const BASE_URL = "/api"; 5 | 6 | export const api = axios.create({ 7 | baseURL: BASE_URL, 8 | withCredentials: true, 9 | }); 10 | -------------------------------------------------------------------------------- /src/components/HelpCenter.jsx: -------------------------------------------------------------------------------- 1 | 2 | export default function HelpCenter() { 3 | return ( 4 | <> 5 | 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /src/components/LinkifyText.jsx: -------------------------------------------------------------------------------- 1 | export default function LinkifyText({ text }) { 2 | const urlRegex = /(https?:\/\/[^\s]+)/g; 3 | const boldRegex = /\*\*(.*?)\*\*/g; 4 | 5 | // Split text by URLs first 6 | const parts = text.split(urlRegex); 7 | 8 | return ( 9 | <> 10 | {parts.map((part, index) => { 11 | if (urlRegex.test(part)) { 12 | // If it's a URL, return an anchor tag 13 | return ( 14 | 21 | {part} 22 | 23 | ); 24 | } else { 25 | // Handle bold formatting inside non-URL text 26 | const boldParts = part.split(boldRegex); 27 | return boldParts.map((subPart, subIndex) => 28 | subIndex % 2 === 1 ? ( 29 | {subPart} 30 | ) : ( 31 | subPart 32 | ) 33 | ); 34 | } 35 | })} 36 | 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /src/components/LoadingIcon.jsx: -------------------------------------------------------------------------------- 1 | export default function LoadingIcon() { 2 | return ( 3 |
11 | Loading... 12 |
13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /src/components/MetaComponent.jsx: -------------------------------------------------------------------------------- 1 | import { Helmet, HelmetProvider } from "react-helmet-async"; 2 | import PropTypes from 'prop-types'; 3 | 4 | export default function MetaComponent({ meta }) { 5 | return ( 6 | 7 | 8 | {meta?.title} 9 | 10 | 11 | 12 | ); 13 | } 14 | 15 | // Define prop types 16 | MetaComponent.propTypes = { 17 | meta: PropTypes.shape({ 18 | title: PropTypes.string, 19 | description: PropTypes.string, 20 | }).isRequired, 21 | }; 22 | -------------------------------------------------------------------------------- /src/components/ModalVideo.jsx: -------------------------------------------------------------------------------- 1 | import ModalVideo from "react-modal-video"; 2 | import PropTypes from 'prop-types'; 3 | 4 | export default function ModalVideoComponent({ isOpen, setIsOpen, videoId }) { 5 | return ( 6 | setIsOpen(false)} 12 | /> 13 | ); 14 | } 15 | 16 | // Define prop types 17 | ModalVideoComponent.propTypes = { 18 | isOpen: PropTypes.bool.isRequired, 19 | setIsOpen: PropTypes.func.isRequired, 20 | videoId: PropTypes.string.isRequired, 21 | }; 22 | -------------------------------------------------------------------------------- /src/components/Preloader.jsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | 3 | export default function Preloader() { 4 | const [preloaderDisable, setPreloaderDisable] = useState(false); 5 | useEffect(() => { 6 | setPreloaderDisable(true); 7 | }, []); 8 | 9 | return ( 10 |
11 |
15 |
16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /src/components/RequireAuth.jsx: -------------------------------------------------------------------------------- 1 | import { Navigate, Outlet, useLocation } from "react-router-dom"; 2 | import useAuth from "@/hooks/useAuth"; 3 | import LoadingIcon from "./LoadingIcon"; 4 | import { toast } from "react-toastify"; 5 | 6 | const RequireAuth = ({ allowedRoles }) => { 7 | const { auth } = useAuth(); 8 | const location = useLocation(); 9 | 10 | if (auth === undefined) { 11 | return ; 12 | } 13 | 14 | if (auth?.roles?.find((role) => allowedRoles?.includes(role))) { 15 | return ; 16 | } 17 | 18 | if (auth?.userName) { 19 | return ; 20 | } 21 | 22 | toast.warn("Please log in first."); 23 | return ; 24 | }; 25 | 26 | export default RequireAuth; 27 | -------------------------------------------------------------------------------- /src/components/RequireDefinedAuth.jsx: -------------------------------------------------------------------------------- 1 | import { Outlet } from "react-router-dom"; 2 | import useAuth from "@/hooks/useAuth"; 3 | import LoadingIcon from "./LoadingIcon"; 4 | 5 | const RequireDefinedAuth = () => { 6 | const { auth } = useAuth(); 7 | 8 | if (auth === undefined) { 9 | return ; 10 | } 11 | 12 | return ; 13 | }; 14 | 15 | export default RequireDefinedAuth; 16 | -------------------------------------------------------------------------------- /src/components/RequireNoAuth.jsx: -------------------------------------------------------------------------------- 1 | import { Navigate, Outlet, useLocation } from "react-router-dom"; 2 | import useAuth from "@/hooks/useAuth"; 3 | import LoadingIcon from "./LoadingIcon"; 4 | import { toast } from "react-toastify"; 5 | 6 | const RequireNoAuth = () => { 7 | const { auth } = useAuth(); 8 | const location = useLocation(); 9 | 10 | if (auth === undefined) { 11 | return ; 12 | } 13 | 14 | if (auth?.userName) { 15 | toast.warn("You must be logged out before accessing this page."); 16 | return ; 17 | } 18 | 19 | return ; 20 | }; 21 | 22 | export default RequireNoAuth; 23 | -------------------------------------------------------------------------------- /src/components/ScrollToTop.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useLocation } from "react-router-dom"; 3 | 4 | export default function ScrollToTop() { 5 | const { pathname } = useLocation(); 6 | 7 | useEffect(() => { 8 | window.scrollTo(0, 0); 9 | window.history.scrollRestoration = "manual"; 10 | }, [pathname]); 11 | 12 | return null; 13 | } 14 | -------------------------------------------------------------------------------- /src/components/Socials.jsx: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { socialMediaLinks } from "@/data/socialLinks"; 3 | 4 | export default function Socials({ componentsClass, textSize }) { 5 | return ( 6 | <> 7 | {socialMediaLinks.map((link, index) => ( 8 | 15 | 16 | 17 | ))} 18 | 19 | ); 20 | } 21 | 22 | // Define prop types 23 | Socials.propTypes = { 24 | componentsClass: PropTypes.string, 25 | textSize: PropTypes.string, 26 | }; 27 | -------------------------------------------------------------------------------- /src/components/Star.jsx: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { useState, useEffect } from "react"; 3 | 4 | export default function Star({ star, textSize, textColor }) { 5 | const [rating, setRating] = useState([]); 6 | 7 | useEffect(() => { 8 | // Reset rating each time star changes 9 | setRating([]); 10 | for (let i = Math.round(star); i >= 1; i--) { 11 | setRating((prev) => [...prev, "star"]); 12 | } 13 | }, [star]); // Include star in the dependency array 14 | 15 | return ( 16 | <> 17 | {rating.map((itm, i) => ( 18 |
24 | ))} 25 | 26 | ); 27 | } 28 | 29 | // Define prop types 30 | Star.propTypes = { 31 | star: PropTypes.number.isRequired, // star should be a number and is required 32 | textSize: PropTypes.string, // textSize is an optional string 33 | textColor: PropTypes.string, // textColor is an optional string 34 | }; 35 | -------------------------------------------------------------------------------- /src/context/AuthProvider.jsx: -------------------------------------------------------------------------------- 1 | import { createContext, useEffect, useLayoutEffect, useState } from "react"; 2 | import { api } from "../api"; 3 | 4 | const REFRESH_URL = "/identity/refresh"; 5 | 6 | // Auth Provider for the entire application 7 | // Reference: https://www.youtube.com/watch?v=AcYF18oGn6Y 8 | 9 | const AuthContext = createContext(undefined); 10 | 11 | // eslint-disable-next-line react/prop-types 12 | export const AuthProvider = ({ children }) => { 13 | const [auth, setAuth] = useState(); 14 | 15 | useEffect(() => { 16 | const refresh = async () => { 17 | try { 18 | const response = await api.get(REFRESH_URL); 19 | 20 | setAuth(response.data); 21 | // eslint-disable-next-line no-unused-vars 22 | } catch (error) { 23 | setAuth(null); 24 | } 25 | }; 26 | 27 | refresh(); 28 | }, []); 29 | 30 | useLayoutEffect(() => { 31 | const authInterceptor = api.interceptors.request.use((config) => { 32 | config.headers.Authorization = 33 | !config._retry && auth?.accessToken 34 | ? `Bearer ${auth?.accessToken}` 35 | : config.headers.Authorization; 36 | 37 | return config; 38 | }); 39 | 40 | return () => { 41 | api.interceptors.request.eject(authInterceptor); 42 | }; 43 | }, [auth]); 44 | 45 | useLayoutEffect(() => { 46 | const refreshInterceptor = api.interceptors.response.use( 47 | (response) => response, 48 | async (error) => { 49 | const originalRequest = error.config; 50 | 51 | if (error.response.status === 401 && !originalRequest._retry) { 52 | try { 53 | const response = await api.get(REFRESH_URL); 54 | 55 | setAuth(response.data); 56 | 57 | originalRequest.headers.Authorization = `Bearer ${response.data.accessToken}`; 58 | originalRequest._retry = true; 59 | 60 | return api(originalRequest); 61 | } catch { 62 | setAuth(null); 63 | } 64 | } 65 | 66 | return Promise.reject(error); 67 | } 68 | ); 69 | 70 | return () => { 71 | api.interceptors.response.eject(refreshInterceptor); 72 | }; 73 | }, [auth]); 74 | 75 | return ( 76 | 77 | {children} 78 | 79 | ); 80 | }; 81 | 82 | export default AuthContext; 83 | -------------------------------------------------------------------------------- /src/context/SignalRProvider.jsx: -------------------------------------------------------------------------------- 1 | import { createContext } from "react"; 2 | import { useState, useEffect } from "react"; 3 | import * as signalR from "@microsoft/signalr"; 4 | import { toast } from "react-toastify"; 5 | import useAuth from "@/hooks/useAuth"; 6 | 7 | const SignalRContext = createContext(); 8 | 9 | export const SignalRProvider = ({ children }) => { 10 | const { auth } = useAuth(); 11 | const [connection, setConnection] = useState(null); 12 | 13 | useEffect(() => { 14 | if (!auth?.accessToken) return; 15 | 16 | const newConnection = new signalR.HubConnectionBuilder() 17 | .withUrl("/api/announcements", { 18 | accessTokenFactory: () => auth.accessToken, 19 | withCredentials: true, 20 | }) 21 | .configureLogging(signalR.LogLevel.None) 22 | .withAutomaticReconnect() 23 | .build(); 24 | 25 | newConnection 26 | .start() 27 | .then(() => setConnection(newConnection)) 28 | .catch(() => {}); 29 | 30 | const handleAnnouncement = (message) => { 31 | toast.info(message, { autoClose: 30_000 }); 32 | }; 33 | 34 | newConnection.on("ReceiveAnnouncement", handleAnnouncement); 35 | 36 | return () => { 37 | newConnection.off("ReceiveAnnouncement", handleAnnouncement); 38 | newConnection.stop(); 39 | }; 40 | }, [auth?.accessToken]); 41 | 42 | return ( 43 | 44 | {children} 45 | 46 | ); 47 | }; 48 | 49 | export default SignalRContext; 50 | -------------------------------------------------------------------------------- /src/data/campuses.js: -------------------------------------------------------------------------------- 1 | export const campuses = [ 2 | { 3 | id: 1, 4 | imgSrc: "/assets/img/campuses/MainBranch_Card.svg", 5 | title: "Main Campus", 6 | desc: "The New Era Educational Institute (NEEI) was formally opened on June 17, 1975 by virtue of GPR No. 232, Series of 1975 issued by the Ministry of Education, Culture and Sports.", 7 | }, 8 | { 9 | id: 2, 10 | imgSrc: "/assets/img/campuses/BatangasBranch_Card.svg", 11 | title: "Batangas", 12 | desc: "As part of the celebration of New Era University's 25th anniversary, the New Era University – Lipa City Branch was established on May 5, 2000 in Batangas.", 13 | }, 14 | { 15 | id: 3, 16 | imgSrc: "/assets/img/campuses/PampangaBranch_Card.svg", 17 | title: "Pampanga", 18 | desc: "New Era University's first branch outside Metro Manila is situated in a sprawling six-hectare campus in the City of San Fernando, Pampanga.", 19 | }, 20 | { 21 | id: 4, 22 | imgSrc: "/assets/img/campuses/GeneralSantosBranch_Card.svg", 23 | title: "General Santos", 24 | desc: "New Era University's first branch outside Metro Manila is situated in a sprawling six-hectare campus in the City of San Fernando, Pampanga.", 25 | }, 26 | { 27 | id: 5, 28 | imgSrc: "/assets/img/campuses/RizalBranch_Card.svg", 29 | title: "Rizal", 30 | desc: "The New Era University – Rizal Branch is the fifth and newest branch of the University. ", 31 | }, 32 | ]; 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/data/contactLinks.js: -------------------------------------------------------------------------------- 1 | export const contactData = [ 2 | { 3 | id: 1, 4 | icon: "/assets/img/contact/1.svg", 5 | address: "9 Central Ave, Quezon City, 1107 Metro Manila", 6 | }, 7 | { 8 | id: 2, 9 | icon: "/assets/img/contact/2.svg", 10 | phoneNumber: "+ (02) 8981 4221", 11 | }, 12 | { 13 | id: 3, 14 | icon: "/assets/img/contact/neu-contact.png", 15 | email: "http://www.neu.edu.ph/", 16 | }, 17 | { 18 | id: 4, 19 | icon: "/assets/img/contact/neu-acss.png", 20 | orgACSS: "Association of Computer Student (ACSS)", 21 | }, 22 | { 23 | id: 5, 24 | icon: "/assets/img/contact/neu-pwneucybereduct.png", 25 | orgACSS: "PWNEU CyberEducation ", 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /src/data/courses.js: -------------------------------------------------------------------------------- 1 | export const coursesData = [ 2 | { 3 | id: 1, 4 | imageSrc: "/assets/img/challengesCards/bgsample.png", 5 | authorImageSrc: "/assets/img/challengesCard/bgsample.png", 6 | title: "Wild Game of Life", 7 | rating: 4.3, 8 | ratingCount: 1991, 9 | lessonCount: 6, 10 | duration: 1320, 11 | level: "Beginner", 12 | originalPrice: 199, 13 | discountedPrice: 79, 14 | paid: true, 15 | category: "Design", 16 | state: "Popular", 17 | languange: "French", 18 | authorName: "Jane Cooper", 19 | viewStatus: "Good", 20 | difficulty: "Easy", 21 | desc: "Introductory course on web hosting, domain registration, and how you can easily publish and edit your website online.", 22 | }, 23 | ]; 24 | 25 | -------------------------------------------------------------------------------- /src/data/features.js: -------------------------------------------------------------------------------- 1 | export const feature = [ 2 | { 3 | id: 1, 4 | title: "Dive into expertly crafted courses that foster deep understanding.", 5 | }, 6 | { 7 | id: 2, 8 | title: "Experience a clear, easy-to-follow curriculum tailored to your academic journey.", 9 | }, 10 | { 11 | id: 3, 12 | title: "Join a supportive community and start learning today.", 13 | }, 14 | 15 | 16 | ]; 17 | 18 | 19 | 20 | export const teachingFeatures = [ 21 | { 22 | id: 1, 23 | title: "Last Education of Bachelor Degree", 24 | }, 25 | { 26 | id: 2, 27 | title: "More Than 15 Years Experience", 28 | }, 29 | { 30 | id: 3, 31 | title: "12 Education Award Winning", 32 | }, 33 | ]; 34 | 35 | export const statictis = [ 36 | { 37 | id: 1, 38 | rating: "9/10", 39 | description: "9/10 users reported better learning outcomes.", 40 | }, 41 | { 42 | id: 2, 43 | rating: "85%", 44 | description: "85% of students see their course through to completion", 45 | }, 46 | ]; 47 | 48 | export const featuresEight = [ 49 | { 50 | id: 1, 51 | icon: "/assets/img/home-8/what/icons/1.svg", 52 | title: "Industry expert teachers", 53 | text: "Lorem ipsum dolor sit amet, consectetur dolorili adipiscing elit. Felis donec massa aliquam id dolor .", 54 | delay: 2, 55 | }, 56 | { 57 | id: 2, 58 | icon: "/assets/img/home-8/what/icons/2.svg", 59 | title: "Up-to-date course content", 60 | text: "Lorem ipsum dolor sit amet, consectetur dolorili adipiscing elit. Felis donec massa aliquam id dolor .", 61 | delay: 3, 62 | }, 63 | { 64 | id: 3, 65 | icon: "/assets/img/home-8/what/icons/3.svg", 66 | title: "Students community", 67 | text: "Lorem ipsum dolor sit amet, consectetur dolorili adipiscing elit. Felis donec massa aliquam id dolor .", 68 | delay: 4, 69 | }, 70 | ]; 71 | 72 | export const statictisEight = [ 73 | { 74 | id: 1, 75 | score: "9/10", 76 | text: "Overall courses satisfaction score", 77 | className: "-el-1", 78 | }, 79 | { 80 | id: 2, 81 | score: "10K+", 82 | text: "Happy Students Worldwide", 83 | className: "-el-2", 84 | }, 85 | { 86 | id: 3, 87 | score: "96%", 88 | text: "Completition Rate On All Courses", 89 | className: "-el-3", 90 | }, 91 | ]; 92 | -------------------------------------------------------------------------------- /src/data/footerLinks.js: -------------------------------------------------------------------------------- 1 | export const footerLinks = [ 2 | { 3 | title: "ABOUT US", 4 | links: [ 5 | { href: "/our-story", label: "About Us" }, 6 | { href: "/chat", label: "Dash AI" }, 7 | { href: "/mission-vision", label: "Mission and Vision" }, 8 | { href: "/contact", label: "Contact Us" }, 9 | ], 10 | }, 11 | { 12 | title: "Resources", 13 | links: [ 14 | // { href: "/achievements-list", label: "Articles" }, 15 | { href: "https://pauljerimy.com/security-certification-roadmap/", label: "Road Map" }, 16 | { href: "https://github.com/pwneu/CTF-Recommended-Tools.git", label: "Recommended Tools" }, 17 | { href: "https://ctftime.org/writeups", label: "Resource Libraries" }, 18 | ], 19 | }, 20 | { 21 | title: "Community", 22 | links: [ 23 | { href: "/discussion-forum", label: "Discord" }, 24 | { href: "/campuses", label: "Campuses" }, 25 | ], 26 | }, 27 | 28 | { 29 | title: "SUPPORT", 30 | links: [ 31 | { href: "/contact", label: "Help Center" }, 32 | { href: "/help-center", label: "FAQs" }, 33 | ], 34 | }, 35 | ]; 36 | -------------------------------------------------------------------------------- /src/data/herostory.js: -------------------------------------------------------------------------------- 1 | export const slidesData = [ 2 | { 3 | id: 1, 4 | bgImage: "/assets/img/about/herostory/mainslider/mainslider-1.png", 5 | }, 6 | { 7 | id: 2, 8 | bgImage: "/assets/img/about/herostory/mainslider/mainslider-2.png", 9 | }, 10 | { 11 | id: 3, 12 | bgImage: "/assets/img/about/herostory/mainslider/mainslider-3.png", 13 | }, 14 | { 15 | id: 4, 16 | bgImage: "/assets/img/about/herostory/mainslider/mainslider-4.png", 17 | }, 18 | { 19 | id: 5, 20 | bgImage: "/assets/img/about/herostory/mainslider/mainslider-5.png", 21 | }, 22 | ]; 23 | 24 | export const feature = [ 25 | { 26 | id: 1, 27 | imgSrc: "/assets/img/about/herostory/icons/NEUheroStory_ICON.svg", 28 | title: "New Era University", 29 | description: "A leader in innovative cybersecurity education", 30 | }, 31 | { 32 | id: 2, 33 | imgSrc: "/assets/img/about/herostory/icons/PWNEU2heroStory_ICON.svg", 34 | title: "PWNEU CyberEducation", 35 | description: "Focused on advancing cybersecurity skills and knowledge through comprehensive education and practical training.", 36 | }, 37 | { 38 | id: 3, 39 | imgSrc: "/assets/img/about/herostory/icons/ACSSheroStory_ICON.svg", 40 | title: "ACCS", 41 | description: "Association of Computer Science Students", 42 | }, 43 | ]; -------------------------------------------------------------------------------- /src/data/links.js: -------------------------------------------------------------------------------- 1 | export const links = [ 2 | { id: 1, href: "/help-center", label: "Help" }, 3 | { id: 2, href: "/privacy-policy", label: "Privacy Policy" }, 4 | { id: 3, href: "", label: "Cookie Notice" }, 5 | { id: 4, href: "", label: "Security" }, 6 | { id: 5, href: "/terms-and-conditions", label: "Terms and Conditions" }, 7 | ]; 8 | 9 | // TODO -- fix broken links -------------------------------------------------------------------------------- /src/data/menu.js: -------------------------------------------------------------------------------- 1 | export const menuList = [ 2 | { 3 | title: "Learn", 4 | links: [ 5 | { 6 | href: "#", 7 | }, 8 | ], 9 | }, 10 | { 11 | title: "Compete", 12 | links: [ 13 | { 14 | href: "/play", 15 | }, 16 | ], 17 | }, 18 | { 19 | title: "University", 20 | links: [ 21 | { 22 | href: "/campuses", 23 | }, 24 | ], 25 | }, 26 | { 27 | title: "Community", 28 | links: [ 29 | { 30 | href: "/discussion-forum", 31 | }, 32 | ], 33 | }, 34 | { 35 | title: "About Us", 36 | links: [ 37 | { href: "/our-story", label: "Our Story" }, 38 | { href: "/who-we-are", label: "Who We Are" }, 39 | { href: "/mission-vision", label: "Mission and Vision" }, 40 | ], 41 | }, 42 | ]; 43 | export const menumobileList = [ 44 | // { 45 | // title: "Learn", 46 | // path: "https://pwneu.github.io/learn/modules/introduction", 47 | // icon: "fa-book", 48 | // }, 49 | { 50 | title: "Archives", 51 | path: "/archives", 52 | icon: "fa-box-archive", 53 | }, 54 | { 55 | title: "Play", 56 | path: "/play", 57 | icon: "fa-gamepad", 58 | }, 59 | { 60 | title: "Leaderboards", 61 | path: "/leaderboards", 62 | icon: "fa-trophy", 63 | }, 64 | { 65 | title: "Dash AI", 66 | path: "/chat", 67 | icon: "fa-robot", 68 | }, 69 | { 70 | title: "Overview", 71 | path: "/our-story", 72 | icon: "fa-eye", 73 | }, 74 | { 75 | title: "Who We Are", 76 | path: "/who-we-are", 77 | icon: "fa-users", 78 | }, 79 | { 80 | title: "Mission and Vision", 81 | path: "/mission-vision", 82 | icon: "fa-bullseye", 83 | }, 84 | { 85 | title: "University", 86 | path: "/campuses", 87 | icon: "fa-university", 88 | }, 89 | { 90 | title: "Privacy Policy", 91 | path: "/privacy-policy", 92 | icon: "fa-shield-alt", 93 | }, 94 | { 95 | title: "Terms and Conditions", 96 | path: "/terms-and-conditions", 97 | icon: "fa-file-alt", 98 | }, 99 | { 100 | title: "Community", 101 | path: "/discussion-forum", 102 | icon: "fa-users", 103 | }, 104 | { 105 | title: "Help Center", 106 | path: "https://www.neu.edu.ph/", 107 | icon: "fa-question-circle", 108 | }, 109 | { 110 | title: "FAQs", 111 | path: "/help-center", 112 | icon: "fa-info-circle", 113 | }, 114 | { 115 | title: "Support", 116 | path: "/contact", 117 | icon: "fa-life-ring", 118 | }, 119 | ]; 120 | -------------------------------------------------------------------------------- /src/data/organization.js: -------------------------------------------------------------------------------- 1 | export const organization = [ 2 | "/assets/img/organization/GuideM__Org.svg", 3 | "/assets/img/organization/Secuna_Org.svg", 4 | "/assets/img/organization/NEU__Org.svg", 5 | "/assets/img/organization/NEUCICS_Org.svg", 6 | 7 | ]; 8 | -------------------------------------------------------------------------------- /src/data/socialLinks.js: -------------------------------------------------------------------------------- 1 | export const socialMediaLinks = [ 2 | { id: 1, href: "#", iconClassName: "icon-facebook" }, 3 | { id: 2, href: "#", iconClassName: "icon-twitter" }, 4 | { id: 3, href: "#", iconClassName: "icon-instagram" }, 5 | { id: 4, href: "#", iconClassName: "icon-linkedin" }, 6 | ]; 7 | 8 | // iconClassName are from font awesome 9 | -------------------------------------------------------------------------------- /src/data/teams.js: -------------------------------------------------------------------------------- 1 | export const developers = [ 2 | { 3 | id: 1, 4 | title: "David King Roderos", 5 | profession: "Software Engineer", 6 | url: "https://github.com/dkroderos", 7 | }, 8 | { 9 | id: 2, 10 | title: "Sharon Grace Hangaan", 11 | profession: "Full-Stack Developer", 12 | url: "https://github.com/sghangaan", 13 | }, 14 | ]; 15 | -------------------------------------------------------------------------------- /src/data/testimonials.js: -------------------------------------------------------------------------------- 1 | export const testimonials = [ 2 | { 3 | id: 1, 4 | name: "David King S. Roderos & Sharon Grace T. Hangaan", 5 | position: "BS in Computer Science, PWNEU | Full-Stack Developer", 6 | comment: `At PWNEU, we believe that learning happens through challenges. Together, we rise, compete, and conquer the ever-evolving world of cybersecurity.`, 7 | description: 8 | "PWNEU is more than just a team—it's a community where cybersecurity enthusiasts come together to learn, compete, and grow. Through CTF challenges, we push our limits, develop real-world problem-solving skills, and inspire the next generation of ethical hackers. Cybersecurity is a battlefield, and we are here to challenge, learn, and defend together.", 9 | }, 10 | { 11 | id: 2, 12 | name: "David King S. Roderos & Sharon Grace T. Hangaan", 13 | position: "BS in Computer Science, PWNEU | Full-Stack Developer", 14 | comment: `To win is to learn, and to learn is to evolve. At PWNEU, we challenge ourselves to be better every day.`, 15 | description: 16 | "CTF is more than just solving puzzles—it's about pushing boundaries, embracing failure, and learning to think like an attacker. At PWNEU, we thrive on the thrill of competition and the pursuit of knowledge. Whether you're a beginner or a seasoned player, there's always something new to learn.", 17 | }, 18 | { 19 | id: 3, 20 | name: "David King S. Roderos & Sharon Grace T. Hangaan", 21 | position: "BS in Computer Science, PWNEU | Full-Stack Developer", 22 | comment: `CTF isn’t just a game—it’s a mindset. At PWNEU, we build, break, and defend together.`, 23 | description: "At PWNEU, we believe cybersecurity is best learned through collaboration, hands-on experience, and real-world challenges. Our team is a community of ethical hackers, problem solvers, and innovators who strive to make an impact in the field. Every CTF challenge is a chance to grow, and every competition is a test of our dedication.", 24 | 25 | }, 26 | ]; 27 | -------------------------------------------------------------------------------- /src/data/topCategories.js: -------------------------------------------------------------------------------- 1 | export const topCatagoriesThree = [ 2 | { 3 | id: 1, 4 | imageSrc: "/assets/img/categories/WebExploitation_Card.gif", // Full URL to the image 5 | title: "Web Exploitation", 6 | }, 7 | { 8 | id: 2, 9 | imageSrc: "/assets/img/categories/Steganography_Card.jpg", 10 | title: "Steganography", 11 | 12 | }, 13 | { 14 | id: 3, 15 | imageSrc: "/assets/img/categories/Forensics_Card.jpg", 16 | title: "Forensics", 17 | }, 18 | { 19 | id: 4, 20 | imageSrc: "/assets/img/categories/Cryptography_Card.gif", 21 | title: "Cryptography", 22 | }, 23 | { 24 | id: 5, 25 | imageSrc: "/assets/img/categories/OSINT_Card.jpg", 26 | title: "OSINT (Open Source Intelligence)", 27 | }, 28 | { 29 | id: 6, 30 | imageSrc: "/assets/img/categories/BinaryExploitation_Card.jpg", 31 | title: "Binary Exploitation", 32 | }, 33 | { 34 | id: 7, 35 | imageSrc: "/assets/img/categories/ReverseEngineering_Card.jpg", 36 | title: "Reverse Engineering", 37 | }, 38 | ]; 39 | 40 | -------------------------------------------------------------------------------- /src/data/whyourcourse.js: -------------------------------------------------------------------------------- 1 | export const whyourcourse = [ 2 | { 3 | id: 1, 4 | icon: "icon-online-learning-4 text-64 text-green-1", 5 | title: "01. Level Up Your Skills", 6 | text: "Embark on a cybersecurity adventure with hands-on challenges and real-world scenarios. Our courses are like a Capture the Flag (CTF) game, designed to enhance your hacking and defense skills while keeping you engaged", 7 | }, 8 | { 9 | id: 2, 10 | icon: "icon-graduation-1 text-64 text-green-1", 11 | title: "02. Master Cybersecurity Techniques", 12 | text: "Conquer complex security puzzles and gain practical expertise. Our training is crafted to equip you with the skills and knowledge needed to excel in the dynamic field of cybersecurity.", 13 | }, 14 | { 15 | id: 3, 16 | icon: "icon-working-at-home-2 text-64 text-green-1", 17 | title: "03. Enter the Cyber Arena", 18 | text: "Prepare to tackle real-world security challenges. With our training, you’ll be ready to dive into the cybersecurity landscape and make a difference in protecting digital assets.", 19 | }, 20 | ]; 21 | 22 | -------------------------------------------------------------------------------- /src/features/about/components/MissionVision.jsx: -------------------------------------------------------------------------------- 1 | import { ourmissionvission } from "@/data/learningPaths"; 2 | 3 | export default function MissionVision() { 4 | return ( 5 |
6 |
7 |
8 | {ourmissionvission.map((elm, i) => ( 9 |
10 |
11 |
12 |
13 | {elm.title} 14 |
15 |

16 | {elm.description} 17 |

18 |
19 |
20 |
21 | ))} 22 |
23 |
24 |
31 |
32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/features/about/components/OurStory.jsx: -------------------------------------------------------------------------------- 1 | export default function OurStory() { 2 | return ( 3 | <> 4 |
5 |
6 |
7 |
8 |
9 |
10 |

Our Story

11 |
12 |
13 |

14 | Welcome to PWNEU Cybersecurity Education! 15 |

16 |

17 | Our journey began with a shared passion for cybersecurity 18 | and a desire to empower students at New Era University. We 19 | recognized the growing need for skilled cybersecurity 20 | professionals and understood that hands-on experience was 21 | key to mastering the complexities of this field. That's 22 | why we founded PWNEU—to create a platform where students can 23 | learn, grow, and challenge themselves through Capture The 24 | Flag (CTF) competitions and other interactive learning 25 | experiences. Our story is one of innovation, community, and 26 | a commitment to nurturing the next generation of 27 | cybersecurity experts. 28 |

29 |
30 |
31 |
32 |
33 |
34 |
35 | 36 |
37 |
38 |
39 |
40 |
41 |

Our Expertise

42 |

43 | Our team consists of seasoned cybersecurity professionals, 44 | educators, and enthusiasts who bring a wealth of knowledge and 45 | experience to the table. We specialize in creating challenging 46 | and relevant CTF scenarios that simulate real-world security 47 | threats. Our expertise extends beyond just technical skills; 48 | we focus on fostering critical thinking, problem-solving, and 49 | teamwork, which are essential attributes for any cybersecurity 50 | professional. With our guidance, students can expect to gain a 51 | deep understanding of cybersecurity principles and practices, 52 | preparing them for successful careers in this ever-evolving 53 | field. 54 |

55 |
56 |
57 |
58 |
59 |
60 | 61 | ); 62 | } 63 | -------------------------------------------------------------------------------- /src/features/about/index.js: -------------------------------------------------------------------------------- 1 | import DevFounder from "./components/DevFounder"; 2 | import HeroStory from "./components/HeroStory"; 3 | import OurApproach from "./components/OurApproach"; 4 | import OurStory from "./components/OurStory"; 5 | import MissionVision from "./components/MissionVision"; 6 | 7 | export { 8 | DevFounder, 9 | HeroStory, 10 | OurApproach, 11 | OurStory, 12 | MissionVision, 13 | }; 14 | -------------------------------------------------------------------------------- /src/features/achievements/index.js: -------------------------------------------------------------------------------- 1 | import AchievementsDetails from "./components/AchievementsDetails"; 2 | import AchievementsOne from "./components/AchievementsOne"; 3 | 4 | export { AchievementsDetails, AchievementsOne }; 5 | -------------------------------------------------------------------------------- /src/features/admin/audits/index.js: -------------------------------------------------------------------------------- 1 | import AuditsList from "./components/AuditsList"; 2 | import AuditsQueryInput from "./components/AuditsQueryInput"; 3 | 4 | export { AuditsList, AuditsQueryInput }; 5 | -------------------------------------------------------------------------------- /src/features/admin/blacklist/index.js: -------------------------------------------------------------------------------- 1 | import AddEmailToBlackList from "./components/AddEmailToBlackList"; 2 | import BlacklistedEmails from "./components/BlacklistedEmails"; 3 | 4 | export { AddEmailToBlackList, BlacklistedEmails }; 5 | -------------------------------------------------------------------------------- /src/features/admin/categories/components/CreateCategory.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { Modal, Button, Form } from "react-bootstrap"; 3 | import { toast } from "react-toastify"; 4 | import { api } from "@/api"; 5 | import { useNavigate } from "react-router-dom"; 6 | 7 | export default function CreateCategory({ show, onHide, onSuccess }) { 8 | const navigate = useNavigate(); 9 | const [isBusy, setIsBusy] = useState(false); 10 | const [name, setName] = useState(""); 11 | const [description, setDescription] = useState(""); 12 | 13 | const handleCreateCategory = async (e) => { 14 | e.preventDefault(); 15 | if (isBusy) return; 16 | 17 | setIsBusy(true); 18 | 19 | try { 20 | const response = await api.post("/play/categories", { 21 | name, 22 | description, 23 | }); 24 | toast.success(`Category created successfully: ${response.data}`); 25 | onSuccess({ id: response.data, name, description }); 26 | 27 | setName(""); 28 | setDescription(""); 29 | onHide(); 30 | } catch (error) { 31 | const status = error?.response?.status; 32 | 33 | if (status === 401) { 34 | navigate("/login"); 35 | } else if (status === 403) { 36 | toast.error("You are not allowed to create categories"); 37 | } else if (status === 400) { 38 | toast.error(error.response.data.message || "Error creating category"); 39 | } else { 40 | toast.error("Something went wrong. Please try again later"); 41 | } 42 | } finally { 43 | setIsBusy(false); 44 | } 45 | }; 46 | 47 | return ( 48 | 49 | 50 | Create Category 51 | 52 | 53 |
54 | 55 | Name 56 | setName(e.target.value)} 61 | required 62 | /> 63 | 64 | 65 | Description 66 | setDescription(e.target.value)} 71 | required 72 | /> 73 | 74 | 75 | 78 | 81 | 82 |
83 |
84 |
85 | ); 86 | } 87 | -------------------------------------------------------------------------------- /src/features/admin/categories/index.js: -------------------------------------------------------------------------------- 1 | import CreateCategory from "./components/CreateCategory"; 2 | import CategoriesList from "./components/CategoriesList"; 3 | 4 | export { CreateCategory, CategoriesList }; -------------------------------------------------------------------------------- /src/features/admin/challenges/components/ChallengesList.jsx: -------------------------------------------------------------------------------- 1 | import { Table } from "react-bootstrap"; 2 | 3 | export default function ChallengesList({ challenges, handleChallengeClick }) { 4 | return ( 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {challenges === undefined ? ( 17 | 18 | 21 | 22 | ) : challenges.length > 0 ? ( 23 | challenges.map((challenge) => ( 24 | handleChallengeClick(challenge.id)} 27 | style={{ cursor: "pointer" }} 28 | > 29 | 30 | 31 | 32 | 33 | 34 | 35 | )) 36 | ) : ( 37 | 38 | 41 | 42 | )} 43 | 44 |
NameDescriptionPointsDeadlineSolve Count
19 | Click the search button to view challenges. 20 |
{challenge.name}{challenge.description}{challenge.points}{challenge.deadline}{challenge.solveCount}
39 | No challenges found. 40 |
45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /src/features/admin/challenges/index.js: -------------------------------------------------------------------------------- 1 | import CreateChallenge from "./components/CreateChallenge"; 2 | import EditChallenge from "./components/EditChallenge"; 3 | import ChallengeHints from "./components/ChallengeHints"; 4 | import ChallengeSolves from "./components/ChallengeSolves"; 5 | import ChallengeArtifacts from "./components/ChallengeArtifacts"; 6 | import ChallengesQueryInput from "./components/ChallengesQueryInput"; 7 | import ChallengesList from "./components/ChallengesList"; 8 | import ChallengeHintUsages from "./components/ChallengeHintUsages"; 9 | 10 | export { 11 | CreateChallenge, 12 | EditChallenge, 13 | ChallengeHints, 14 | ChallengeSolves, 15 | ChallengeArtifacts, 16 | ChallengesQueryInput, 17 | ChallengesList, 18 | ChallengeHintUsages, 19 | }; 20 | -------------------------------------------------------------------------------- /src/features/admin/configurations/index.js: -------------------------------------------------------------------------------- 1 | import CertificationIssuer from "./components/CertificationIssuer"; 2 | import ChallengesLocked from "./components/ChallengesLocked"; 3 | import IsCertificationEnabled from "./components/IsCertificationEnabled"; 4 | import IsTurnstileEnabled from "./components/IsTurnstileEnabled"; 5 | import PublicLeaderboardCount from "./components/PublicLeaderboardCount"; 6 | import SubmissionsAllowed from "./components/SubmissionsAllowed"; 7 | 8 | export { 9 | CertificationIssuer, 10 | ChallengesLocked, 11 | IsCertificationEnabled, 12 | IsTurnstileEnabled, 13 | PublicLeaderboardCount, 14 | SubmissionsAllowed, 15 | }; 16 | -------------------------------------------------------------------------------- /src/features/admin/keys/index.js: -------------------------------------------------------------------------------- 1 | import CreateAccessKey from "./components/CreateAccessKey"; 2 | import AccessKeysList from "./components/AccessKeysList"; 3 | 4 | export { CreateAccessKey, AccessKeysList }; -------------------------------------------------------------------------------- /src/features/admin/submissions/components/UserRanksAdmin.jsx: -------------------------------------------------------------------------------- 1 | import { ListGroup, OverlayTrigger, Tooltip } from "react-bootstrap"; 2 | import { Link } from "react-router-dom"; 3 | 4 | export default function UserRanksAdmin({ userRanks, requesterRank, isManager }) { 5 | const renderUserRankItem = (rank, isRequester) => { 6 | const userLink = `/admin/user/${rank.id}`; 7 | 8 | const listItem = ( 9 | 14 |
15 | {rank.position}. 16 | {rank.userName} 17 |
18 | {rank.points} pts 19 |
20 | ); 21 | 22 | return ( 23 | 28 | Latest Solve Time:{" "} 29 | {new Date(rank.latestSolve).toLocaleString()} 30 | 31 | } 32 | > 33 | {isManager ? ( 34 | 35 | {listItem} 36 | 37 | ) : ( 38 | listItem 39 | )} 40 | 41 | ); 42 | }; 43 | 44 | // Check if requesterRank is in userRanks 45 | const isRequesterInUserRanks = 46 | requesterRank && userRanks.some((rank) => rank.id === requesterRank.id); 47 | 48 | return ( 49 | <> 50 | 51 | {userRanks.map((rank) => 52 | renderUserRankItem( 53 | rank, 54 | isRequesterInUserRanks && rank.id === requesterRank.id 55 | ) 56 | )} 57 | 58 | 59 | {requesterRank && !isRequesterInUserRanks && ( 60 | 61 | {renderUserRankItem(requesterRank, true)} 62 | 63 | )} 64 | 65 | ); 66 | } 67 | -------------------------------------------------------------------------------- /src/features/admin/submissions/index.js: -------------------------------------------------------------------------------- 1 | import UserRanksAdmin from "./components/UserRanksAdmin"; 2 | import LeaderboardGraph from "./components/LeaderboardGraph"; 3 | 4 | export { UserRanksAdmin, LeaderboardGraph }; 5 | -------------------------------------------------------------------------------- /src/features/admin/users/components/DownloadCertificateButton.jsx: -------------------------------------------------------------------------------- 1 | import { Button, Spinner } from "react-bootstrap"; 2 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 3 | import { faDownload } from "@fortawesome/free-solid-svg-icons"; 4 | import { useState } from "react"; 5 | import { api } from "@/api"; 6 | import { useNavigate } from "react-router-dom"; 7 | import { toast } from "react-toastify"; 8 | 9 | export default function DownloadCertificateButton({ userDetailsId }) { 10 | const navigate = useNavigate(); 11 | const [isDownloadingCertificate, setIsDownloadingCertificate] = 12 | useState(false); 13 | 14 | const downloadCertificate = async () => { 15 | if (isDownloadingCertificate) return; 16 | 17 | try { 18 | setIsDownloadingCertificate(true); 19 | 20 | const checkResponse = await api.get( 21 | `/identity/users/${userDetailsId}/certificate/check` 22 | ); 23 | 24 | if (checkResponse.data === "WithoutCertificate") { 25 | toast.error("The user doesn't have a certificate yet"); 26 | return; 27 | } 28 | 29 | if (checkResponse.data === "NotAllowed") { 30 | toast.error("Certification is disabled"); 31 | return; 32 | } 33 | 34 | const response = await api.get( 35 | `/identity/users/${userDetailsId}/certificate`, 36 | { 37 | responseType: "blob", 38 | } 39 | ); 40 | 41 | const url = window.URL.createObjectURL(new Blob([response.data])); 42 | const link = document.createElement("a"); 43 | link.href = url; 44 | link.setAttribute("download", "pwneu-certificate.pdf"); 45 | document.body.appendChild(link); 46 | link.click(); 47 | link.remove(); 48 | window.URL.revokeObjectURL(url); 49 | } catch (error) { 50 | const status = error?.response?.status; 51 | 52 | if (status === 401) { 53 | navigate("/login"); 54 | } else if (status === 400) { 55 | toast.error(error.response.data.message); 56 | } else if (status === 429) { 57 | toast.warn("Slow down on generating certificate!"); 58 | } else { 59 | toast.error( 60 | "Something went wrong getting user certificate. Please try again later" 61 | ); 62 | } 63 | } finally { 64 | setIsDownloadingCertificate(false); 65 | } 66 | }; 67 | 68 | return ( 69 | <> 70 | 83 | 84 | ); 85 | } 86 | -------------------------------------------------------------------------------- /src/features/admin/users/components/GenerateUserStatsButton.jsx: -------------------------------------------------------------------------------- 1 | import { Button, Spinner } from "react-bootstrap"; 2 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 3 | import { faDownload } from "@fortawesome/free-solid-svg-icons"; 4 | import { useState } from "react"; 5 | import { api } from "@/api"; 6 | import { useNavigate } from "react-router-dom"; 7 | import { toast } from "react-toastify"; 8 | 9 | export default function GenerateUserStatsButton({ userDetailsId }) { 10 | const navigate = useNavigate(); 11 | const [isGeneratingStatsReport, setIsGeneratingStatsReport] = useState(false); 12 | 13 | const generateStatsReport = async () => { 14 | if (isGeneratingStatsReport) return; 15 | try { 16 | setIsGeneratingStatsReport(true); 17 | 18 | try { 19 | await api.get(`/identity/users/${userDetailsId}/details`); 20 | } catch (error) { 21 | if (error?.response?.status === 404) { 22 | toast.error(error.response.data.message); 23 | return; 24 | } 25 | throw error; 26 | } 27 | 28 | const response = await api.get(`/play/users/${userDetailsId}/stats`, { 29 | responseType: "blob", 30 | }); 31 | 32 | const url = window.URL.createObjectURL(new Blob([response.data])); 33 | const link = document.createElement("a"); 34 | link.href = url; 35 | link.setAttribute("download", `user-stats-report-${userDetailsId}.pdf`); 36 | document.body.appendChild(link); 37 | link.click(); 38 | link.remove(); 39 | window.URL.revokeObjectURL(url); 40 | } catch (error) { 41 | const status = error?.response?.status; 42 | 43 | if (status === 401) { 44 | navigate("/login"); 45 | } else if (status === 400) { 46 | toast.error(error.response.data.message); 47 | } else if (status === 429) { 48 | toast.error("Slow down on downloading generating user stats!"); 49 | } else { 50 | toast.error( 51 | "Something went wrong generating user stats. Please try again later" 52 | ); 53 | } 54 | } finally { 55 | setIsGeneratingStatsReport(false); 56 | } 57 | }; 58 | 59 | return ( 60 | <> 61 | 74 | 75 | ); 76 | } 77 | -------------------------------------------------------------------------------- /src/features/admin/users/components/ResetCertificateButton.jsx: -------------------------------------------------------------------------------- 1 | import { Button, Spinner, Modal } from "react-bootstrap"; 2 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 3 | import { faTrash } from "@fortawesome/free-solid-svg-icons"; 4 | import { useState } from "react"; 5 | import { api } from "@/api"; 6 | import { useNavigate } from "react-router-dom"; 7 | import { toast } from "react-toastify"; 8 | 9 | export default function ResetCertificateButton({ userDetailsId }) { 10 | const navigate = useNavigate(); 11 | const [isResetingCertificate, setIsResettingCertificate] = useState(false); 12 | const [showConfirmResetCertificate, setShowConfirmResetCertificate] = 13 | useState(false); 14 | 15 | const handleResetCertificateClick = () => { 16 | setShowConfirmResetCertificate(true); 17 | }; 18 | 19 | const confirmResetCertificate = async () => { 20 | if (isResetingCertificate) return; 21 | setIsResettingCertificate(true); 22 | try { 23 | await api.delete(`/identity/users/${userDetailsId}/certificate`); 24 | toast.success("Successfully reset user certificate"); 25 | } catch (error) { 26 | const status = error?.response?.status; 27 | 28 | if (status === 401) { 29 | navigate("/login"); 30 | } else if (status === 400) { 31 | toast.error(error.response.data.message); 32 | } else { 33 | toast.error( 34 | "Something went wrong resetting user certificate. Please try again later" 35 | ); 36 | } 37 | } finally { 38 | setShowConfirmResetCertificate(false); 39 | setIsResettingCertificate(false); 40 | } 41 | }; 42 | 43 | return ( 44 | <> 45 | 58 | 59 | setShowConfirmResetCertificate(false)} 62 | > 63 | 64 | Confirm Reset User Certificate 65 | 66 | 67 |

Do you want to reset the certificate of this user?

68 |
69 | 70 | 76 | 83 | 84 |
85 | 86 | ); 87 | } 88 | -------------------------------------------------------------------------------- /src/features/admin/users/components/UserGraph.jsx: -------------------------------------------------------------------------------- 1 | import { Line } from "react-chartjs-2"; 2 | import "chart.js/auto"; // Required for automatic Chart.js version management 3 | 4 | export default function UserGraph({ userGraph }) { 5 | // Check if userGraph is undefined (loading state) 6 | if (userGraph === undefined) { 7 | return

; // Display loading message if userGraph is undefined 8 | } 9 | 10 | // Check if userGraph is null or empty 11 | if (!userGraph || userGraph.length === 0) { 12 | return

; // Display message if no data is available 13 | } 14 | 15 | // Prepare data for the chart 16 | const labels = userGraph.activities.map((entry) => 17 | new Date(entry.occurredAt).toLocaleString() 18 | ); 19 | const scores = userGraph.activities.map((entry) => entry.score); 20 | 21 | // Chart data 22 | const data = { 23 | labels, 24 | datasets: [ 25 | { 26 | label: "User Activity Score", 27 | data: scores, 28 | fill: false, 29 | backgroundColor: "rgba(75, 192, 192, 0.2)", 30 | borderColor: "rgba(75, 192, 192, 1)", 31 | tension: 0.1, 32 | }, 33 | ], 34 | }; 35 | 36 | // Chart options 37 | const options = { 38 | responsive: true, 39 | plugins: { 40 | legend: { 41 | display: true, // Show legend if you want 42 | }, 43 | title: { 44 | display: false, // Hide the title 45 | }, 46 | tooltip: { 47 | mode: "index", 48 | intersect: false, 49 | }, 50 | }, 51 | }; 52 | 53 | return ( 54 | <> 55 |

User Graph

56 | 57 | 58 | ); 59 | } 60 | -------------------------------------------------------------------------------- /src/features/admin/users/components/UsersList.jsx: -------------------------------------------------------------------------------- 1 | import { Table } from "react-bootstrap"; 2 | 3 | export default function UsersList({ users, showEmail, onUserClick }) { 4 | return ( 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | {users === undefined ? ( 19 | 20 | 23 | 24 | ) : users.length > 0 ? ( 25 | users.map((user) => ( 26 | onUserClick(user.id)} 29 | style={{ cursor: "pointer" }} 30 | > 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | )) 40 | ) : ( 41 | 42 | 45 | 46 | )} 47 | 48 |
IDUsernameFull NameEmailEmail ConfirmedVisible on LeaderboardsCreated At
21 | Click the search button to view users. 22 |
{user.id}{user.userName}{user.fullName}{showEmail ? user.email : "••••••••••"}{user.emailConfirmed ? "Yes" : "No"}{user.isVisibleOnLeaderboards ? "Yes" : "No"}{new Date(user.createdAt).toLocaleString()}
43 | No users found. 44 |
49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /src/features/admin/users/components/VerifyUserButton.jsx: -------------------------------------------------------------------------------- 1 | import { Button, Spinner, Modal } from "react-bootstrap"; 2 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 3 | import { faCheck } from "@fortawesome/free-solid-svg-icons"; 4 | import { useState } from "react"; 5 | import { api } from "@/api"; 6 | import { useNavigate } from "react-router-dom"; 7 | import { toast } from "react-toastify"; 8 | 9 | export default function VerifyUserButton({ 10 | userDetailsEmailConfirmed, 11 | getUserDetails, 12 | userDetailsId, 13 | }) { 14 | const navigate = useNavigate(); 15 | const [isVerifying, setIsVerifying] = useState(false); 16 | const [showConfirmVerifyModal, setShowConfirmVerifyModal] = useState(false); 17 | 18 | const handleVerifyClick = () => { 19 | setShowConfirmVerifyModal(true); 20 | }; 21 | 22 | const confirmVerify = async () => { 23 | setIsVerifying(true); 24 | try { 25 | await api.put(`/identity/users/${userDetailsId}/verify`); 26 | toast.success(`User verified successfully: ${userDetailsId}`); 27 | getUserDetails(userDetailsId); 28 | } catch (error) { 29 | const status = error?.response?.status; 30 | 31 | if (status === 401) { 32 | navigate("/login"); 33 | } else if (status === 403) { 34 | toast.error("You are not allowed to verify a user"); 35 | } else if (status === 400) { 36 | toast.error(error.response.data.message); 37 | } else { 38 | toast.error("Error verifying user. Please try again later"); 39 | } 40 | } finally { 41 | setShowConfirmVerifyModal(false); 42 | setIsVerifying(false); 43 | } 44 | }; 45 | 46 | return ( 47 | <> 48 | 61 | 62 | setShowConfirmVerifyModal(false)} 65 | > 66 | 67 | Confirm User Verification 68 | 69 | 70 |

Do you want to verify this user?

71 |
72 | 73 | 79 | 86 | 87 |
88 | 89 | ); 90 | } 91 | -------------------------------------------------------------------------------- /src/features/admin/users/index.js: -------------------------------------------------------------------------------- 1 | import UsersQueryInput from "./components/UsersQueryInput"; 2 | import UserSolves from "./components/UserSolves"; 3 | import UsersList from "./components/UsersList"; 4 | import UserDetails from "./components/UserDetails"; 5 | import UserHintUsages from "./components/UserHintUsages"; 6 | import UserGraph from "./components/UserGraph"; 7 | import UserEvaluations from "./components/UserEvaluations"; 8 | 9 | export { 10 | UsersQueryInput, 11 | UserSolves, 12 | UsersList, 13 | UserDetails, 14 | UserHintUsages, 15 | UserGraph, 16 | UserEvaluations, 17 | }; 18 | -------------------------------------------------------------------------------- /src/features/authentication/components/AccountHasVerified.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/no-unescaped-entities */ 2 | import { Link } from "react-router-dom"; 3 | 4 | // eslint-disable-next-line react/prop-types 5 | export default function AccountHasVerified({ email }) { 6 | return ( 7 |
14 |
15 |
16 |
17 |
18 |
19 | Image Description 25 |
26 |

27 | Welcome Aboard, and
Thank You for confirming! 28 |

29 |

30 | Your account has been successfully verified with the email{" "} 31 |
32 | {email} — you're all set! 33 |

34 | 35 |

36 | Ready to get started? Head over to the
37 | 42 | PWNEU Competition 43 | {" "} 44 | and begin your journey! 45 |

46 |
47 |
48 |
49 |
50 |
51 |
52 | ); 53 | } 54 | -------------------------------------------------------------------------------- /src/features/authentication/components/AccountVerificationFailed.jsx: -------------------------------------------------------------------------------- 1 | import { useNavigate } from "react-router-dom"; 2 | 3 | function AccountVerificationFailed() { 4 | const navigate = useNavigate(); 5 | 6 | return ( 7 |
14 |
15 |
16 |
17 |
18 |
19 | Image Description 25 |
26 |

27 | Account Verification Failed 28 |

29 |

30 | { 31 | "We encountered an issue verifying your account. Please try again later. If the problem persists, feel free to contact support for assistance." 32 | } 33 |
34 |

35 |
36 | 46 |
47 |
48 |
49 |
50 |
51 | ); 52 | } 53 | 54 | export default AccountVerificationFailed; 55 | -------------------------------------------------------------------------------- /src/features/authentication/components/AccountVerificationLoading.jsx: -------------------------------------------------------------------------------- 1 | export default function AccountVerificationLoading() { 2 | return ( 3 | <> 4 |
11 |
12 |
13 |
14 |
15 |
16 | Image Description 22 |
23 |

Verifying...

24 |
30 | Loading... 31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | 39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /src/features/authentication/components/PasswordResetCompleted.jsx: -------------------------------------------------------------------------------- 1 | import { useNavigate } from "react-router-dom"; 2 | 3 | export default function PasswordResetCompeleted() { 4 | const navigate = useNavigate(); 5 | 6 | return ( 7 |
14 |
15 |
16 |
17 |
18 |
19 | Image Description 25 |
26 | 27 |

28 | Password Changed Successfully! 29 |

30 |

31 | { 32 | "Your previous password has been reseted. Please set a new" 33 | } 34 |
35 | { 36 | "password for your account." 37 | } 38 |

39 |
40 | 50 |
51 |
52 |
53 |
54 |
55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /src/features/authentication/components/PasswordResetSent.jsx: -------------------------------------------------------------------------------- 1 | import { useNavigate } from "react-router-dom"; 2 | 3 | export default function PasswordResetSent() { 4 | const navigate = useNavigate(); 5 | 6 | return ( 7 |
14 |
15 |
16 |
17 |
18 |
19 | Image Description 25 |
26 | 27 |

28 | Password Reset Requested 29 |

30 |

31 | { 32 | "If the email you provided is valid, we’ve sent a link to reset your password." 33 | } 34 |
35 | { 36 | "Please check your inbox (and your spam folder, just in case)." 37 | } 38 |

39 |
40 | 50 |
51 |
52 |
53 |
54 |
55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /src/features/authentication/components/VerifyEmail.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { api } from "@/api"; 3 | import HeaderAuth from "@/features/authentication/layout/HeaderAuth"; 4 | import AuthImageMove from "@/features/authentication/layout/AuthImageMove"; 5 | import Preloader from "@/components/Preloader"; 6 | import MetaComponent from "@/components/MetaComponent"; 7 | import AccountHasVerified from "./AccountHasVerified"; 8 | import AccountVerificationFailed from "./AccountVerificationFailed"; 9 | 10 | // Unused but keep it 11 | 12 | const VERIFY_EMAIL_API = "/identity/verify"; 13 | 14 | const metadata = { 15 | title: "Verify Email || PWNEU", 16 | description: "Verify your email to join PWNEU", 17 | }; 18 | 19 | export default function VerifyEmail() { 20 | const [verificationStatus, setVerificationStatus] = useState(null); 21 | const [email, setEmail] = useState(null); 22 | 23 | useEffect(() => { 24 | const queryParams = new URLSearchParams(window.location.search); 25 | const emailParam = queryParams.get("email"); 26 | const confirmationToken = queryParams.get("confirmationToken"); 27 | 28 | if (emailParam && confirmationToken) { 29 | setEmail(emailParam); 30 | verifyEmail(emailParam, confirmationToken); 31 | } 32 | }, []); 33 | 34 | const verifyEmail = async (email, token) => { 35 | try { 36 | await api.post(VERIFY_EMAIL_API, { email, confirmationToken: token }); 37 | setVerificationStatus("success"); 38 | } catch (error) { 39 | setVerificationStatus("failed"); 40 | } 41 | }; 42 | 43 | return ( 44 |
45 | 46 | 47 | 48 |
49 |
50 | 51 | {verificationStatus === "success" ? ( 52 | 53 | ) : verificationStatus === "failed" ? ( 54 | 55 | ) : ( 56 | <> 57 | )} 58 |
59 |
60 |
61 | ); 62 | } 63 | -------------------------------------------------------------------------------- /src/features/authentication/index.js: -------------------------------------------------------------------------------- 1 | import AccountHasbeenCreated from "./components/AccountHasbeenCreated"; 2 | import AccountHasVerified from "./components/AccountHasVerified"; 3 | import AccountVerificationFailed from "./components/AccountVerificationFailed"; 4 | import AccountVerificationLoading from "./components/AccountVerificationLoading"; 5 | import ForgotPassword from "./components/ForgotPassword"; 6 | import LoginForm from "./components/LoginForm"; 7 | import PasswordResetCompleted from "./components/PasswordResetCompleted"; 8 | import PasswordResetSent from "./components/PasswordResetSent"; 9 | import ResetPassword from "./components/ResetPassword"; 10 | import SignUpForm from "./components/SignUpForm"; 11 | import VerifyCode from "./components/VerifyCode"; 12 | import VerifyEmail from "./components/VerifyEmail"; 13 | import AuthImageMove from "./layout/AuthImageMove"; 14 | import HeaderAuth from "./layout/HeaderAuth"; 15 | 16 | export { 17 | AccountHasbeenCreated, 18 | AccountHasVerified, 19 | AccountVerificationFailed, 20 | AccountVerificationLoading, 21 | ForgotPassword, 22 | LoginForm, 23 | PasswordResetCompleted, 24 | PasswordResetSent, 25 | ResetPassword, 26 | SignUpForm, 27 | VerifyCode, 28 | VerifyEmail, 29 | AuthImageMove, 30 | HeaderAuth, 31 | }; 32 | -------------------------------------------------------------------------------- /src/features/authentication/layout/AuthImageMove.jsx: -------------------------------------------------------------------------------- 1 | import gsap from "gsap"; 2 | import { useEffect } from "react"; 3 | 4 | export default function AuthImageMove() { 5 | useEffect(() => { 6 | const parallaxIt = () => { 7 | const target = document.querySelectorAll(".js-mouse-move-container"); 8 | 9 | target.forEach((container) => { 10 | const targets = container.querySelectorAll(".js-mouse-move"); 11 | 12 | targets.forEach((el) => { 13 | const movement = el.getAttribute("data-move"); 14 | 15 | document.addEventListener("mousemove", (e) => { 16 | const relX = e.pageX - container.offsetLeft; 17 | const relY = e.pageY - container.offsetTop; 18 | 19 | gsap.to(el, { 20 | x: 21 | ((relX - container.offsetWidth / 2) / container.offsetWidth) * 22 | Number(movement), 23 | y: 24 | ((relY - container.offsetHeight / 2) / container.offsetHeight) * 25 | Number(movement), 26 | duration: 0.2, 27 | }); 28 | }); 29 | }); 30 | }); 31 | }; 32 | 33 | parallaxIt(); 34 | }, []); 35 | return ( 36 |
37 |
38 | bg 45 |
46 | 47 | 48 |
49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /src/features/authentication/layout/HeaderAuth.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { Link } from "react-router-dom"; 3 | // import Menu from "../../../layout/components/Menu"; 4 | import MobileMenu from "../../../layout/components/MobileMenu"; 5 | 6 | export default function HeaderAuth() { 7 | const [activeMobileMenu, setActiveMobileMenu] = useState(false); 8 | return ( 9 |
10 |
11 |
15 |
16 |
17 |
18 | 19 | logo 20 | 21 |
22 |
23 |
24 | 25 |
26 |
27 | {/* */} 28 | 32 | 33 |
34 |
35 | 42 |
43 |
44 | 45 | {/* */} 53 |
54 |
55 |
56 |
57 |
58 | ); 59 | } 60 | -------------------------------------------------------------------------------- /src/features/campuses/components/CampusesBranch.jsx: -------------------------------------------------------------------------------- 1 | import { campuses } from "@/data/learningPaths"; 2 | 3 | export default function CampusPreview() { 4 | return ( 5 |
6 |
7 |
13 |
14 |
15 | campuses badge 24 |
25 |
26 |

27 | {" "} 28 | Campuses of New Era University 29 |

30 |
31 |
32 |

33 | A private educational institution in the Philippines, run by the 34 | Iglesia Ni Cristo. Although it is associated with the Iglesia Ni 35 | Cristo, it is a non-sectarian university. Its main campus is at 36 | New Era, Quezon City, within the Central Office Complex of the 37 | Iglesia Ni Cristo. 38 |

39 |
40 |
41 | 42 |
43 |
44 |
49 |
50 | {campuses.map((elm, i) => ( 51 |
56 |
57 |
58 |
59 | {elm.title} 60 |
61 |

62 | {elm.description} 63 |

64 |
65 |
66 |
67 | ))} 68 |
69 |
70 |
71 |
72 |
73 |
79 |
80 | ); 81 | } 82 | -------------------------------------------------------------------------------- /src/features/campuses/index.js: -------------------------------------------------------------------------------- 1 | import CampusPreview from "./components/CampusesBranch"; 2 | 3 | export { 4 | CampusPreview, 5 | }; 6 | -------------------------------------------------------------------------------- /src/features/challengeDetails/index.js: -------------------------------------------------------------------------------- 1 | import ChallengeDetails from "./components/ChallengeDetails"; 2 | import ChallengeDetailsArtifacts from "./components/ChallengeDetailsArtifacts"; 3 | import ChallengeDetailsHints from "./components/ChallengeDetailsHints"; 4 | import ChallengeDetailsOld from "./components/ChallengeDetailsOld"; 5 | import ChallengeInfoCard from "./components/ChallengeInfoCard"; 6 | import RecentSolvers from "./components/RecentSolvers"; 7 | 8 | export { 9 | ChallengeDetails, 10 | ChallengeDetailsArtifacts, 11 | ChallengeDetailsHints, 12 | ChallengeDetailsOld, 13 | ChallengeInfoCard, 14 | RecentSolvers, 15 | }; 16 | -------------------------------------------------------------------------------- /src/features/challenges/components/CategoryMobileFilter.jsx: -------------------------------------------------------------------------------- 1 | export default function CategoryMobileFilter({ 2 | categories, 3 | selectedCategory, 4 | setSelectedCategory, 5 | isBusy, 6 | }) { 7 | const handleSelectedCategoryChange = (category) => { 8 | if (isBusy) return; 9 | sessionStorage.setItem("selectedCategoryId", category?.id || ""); 10 | setSelectedCategory(category); 11 | }; 12 | 13 | return ( 14 | <> 15 |
16 |
Category
17 |
18 |
handleSelectedCategoryChange(null)} 21 | > 22 |
23 | 28 |
29 |
30 |
31 |
32 | 33 |
All
34 |
35 |
36 | {categories.map((category, index) => ( 37 |
handleSelectedCategoryChange(category)} 41 | > 42 |
43 | 48 |
49 |
50 |
51 |
52 | 53 |
54 | {" "} 55 | {`${category.name} (${category.challengesCount})`} 56 |
57 |
58 | ))} 59 |
60 |
61 |
62 | 63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /src/features/challenges/components/ChallengesSortBy.jsx: -------------------------------------------------------------------------------- 1 | import { sortByOptions } from "../data/challengesFilterOptions"; 2 | 3 | export default function ChallengesSortBy({ 4 | selectedSortBy, 5 | setSelectedSortBy, 6 | isBusy, 7 | }) { 8 | const handleSelectedSortByChange = (sortByOption) => { 9 | if (isBusy) return; 10 | setSelectedSortBy(sortByOption); 11 | document.getElementById("sortByButton").classList.toggle("-is-dd-active"); 12 | document.getElementById("sortByContent").classList.toggle("-is-el-visible"); 13 | }; 14 | 15 | return ( 16 | <> 17 |
18 |
19 |
Sort by:
20 | 21 |
25 |
{ 27 | document 28 | .getElementById("sortByButton") 29 | .classList.toggle("-is-dd-active"); 30 | document 31 | .getElementById("sortByContent") 32 | .classList.toggle("-is-el-visible"); 33 | }} 34 | className="dropdown__button d-flex items-center text-14 rounded-8 px-20 py-10 text-14 lh-12" 35 | data-el-toggle=".js-category-toggle" 36 | data-el-toggle-active=".js-category-active" 37 | > 38 | {selectedSortBy} 39 | 40 |
41 | 42 |
46 |
47 | {sortByOptions.map((sortByOption, i) => ( 48 |
{ 51 | handleSelectedSortByChange(sortByOption); 52 | }} 53 | > 54 | 59 | {sortByOption} 60 | 61 |
62 | ))} 63 |
64 |
65 |
66 |
67 |
68 | 69 | ); 70 | } 71 | -------------------------------------------------------------------------------- /src/features/challenges/components/ChallengesSortOrder.jsx: -------------------------------------------------------------------------------- 1 | import { sortOrderOptions } from "../data/challengesFilterOptions"; 2 | 3 | export default function ChallengesSortOrder({ 4 | selectedSortOrder, 5 | setSelectedSortOrder, 6 | isBusy, 7 | }) { 8 | const handleSelectedSortOrderChange = (sortOrderOption) => { 9 | if (isBusy) return; 10 | setSelectedSortOrder(sortOrderOption); 11 | document 12 | .getElementById("sortOrderButton") 13 | .classList.toggle("-is-dd-active"); 14 | document 15 | .getElementById("sortOrderContent") 16 | .classList.toggle("-is-el-visible"); 17 | }; 18 | 19 | return ( 20 | <> 21 |
22 |
23 |
24 | Sort Order: 25 |
26 | 27 |
31 |
{ 33 | document 34 | .getElementById("sortOrderButton") 35 | .classList.toggle("-is-dd-active"); 36 | document 37 | .getElementById("sortOrderContent") 38 | .classList.toggle("-is-el-visible"); 39 | }} 40 | className="dropdown__button d-flex items-center text-14 rounded-8 px-20 py-10" 41 | > 42 | 43 | {selectedSortOrder.value} 44 | 45 | 46 |
47 | 48 |
52 |
53 | {sortOrderOptions.map((sortOrderOption) => ( 54 |
{ 57 | handleSelectedSortOrderChange(sortOrderOption); 58 | }} 59 | > 60 | 67 | {sortOrderOption.value} 68 | 69 |
70 | ))} 71 |
72 |
73 |
74 |
75 |
76 | 77 | ); 78 | } 79 | -------------------------------------------------------------------------------- /src/features/challenges/components/ExcludeSolvesFilter.jsx: -------------------------------------------------------------------------------- 1 | import { excludeSolveOptions } from "../data/challengesFilterOptions"; 2 | import { useState } from "react"; 3 | 4 | export default function ExcludeSolvesFilter({ 5 | selectedExcludeSolves, 6 | setSelectedExcludeSolves, 7 | isBusy, 8 | }) { 9 | const [excludeSolvesOpen, setExcludeSolvesOpen] = useState(true); 10 | 11 | const handleSelectedExcludeSolvesChange = (excludeSolveOption) => { 12 | if (isBusy) return; 13 | setSelectedExcludeSolves(excludeSolveOption); 14 | }; 15 | 16 | return ( 17 | <> 18 |
19 |
20 |
25 |
setExcludeSolvesOpen((pre) => !pre)} 28 | > 29 |
Exclude Solves
30 | 31 |
32 |
33 |
34 |
35 |
36 | 37 |
41 |
42 |
43 | {excludeSolveOptions.map((excludeSolveOption, i) => ( 44 |
47 | handleSelectedExcludeSolvesChange(excludeSolveOption) 48 | } 49 | className="sidebar-checkbox__item cursor" 50 | > 51 |
52 | 59 |
60 |
61 |
62 |
63 | 64 |
65 | {excludeSolveOption.value} 66 |
67 |
68 | ))} 69 |
70 |
71 |
72 |
73 |
74 |
75 | 76 | ); 77 | } 78 | -------------------------------------------------------------------------------- /src/features/challenges/components/ExcludeSolvesMobileFilter.jsx: -------------------------------------------------------------------------------- 1 | import { excludeSolveOptions } from "../data/challengesFilterOptions"; 2 | 3 | export default function ExcludeSolvesMobileFilter({ 4 | selectedExcludeSolves, 5 | setSelectedExcludeSolves, 6 | isBusy, 7 | }) { 8 | const handleSelectedExcludeSolvesChange = (excludeSolveOption) => { 9 | if (isBusy) return; 10 | setSelectedExcludeSolves(excludeSolveOption); 11 | }; 12 | 13 | return ( 14 | <> 15 |
16 |
Exclude Solves
17 |
18 | {excludeSolveOptions.map((excludeSolveOption, index) => ( 19 |
23 | handleSelectedExcludeSolvesChange(excludeSolveOption) 24 | } 25 | > 26 |
27 | 32 |
33 |
34 |
35 |
36 | 37 |
38 | {excludeSolveOption.value} 39 |
40 |
41 | ))} 42 |
43 |
44 | 45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /src/features/challenges/components/FilterButton.jsx: -------------------------------------------------------------------------------- 1 | export default function FilterButton({ setFilterOpen }) { 2 | return ( 3 | <> 4 | {/* Filter Button */} 5 |
6 |
7 | 14 |
15 |
16 | {/* End Filter Button */} 17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /src/features/challenges/components/SidebarFilters.jsx: -------------------------------------------------------------------------------- 1 | 2 | // export default function SidebarFilters({ 3 | // pageNumber, 4 | // setPageNumber, 5 | // data, 6 | // pageCapacity, 7 | // }) { 8 | // const handlePrevious = () => { 9 | // if (pageNumber == 1) { 10 | // } else { 11 | // setPageNumber((pre) => pre - 1); 12 | // } 13 | // }; 14 | // const handleNext = () => { 15 | // if (Math.ceil(data.length / pageCapacity) > pageNumber) { 16 | // setPageNumber((pre) => pre + 1); 17 | // } 18 | // }; 19 | 20 | // return ( 21 | //
22 | // 25 | 26 | //
27 | // setPageNumber(1)} 29 | // className={pageNumber == 1 ? "-count-is-active" : ""} 30 | // href="#" 31 | // > 32 | // 1 33 | // 34 | // {data.length > pageCapacity ? ( 35 | // setPageNumber(2)} 37 | // className={pageNumber == 2 ? "-count-is-active" : ""} 38 | // href="#" 39 | // > 40 | // 2 41 | // 42 | // ) : ( 43 | // "" 44 | // )} 45 | // {data.length > pageCapacity * 2 ? ( 46 | // setPageNumber(3)} 48 | // className={pageNumber == 3 ? "-count-is-active" : ""} 49 | // href="#" 50 | // > 51 | // 3 52 | // 53 | // ) : ( 54 | // "" 55 | // )} 56 | 57 | // {data.length > pageCapacity * 4 && pageNumber != 4 && ...} 58 | 59 | // {pageNumber > 3 && 60 | // Math.ceil(data.length / pageCapacity) != pageNumber && ( 61 | // 62 | // {pageNumber} 63 | // 64 | // )} 65 | // {data.length > pageCapacity * 4 && 66 | // pageNumber < Math.ceil(data.length / pageCapacity) - 1 && 67 | // pageNumber > 3 && ...} 68 | // {data.length > pageCapacity * 3 + 1 ? ( 69 | // setPageNumber(Math.ceil(data.length / pageCapacity))} 76 | // > 77 | // {Math.ceil(data.length / pageCapacity)} 78 | // 79 | // ) : ( 80 | // "" 81 | // )} 82 | //
83 | 84 | // 87 | //
88 | // ); 89 | // } 90 | -------------------------------------------------------------------------------- /src/features/challenges/data/challengesFilterOptions.js: -------------------------------------------------------------------------------- 1 | export const sortByOptions = ["Default", "Points", "Solves", "Name"]; 2 | export const sortOrderOptions = [ 3 | { id: "asc", value: "Ascending" }, 4 | { id: "desc", value: "Descending" }, 5 | ]; 6 | export const excludeSolveOptions = [ 7 | { id: true, value: "Yes" }, 8 | { id: false, value: "No" }, 9 | ]; 10 | -------------------------------------------------------------------------------- /src/features/challenges/index.js: -------------------------------------------------------------------------------- 1 | import ChallengesCategoryFilter from "./components/CategoryFilter"; 2 | import ExcludeSolvesFilter from "./components/ExcludeSolvesFilter"; 3 | import FilterButton from "./components/FilterButton"; 4 | import ChallengesList from "./components/ChallengesList"; 5 | import ChallengesPagination from "./components/ChallengesPagination"; 6 | import ChallengesContainer from "./layout/ChallengesContainer"; 7 | import ChallengesHeader from "./layout/ChallengesHeader"; 8 | 9 | export { 10 | ChallengesCategoryFilter, 11 | ExcludeSolvesFilter, 12 | FilterButton, 13 | ChallengesContainer, 14 | ChallengesList, 15 | ChallengesHeader, 16 | ChallengesPagination, 17 | }; 18 | -------------------------------------------------------------------------------- /src/features/challenges/layout/ChallengesHeader.jsx: -------------------------------------------------------------------------------- 1 | import { useLayoutEffect } from "react"; 2 | 3 | export default function ChallengesHeader() { 4 | useLayoutEffect(() => { 5 | const link = document.createElement("link"); 6 | link.rel = "stylesheet"; 7 | link.href = "/assets/css/leaderboards.css"; 8 | document.head.appendChild(link); 9 | 10 | return () => { 11 | document.head.removeChild(link); 12 | }; 13 | }, []); 14 | 15 | return ( 16 |
17 |
18 |
19 |
20 | NEU Logo 25 |
26 |
27 | 28 |

CTF Challenges

29 |

30 | Explore a diverse range of cybersecurity challenges designed 31 | to test your skills and knowledge across various domains. 32 | Whether you’re a beginner looking to get started or an 33 | experienced professional seeking to sharpen your expertise, 34 | our challenges cater to all levels and interests. Filter by 35 | difficulty or category to find the perfect challenge for you, 36 | or browse through events to see the latest and past 37 | competitions. 38 |

39 | 40 |
41 | 42 |
43 |
44 |
45 | ); 46 | } -------------------------------------------------------------------------------- /src/features/chatbot/components/ChatHeader.jsx: -------------------------------------------------------------------------------- 1 | const ChatHeader = () => ( 2 |
3 |
4 |
5 |
6 |
7 |

13 | {" "} 14 | {"Dash AI Chatbot (Experimental v0.0.1) "}{" "} 15 |

16 |

22 | Learn to use Dash AI, the advanced chatbot designed for CTF 23 | challenges. Get real-time hints, guidance, and interactive 24 | problem-solving to enhance your cybersecurity skills and tackle 25 | challenges with confidence. Start your CTF journey today! 26 |

27 |
28 |
29 |
35 | image 40 |
41 |
42 |
43 |
44 | ); 45 | 46 | export default ChatHeader; 47 | -------------------------------------------------------------------------------- /src/features/chatbot/components/ChatInterface.jsx: -------------------------------------------------------------------------------- 1 | import { useState, useLayoutEffect } from "react"; 2 | import MessageInput from "./MessageInput"; 3 | import Result from "./Result"; 4 | import { api } from "@/api"; // Uncomment if testing chatbot 5 | import { toast } from "react-toastify"; 6 | import { useNavigate } from "react-router-dom"; 7 | 8 | const ChatInterface = () => { 9 | useLayoutEffect(() => { 10 | const link = document.createElement("link"); 11 | link.rel = "stylesheet"; 12 | link.href = "/assets/css/chatui.css"; 13 | document.head.appendChild(link); 14 | 15 | return () => { 16 | document.head.removeChild(link); 17 | }; 18 | }, []); 19 | 20 | const [message, setMessage] = useState(""); 21 | const [result, setResult] = useState(""); 22 | const [isGenerating, setIsGenerating] = useState(false); 23 | const navigate = useNavigate(); 24 | 25 | // Function to handle message input change 26 | const handleInputChange = (event) => { 27 | setMessage(event.target.value); 28 | }; 29 | 30 | // Function to generate result based on the user's input 31 | const generateResult = async () => { 32 | if (isGenerating || message === "") return; 33 | 34 | try { 35 | setIsGenerating(true); 36 | setResult("Generating..."); 37 | 38 | const response = await api.post("/chat/conversations", { 39 | input: message, 40 | }); 41 | 42 | setResult(response.data); 43 | 44 | // setTimeout(() => { 45 | // setResult(sampleResult); 46 | // }, 1000); 47 | } catch (error) { 48 | const status = error?.response?.status; 49 | 50 | setResult("Failed..."); 51 | 52 | if (status === 401) { 53 | navigate("/login"); 54 | } else if (status === 400) { 55 | toast.info(error.response?.data?.message); 56 | } else if (status === 403) { 57 | toast.info("Managers and admins are not allowed to chat with the bot"); 58 | } else if (status === 429) { 59 | toast.warn("Slow down on chatting!"); 60 | } else { 61 | toast.error("Error creating conversation. Please try again later"); 62 | } 63 | } finally { 64 | setIsGenerating(false); 65 | } 66 | }; 67 | 68 | return ( 69 |
75 |
76 | 81 | 82 |
83 |
84 | ); 85 | }; 86 | 87 | export default ChatInterface; 88 | -------------------------------------------------------------------------------- /src/features/chatbot/components/MessageInput.jsx: -------------------------------------------------------------------------------- 1 | const MessageInput = ({ message, onInputChange, onGenerateResult }) => { 2 | return ( 3 |
4 | { 9 | if (e.key === "Enter") { 10 | onGenerateResult(); 11 | } 12 | }} 13 | placeholder="Message Dash AI" 14 | /> 15 | Generate Answer 27 |
28 | ); 29 | }; 30 | 31 | export default MessageInput; 32 | -------------------------------------------------------------------------------- /src/features/chatbot/components/Result.jsx: -------------------------------------------------------------------------------- 1 | export default function Result({ result }) { 2 | return ( 3 |
4 |
5 | {result === "" || result == null ? ( 6 |

What can I help with?

7 | ) : ( 8 |

") 12 | .replace(/\*\*(.*?)\*\*/g, "$1"), 13 | }} 14 | /> 15 | )} 16 |

17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /src/features/chatbot/index.js: -------------------------------------------------------------------------------- 1 | import ChatHeader from "./components/ChatHeader"; 2 | import ChatInterface from "./components/ChatInterface"; 3 | import MessageInput from "./components/MessageInput"; 4 | 5 | 6 | export { 7 | ChatHeader, 8 | ChatInterface, 9 | MessageInput, 10 | }; 11 | -------------------------------------------------------------------------------- /src/features/contact/components/MapComponent.jsx: -------------------------------------------------------------------------------- 1 | export default function MapComponent() { 2 | return ( 3 |
4 | 13 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /src/features/contact/components/NotFound.jsx: -------------------------------------------------------------------------------- 1 | export default function NotFound() { 2 | return ( 3 | <> 4 |
5 |
6 |
7 |
8 |
9 | image 13 |
14 |
15 | 16 |
17 |
18 |

19 | 404 20 |

21 |

22 | Oops! It looks like you're lost. 23 |

24 |
25 | The page you're looking for isn't available. 26 |
27 |
28 |

32 | {"PWNEU{404_N0t_f0UNd}"} 33 |

34 |
35 |
36 |
37 |
38 |
39 | 40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /src/features/contact/index.js: -------------------------------------------------------------------------------- 1 | import Contact from "./components/Contact"; 2 | import DiscussionForum from "./components/DiscussionForum"; 3 | import MapComponent from "./components/MapComponent"; 4 | import NotFound from "./components/NotFound"; 5 | 6 | export { Contact, DiscussionForum, MapComponent,NotFound }; 7 | -------------------------------------------------------------------------------- /src/features/home/components/Features.jsx: -------------------------------------------------------------------------------- 1 | import { feature } from "@/data/features"; 2 | 3 | export default function Features() { 4 | return ( 5 |
6 |
7 |
8 |
9 |
10 |

14 | Master New Skills on Your Own Terms. 15 |

16 |

17 | College of Informatics and Computing Studies

New Era University 18 |

19 | 20 |

21 | Unlock the benefits of PWNEU Missions, designed specifically 22 |
for university students like you: 23 |

24 |
25 | {feature.map((elm, i) => ( 26 |
31 | 40 |
41 | {elm.title} 42 |
43 |
44 | ))} 45 |
46 |
47 |
48 | 49 |
53 |
54 | 58 |
59 |
60 |
61 |
62 |
63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /src/features/home/components/FindLearningPath.jsx: -------------------------------------------------------------------------------- 1 | import { Link } from "react-router-dom"; 2 | 3 | export default function FindLearningPath() { 4 | return ( 5 |
6 |
7 |
14 |
15 | 16 |
17 |
18 |
19 |

25 | Find Your Cybersecurity Pathway 26 |

27 |
28 | 29 |
30 | 31 |
32 |

38 | Discover Your Perfect Match: Align your skills and goals with our 39 | tailored cybersecurity programs and challenges. Explore different 40 | learning paths, from beginner to advanced, and plot your course to 41 | mastering the art of digital defense. 42 |

43 |
44 |
45 | 46 |
47 | 48 |
49 | 56 | Get Started on Your Cybersecurity Journey Today 57 | 58 |
59 |
60 |
61 |
62 | ); 63 | } 64 | -------------------------------------------------------------------------------- /src/features/home/components/WhyPWNEU.jsx: -------------------------------------------------------------------------------- 1 | import {whyourcourse } from "@/data/whyourcourse"; 2 | 3 | export default function WhyPWNEU() { 4 | return ( 5 |
6 |
7 |
8 |
9 |
10 |

15 | Why learn with our courses? 16 |

17 | 18 |

23 | Unlock your potential with courses designed to make cybersecurity both engaging and effective. 24 |

25 |
26 |
27 |
28 | 29 |
30 | {whyourcourse.map((elm, i) => ( 31 |
37 |
38 |
39 |
40 | 41 |
42 |

{elm.title}

43 |

{elm.text}

44 |
45 |
46 |
47 | ))} 48 |
49 |
50 |
51 | ); 52 | } 53 | -------------------------------------------------------------------------------- /src/features/home/index.js: -------------------------------------------------------------------------------- 1 | import CampusesTag from "./components/CampusesTag"; 2 | import Features from "./components/Features"; 3 | import LearningFeatures from "./components/LearningFeatures"; 4 | import FindLearningPath from "./components/FindLearningPath"; 5 | import HomeHero from "./components/HomeHero"; 6 | import TopCategories from "./components/TopCategories"; 7 | import WhyPWNEU from "./components/WhyPWNEU"; 8 | import Achievements from "./components/Achievements"; 9 | import Testimonials from "./components/Testimonials"; 10 | 11 | export { 12 | CampusesTag, 13 | Features, 14 | LearningFeatures, 15 | FindLearningPath, 16 | HomeHero, 17 | Testimonials, 18 | TopCategories, 19 | WhyPWNEU, 20 | Achievements 21 | }; 22 | -------------------------------------------------------------------------------- /src/features/leaderboards/components/LeaderboardsLoading.jsx: -------------------------------------------------------------------------------- 1 | export default function LeaderboardsLoading() { 2 | return ( 3 | <> 4 |

5 | Loading Leaderboards... 6 |

7 |
14 | Loading... 15 |
16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /src/features/leaderboards/components/NoLeaderboards.jsx: -------------------------------------------------------------------------------- 1 | export default function NoLeaderboards() { 2 | return ( 3 | <> 4 |
8 |
9 |
10 |

11 | No Leaderboards Available... 12 |

13 |
14 |
15 | image 19 |
20 |
21 |
22 |
23 |
24 | 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/features/leaderboards/components/UserRanks.jsx: -------------------------------------------------------------------------------- 1 | import { useNavigate } from "react-router-dom"; 2 | 3 | export default function UserRanks({ 4 | userRanks, 5 | requesterRank, 6 | isManager, 7 | totalLeaderboardCount, 8 | }) { 9 | const navigate = useNavigate(); 10 | 11 | const renderUserRankItem = (rank, isRequester) => { 12 | const userLink = `/admin/user/${rank.id}`; 13 | const className = isRequester ? "rank-item requester" : "rank-item"; 14 | 15 | const handleRowClick = () => { 16 | if (isRequester) { 17 | navigate("/dashboard"); 18 | } else if (isManager) { 19 | navigate(userLink); 20 | } 21 | }; 22 | 23 | return ( 24 |
29 |
{rank.position}
30 |
34 | {rank.userName} 35 |
36 | {`${rank.userName} latest solve time: ${new Date( 37 | rank.latestSolve 38 | ).toLocaleString()}`} 39 |
40 |
41 |
{rank.points}
42 |
43 | ); 44 | }; 45 | 46 | // Check if requesterRank is in userRanks 47 | const isRequesterInUserRanks = 48 | requesterRank && userRanks.some((rank) => rank.id === requesterRank.id); 49 | 50 | return ( 51 |
57 |

58 | {`There are currently (${totalLeaderboardCount}) participant(s).`} 59 |

60 |
61 |
62 |
Rank
63 |
Username
64 |
Points
65 |
66 |
67 | {userRanks.map((rank) => 68 | renderUserRankItem( 69 | rank, 70 | isRequesterInUserRanks && rank.id === requesterRank.id 71 | ) 72 | )} 73 | {requesterRank && !isRequesterInUserRanks && ( 74 | <> 75 |
76 | {renderUserRankItem(requesterRank, true)} 77 | 78 | )} 79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | ); 87 | } 88 | -------------------------------------------------------------------------------- /src/features/leaderboards/index.js: -------------------------------------------------------------------------------- 1 | import UserRanks from "./components/UserRanks"; 2 | import LeaderboardGraph from "./components/LeaderboardGraph"; 3 | import LeaderboardOverview from "@/features/leaderboards/layout/LeaderboardOverview"; 4 | import NoLeaderboards from "./components/NoLeaderboards"; 5 | import LeaderboardsLoading from "./components/LeaderboardsLoading"; 6 | 7 | export { 8 | LeaderboardOverview, 9 | UserRanks, 10 | LeaderboardGraph, 11 | NoLeaderboards, 12 | LeaderboardsLoading, 13 | }; 14 | -------------------------------------------------------------------------------- /src/features/leaderboards/layout/LeaderboardOverview.jsx: -------------------------------------------------------------------------------- 1 | export default function LeaderboardOverview() { 2 | return ( 3 |
4 |
5 |
6 |
7 |

13 | PWNEU CTF Leaderboard 14 |

15 |

21 | The CTF (Capture the Flag) Leaderboard displays the rankings of 22 | participants based on their performance in solving challenges. 23 | Each participant earns points for completing tasks of varying 24 | difficulty, with higher points awarded for more complex or unique 25 | challenges. 26 |

27 |

33 | The leaderboard reflects real-time standings, showcasing the top 34 | scorers and encouraging a competitive and engaging environment. 35 |

36 |
37 |
43 | NEU Logo 48 |
49 |
50 |
51 |
52 | ); 53 | } 54 | -------------------------------------------------------------------------------- /src/features/ownership/index.js: -------------------------------------------------------------------------------- 1 | import PrivacyPolicyTabs from "./components/PrivacyPolicyTabs"; 2 | import TermsAndConditionTabs from "./components/TermsAndConditionTabs"; 3 | 4 | export { PrivacyPolicyTabs, TermsAndConditionTabs }; 5 | -------------------------------------------------------------------------------- /src/features/profile/components/UserOverviewOld.jsx: -------------------------------------------------------------------------------- 1 | import { platformoverview } from "../data/platformoverview"; 2 | export default function UserOverviewOld() { 3 | return ( 4 |
5 |
6 |
10 |
11 | 12 |
13 |
14 |
19 |

20 | Grow your career today with the Education courses 21 |

22 |

23 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do 24 | eiusmod tempor incididunt. 25 |

26 | 31 |
32 | 33 |
38 |
39 | {platformoverview.map((elm, i) => ( 40 |
41 |
42 |
43 | {elm.score} 44 |
45 |
{elm.text}
46 |
47 |
48 | ))} 49 |
50 |
51 |
52 |
53 |
54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /src/features/profile/components/UserProfileEvaluation.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --color-purple-1: #3a045e; 3 | --color-dark-1: #010124; 4 | 5 | } 6 | 7 | .animated-chart { 8 | transition: all 0.3s ease; 9 | } 10 | 11 | .animated-chart:hover { 12 | transform: scale(1.05); /* Enlarge the pie chart on hover */ 13 | } 14 | 15 | .animated-bar { 16 | transition: all 0.3s ease; 17 | } 18 | 19 | .animated-bar:hover { 20 | transform: scale(1.05); /* Enlarge the bar chart on hover */ 21 | } 22 | 23 | canvas { 24 | transition: background-color 0.5s ease-in-out; 25 | } 26 | 27 | canvas:hover { 28 | background-color: var(--color-dark-1); 29 | } 30 | -------------------------------------------------------------------------------- /src/features/profile/components/UserProfileGraphOld.jsx: -------------------------------------------------------------------------------- 1 | import { Line } from "react-chartjs-2"; 2 | import "chart.js/auto"; // Required for automatic Chart.js version management 3 | import { api } from "@/api"; 4 | import { useState, useEffect } from "react"; 5 | 6 | export default function UserProfileGraphOld() { 7 | const [userGraph, setUserGraph] = useState(); 8 | 9 | const getMyGraph = async () => { 10 | try { 11 | const response = await api.get(`/play/me/graph`); 12 | setUserGraph(response.data); 13 | } catch { 14 | setUserGraph(null); 15 | } 16 | }; 17 | 18 | useEffect(() => { 19 | getMyGraph(); 20 | }, []); 21 | 22 | // Display loading message if userGraph is undefined 23 | if (userGraph === undefined) { 24 | return

Loading...

; 25 | } 26 | 27 | // Check if userGraph is null, undefined, or empty 28 | if (!userGraph || userGraph.length === 0) { 29 | return

No data available.

; 30 | } 31 | 32 | // Prepare data for the chart 33 | const labels = userGraph.map((entry) => 34 | new Date(entry.activityDate).toLocaleString() 35 | ); 36 | const scores = userGraph.map((entry) => entry.score); 37 | 38 | // Chart data 39 | const data = { 40 | labels, 41 | datasets: [ 42 | { 43 | label: "Activity Score", 44 | data: scores, 45 | fill: false, 46 | backgroundColor: "rgba(75, 192, 192, 0.2)", 47 | borderColor: "rgba(75, 192, 192, 1)", 48 | tension: 0.1, 49 | }, 50 | ], 51 | }; 52 | 53 | // Chart options 54 | const options = { 55 | responsive: true, 56 | plugins: { 57 | legend: { 58 | display: true, // Show legend if you want 59 | }, 60 | title: { 61 | display: false, // Hide the title 62 | }, 63 | tooltip: { 64 | mode: "index", 65 | intersect: false, 66 | }, 67 | }, 68 | }; 69 | 70 | return ( 71 |
72 |

Graph Activity

73 |
74 | {" "} 75 | {/* Adjusted height values */} 76 | 77 |
78 |
79 | ); 80 | } 81 | -------------------------------------------------------------------------------- /src/features/profile/data/dashboard.js: -------------------------------------------------------------------------------- 1 | export const RankPoints = [ 2 | { 3 | id: 1, 4 | title: "Rank", 5 | value: 13, 6 | iconClass: "icon-bar-chart-2", 7 | }, 8 | { 9 | id: 2, 10 | title: "Points", 11 | value: 5000, 12 | iconClass: "icon-graduation-1", 13 | }, 14 | { 15 | id: 3, 16 | title: "Total Solves", 17 | value: 150, 18 | iconClass: "icon-person-3", 19 | }, 20 | { 21 | id: 3, 22 | title: "Hint Usage", 23 | value: 22, 24 | iconClass: "icon-search", 25 | }, 26 | 27 | 28 | ]; 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/features/profile/data/platformoverview.js: -------------------------------------------------------------------------------- 1 | 2 | export const platformoverview = [ 3 | { 4 | id: 1, 5 | score: "700", 6 | text: "Total number of participants", 7 | className: "-el-1", 8 | }, 9 | { 10 | id: 2, 11 | score: "85%", 12 | text: "Your current rank percentile", 13 | className: "-el-2", 14 | }, 15 | { 16 | id: 3, 17 | score: "5500", 18 | text: "Highest Scoring Challenge: Web Explotation", 19 | className: "-el-3", 20 | } 21 | ]; 22 | -------------------------------------------------------------------------------- /src/features/profile/index.js: -------------------------------------------------------------------------------- 1 | import UserProfileOld from "./components/UserProfileOld"; 2 | import UserOverviewOld from "./components/UserOverviewOld"; 3 | import UserProfileEvaluationOld from "./components/UserProfileEvaluationOld"; 4 | import UserProfileGraphOld from "./components/UserProfileGraphOld"; 5 | import UserProfileSolvesOld from "./components/UserProfileSolvesOld"; 6 | import UserProfileHintUsagesOld from "./components/UserProfileHintUsagesOld"; 7 | 8 | export { 9 | UserProfileOld, 10 | UserOverviewOld, 11 | UserProfileEvaluationOld, 12 | UserProfileGraphOld, 13 | UserProfileHintUsagesOld, 14 | UserProfileSolvesOld, 15 | }; 16 | -------------------------------------------------------------------------------- /src/features/user-profile/components/Charts.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | LineChart, 3 | Tooltip, 4 | Line, 5 | CartesianGrid, 6 | XAxis, 7 | YAxis, 8 | ResponsiveContainer, 9 | } from "recharts"; 10 | 11 | const data = [ 12 | { name: "Jan", value: 148 }, 13 | { name: "Feb", value: 100 }, 14 | { name: "Marc", value: 205 }, 15 | { name: "April", value: 110 }, 16 | { name: "May", value: 165 }, 17 | { name: "Jun", value: 145 }, 18 | { name: "July", value: 180 }, 19 | { name: "Agust", value: 156 }, 20 | { name: "Sept", value: 148 }, 21 | { name: "Oct", value: 220 }, 22 | { name: "Now", value: 180 }, 23 | { name: "Dec", value: 245 }, 24 | ]; 25 | 26 | const Charts = () => { 27 | const chart = (interval) => ( 28 | 29 | 30 | 31 | 32 | 38 | 39 | 47 | 48 | 49 | ); 50 | 51 | return <>{chart("preserveEnd")}; 52 | }; 53 | 54 | export default Charts; 55 | -------------------------------------------------------------------------------- /src/features/user-profile/components/PieCharts.jsx: -------------------------------------------------------------------------------- 1 | import { Pie, Tooltip, Legend, PieChart, ResponsiveContainer } from "recharts"; 2 | 3 | const data = [ 4 | { name: "Direct", value: 400 }, 5 | { name: "Referal", value: 300 }, 6 | { name: "Organic", value: 300 }, 7 | ]; 8 | 9 | const PieChartComponent = () => { 10 | return ( 11 | 12 | 13 | 23 | 24 | 25 | 26 | 27 | ); 28 | }; 29 | export default PieChartComponent; 30 | -------------------------------------------------------------------------------- /src/features/user-profile/components/UserProfileDetails.jsx: -------------------------------------------------------------------------------- 1 | import { api } from "@/api"; 2 | import { useState, useEffect } from "react"; 3 | import { toast } from "react-toastify"; 4 | 5 | export default function UserProfileDetails({ activeTab }) { 6 | const [userEmail, setUserEmail] = useState(""); 7 | const [userDetails, setUserDetails] = useState(); 8 | 9 | useEffect(() => { 10 | const getMyEmail = async () => { 11 | try { 12 | const response = await api.get(`/identity/me/email`); 13 | setUserEmail(response.data); 14 | // eslint-disable-next-line no-unused-vars 15 | } catch (error) { 16 | setUserEmail("No Email"); 17 | } 18 | }; 19 | 20 | const getMyDetails = async () => { 21 | try { 22 | const response = await api.get(`/identity/me/details`); 23 | setUserDetails(response.data); 24 | } catch (error) { 25 | const status = error?.response?.status; 26 | 27 | if (status === 404) { 28 | toast.error(error.response.data.message || "User not found"); 29 | } else { 30 | toast.error( 31 | "Something went wrong getting user details. Please try again later" 32 | ); 33 | } 34 | setUserDetails(null); 35 | } 36 | }; 37 | 38 | getMyDetails(); 39 | getMyEmail(); 40 | }, []); 41 | 42 | return ( 43 |
46 |
47 |
48 |
49 | 50 | 51 | 52 |
53 | 54 |
55 | 58 | 59 | 60 |
61 | 62 |
63 | 66 | 67 | 68 |
69 | 70 |
71 | 77 | 78 | 79 |
80 | 81 |
82 | 85 | 86 | 91 |
92 |
93 |
94 |
95 | ); 96 | } 97 | -------------------------------------------------------------------------------- /src/features/user-profile/components/UserProfileGraph.jsx: -------------------------------------------------------------------------------- 1 | import { Line } from "react-chartjs-2"; 2 | import "chart.js/auto"; // Required for automatic Chart.js version management 3 | import { api } from "@/api"; 4 | import { useState, useEffect } from "react"; 5 | 6 | export default function UserProfileGraph() { 7 | const [userGraph, setUserGraph] = useState(); 8 | 9 | const getMyGraph = async () => { 10 | try { 11 | const response = await api.get(`/play/me/graph`); 12 | setUserGraph(response.data); 13 | } catch { 14 | setUserGraph(null); 15 | } 16 | }; 17 | 18 | useEffect(() => { 19 | getMyGraph(); 20 | }, []); 21 | 22 | // Display loading message if userGraph is undefined 23 | if (userGraph === undefined) { 24 | return

; 25 | } 26 | 27 | // Check if userGraph is null, undefined, or empty 28 | if (!userGraph || userGraph.length === 0) { 29 | return

; 30 | } 31 | 32 | // Prepare data for the chart 33 | const labels = userGraph.activities.map((entry) => 34 | new Date(entry.occurredAt).toLocaleString() 35 | ); 36 | const scores = userGraph.activities.map((entry) => entry.score); 37 | 38 | // Chart data 39 | const data = { 40 | labels, 41 | datasets: [ 42 | { 43 | label: "Activity Score", 44 | data: scores, 45 | fill: false, 46 | backgroundColor: "rgba(75, 192, 192, 0.2)", 47 | borderColor: "rgba(75, 192, 192, 1)", 48 | tension: 0.1, 49 | }, 50 | ], 51 | }; 52 | 53 | // Chart options with X-axis labels hidden 54 | const options = { 55 | responsive: true, 56 | plugins: { 57 | legend: { 58 | display: false, // Show legend if you want 59 | }, 60 | title: { 61 | display: false, // Hide the title 62 | }, 63 | tooltip: { 64 | mode: "index", 65 | intersect: false, 66 | }, 67 | }, 68 | scales: { 69 | x: { 70 | display: false, // Hide the X-axis labels 71 | }, 72 | }, 73 | }; 74 | 75 | return ( 76 | <> 77 |
85 |
86 |

87 | Points Graph 88 |

89 |
90 | {/* Adjusted height values */} 91 | 96 |
97 |
98 |
99 | 100 | ); 101 | } 102 | -------------------------------------------------------------------------------- /src/features/user-profile/data/cardboards.js: -------------------------------------------------------------------------------- 1 | export const cardboards = [ 2 | { 3 | id: 1, 4 | title: "Rank", 5 | value: 55, 6 | iconClass: "text-40 icon-bar-chart-2 text-black-2", 7 | }, 8 | { 9 | id: 2, 10 | title: "Points", 11 | value: 3759, 12 | iconClass: "text-40 icon-graduation-1 text-black-2 ", 13 | }, 14 | { 15 | id: 3, 16 | title: "Total Solves", 17 | value: 186, 18 | iconClass: "text-40 icon-person-3 text-black-2 ", 19 | }, 20 | { 21 | id: 4, 22 | title: "Total Hint Usage", 23 | value: 86, 24 | iconClass: "text-40 icon-search text-black-2 ", 25 | }, 26 | ]; 27 | -------------------------------------------------------------------------------- /src/features/user-profile/data/certificate.js: -------------------------------------------------------------------------------- 1 | export const certificate = [ 2 | { 3 | id: 1, 4 | title: "Enter certificate id: ", 5 | items: ["load pdf information"], 6 | }, 7 | 8 | ]; 9 | -------------------------------------------------------------------------------- /src/features/user-profile/data/sidebarprofile.js: -------------------------------------------------------------------------------- 1 | export const sidebarprofile = [ 2 | { 3 | id: 1, 4 | href: "/play", 5 | iconClass: "fa fa-gamepad", 6 | text: "CTF Challenges", 7 | }, 8 | { 9 | id: 2, 10 | href: "/dashboard", 11 | iconClass: "fa fa-pie-chart", 12 | text: "Dashboard", 13 | }, 14 | // { 15 | // id: 3, 16 | // href: "/user-summary", 17 | // text: "Summary", 18 | // iconClass: "icon-bar-chart", 19 | // }, 20 | { 21 | id: 3, 22 | href: "/solve-overview", 23 | text: "Solve Overview", 24 | iconClass: "icon-search", 25 | }, 26 | { 27 | id: 4, 28 | href: "/hint-usage", 29 | text: "Hint Usage", 30 | iconClass: "fa fa-lightbulb", 31 | }, 32 | // { 33 | // id: 5, 34 | // href: "/certify", 35 | // text: "Certification", 36 | // iconClass: "fa fa-certificate", 37 | // }, 38 | { 39 | id: 5, 40 | href: "/settings", 41 | iconClass: "fa fa-gear", 42 | text: "Settings", 43 | }, 44 | // { 45 | // id: 7, 46 | // href: "/", 47 | // iconClass: "icon-power", 48 | // text: "Logout", 49 | // }, 50 | ]; 51 | -------------------------------------------------------------------------------- /src/features/user-profile/data/solveoverview.js: -------------------------------------------------------------------------------- 1 | export const solveoverview = [ 2 | { 3 | id: 1, 4 | imgSrc: "/assets/img/misc/2.png", 5 | challengeId: "728a8ae3-f732-4c2b-bc69-b0e049f7eefe", 6 | challengeCategory: "Web Exploitation", 7 | challengeName: "Future life.", 8 | challengePoints: "150", 9 | SolveAt: "12/18/2024, 2:43:27 PM", 10 | 11 | }, 12 | { 13 | id: 2, 14 | imgSrc: "/assets/img/misc/2.png", 15 | challengeId: "06bc3b08-0134-4745-b608-f7d3c0e6a9b5", 16 | challengeCategory: "Forensics", 17 | challengeName: "Natural success building.", 18 | challengePoints: "350", 19 | SolveAt: "12/18/2024, 2:43:28 PM", 20 | 21 | }, 22 | { 23 | id: 3, 24 | imgSrc: "/assets/img/misc/2.png", 25 | challengeId: "b95c6e3e-c0f9-4746-b424-96caf8da4793", 26 | challengeCategory: "General Skills", 27 | challengeName: "Husband side.", 28 | challengePoints: "200", 29 | SolveAt: "12/18/2024, 2:43:28 PM", 30 | 31 | }, 32 | { 33 | id: 4, 34 | imgSrc: "/assets/img/misc/2.png", 35 | challengeId: "3a517ab0-ebeb-458b-9e3b-d6ed8830e172", 36 | challengeCategory: "Cryptography", 37 | challengeName: "Ago police.", 38 | challengePoints: "450", 39 | SolveAt: "12/18/2024, 2:43:29 PM", 40 | 41 | }, 42 | { 43 | id: 5, 44 | imgSrc: "/assets/img/misc/2.png", 45 | challengeId: "ee798e2e-cdae-4043-9c61-1d70cf94144a", 46 | challengeCategory: "Stenography", 47 | challengeName: "Bad foreign rate.", 48 | challengePoints: "150", 49 | SolveAt: "12/18/2024, 2:43:30 PM", 50 | 51 | }, 52 | 53 | ]; -------------------------------------------------------------------------------- /src/features/user-profile/index.js: -------------------------------------------------------------------------------- 1 | import Charts from "./components/Charts"; 2 | import PieCharts from "./components/PieCharts"; 3 | import UserReportOld from "./components/UserReportOld"; 4 | import UserChangePassword from "./components/UserChangePassword"; 5 | import UserProfileDetails from "./components/UserProfileDetails"; 6 | import UserProfileStatsReport from "./components/UserProfileStatsReport"; 7 | import UserProfileGraph from "./components/UserProfileGraph"; 8 | import UserProfileOverview from "./components/UserProfileOverview"; 9 | 10 | import UserProfileSidebar from "./layout/UserProfileSidebar"; 11 | import UserProfileDashboard from "./layout/UserProfileDashboard"; 12 | import UserProfileSettings from "./layout/UserProfileSettings"; 13 | import UserProfileSolves from "./layout/UserProfileSolves"; 14 | import UserProfileHintUsages from "./layout/UserProfileHintUsages"; 15 | import UserProfileCertificate from "./layout/UserProfileCertificate"; 16 | 17 | // import HeaderProfile from "../../layout/headers/HeaderProfile"; 18 | // import FooterProfile from "../../layout/footers/FooterProfile"; 19 | 20 | export { 21 | UserProfileOverview, 22 | UserProfileGraph, 23 | UserProfileStatsReport, 24 | UserProfileSidebar, 25 | UserProfileDashboard, 26 | Charts, 27 | PieCharts, 28 | UserReportOld, 29 | UserProfileSolves, 30 | UserProfileHintUsages, 31 | UserProfileCertificate, 32 | UserProfileSettings, 33 | UserChangePassword, 34 | UserProfileDetails, 35 | // HeaderProfile, 36 | // FooterProfile 37 | }; 38 | -------------------------------------------------------------------------------- /src/features/user-profile/layout/UserProfileCertificate.jsx: -------------------------------------------------------------------------------- 1 | import FooterProfile from "@/layout/footers/FooterProfile"; 2 | 3 | // Unused 4 | 5 | export default function UserProfileCertificate() { 6 | const handleSubmit = (e) => { 7 | e.preventDefault(); 8 | }; 9 | return ( 10 |
11 |
12 |
13 |
14 |

15 | Get your Certificate 16 |

17 |
18 | To access your certificate, please enter your unique Certificate 19 | ID below and click “Submit.” 20 |
21 |
22 |
23 | 24 |
25 |
26 |
27 |
28 |
29 |
30 |
34 |
35 | 38 | 39 | 44 |
45 | 46 |
47 | 50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | 60 | 61 |
62 | ); 63 | } 64 | -------------------------------------------------------------------------------- /src/features/user-profile/layout/UserProfileSettings.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import UserProfileDetails from "../components/UserProfileDetails"; 3 | import UserChangePassword from "../components/UserChangePassword"; 4 | 5 | import FooterProfile from "@/layout/footers/FooterProfile"; 6 | 7 | const buttons = ["Profile", "Change Password"]; 8 | 9 | export default function UserProfileSettings() { 10 | const [activeTab, setActiveTab] = useState(1); 11 | 12 | return ( 13 |
14 |
15 |
16 |
17 |

Settings

18 |
19 | You can update your password and view your profile details here. 20 |
21 |
22 |
23 | 24 |
25 |
26 |
27 |
28 |
29 | {buttons.map((elm, i) => ( 30 | 40 | ))} 41 |
42 | 43 |
44 | 45 | 46 |
47 |
48 |
49 |
50 |
51 |
52 | 53 |
54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /src/features/user-profile/layout/UserProfileSidebar.jsx: -------------------------------------------------------------------------------- 1 | import { sidebarprofile } from "../data/sidebarprofile"; 2 | import { Link } from "react-router-dom"; 3 | import { useLocation, useNavigate } from "react-router-dom"; 4 | import { api } from "@/api"; 5 | import { useState } from "react"; 6 | import useAuth from "@/hooks/useAuth"; 7 | import { toast } from "react-toastify"; 8 | 9 | export default function Sidebar() { 10 | const { pathname } = useLocation(); 11 | const [isLoggingOut, setIsLoggingOut] = useState(false); 12 | const { setAuth } = useAuth(); 13 | const navigate = useNavigate(); 14 | 15 | const handleLogout = async () => { 16 | setIsLoggingOut(true); 17 | try { 18 | await api.post("/identity/logout"); 19 | setAuth(null); 20 | navigate("/login"); 21 | } catch (error) { 22 | toast.error("Unable to log out"); 23 | } finally { 24 | setIsLoggingOut(false); 25 | } 26 | }; 27 | 28 | return ( 29 |
30 | {sidebarprofile.map((elm, i) => ( 31 | 38 |
39 | 40 | {elm.text} 41 |
42 | 43 | ))} 44 | 45 |
46 | 53 |
54 |
55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /src/hooks/useAuth.js: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import AuthContext from "../context/AuthProvider"; 3 | 4 | // Wrapper for the AuthProvider.jsx 5 | // Reference: https://www.youtube.com/watch?v=AcYF18oGn6Y 6 | 7 | const useAuth = () => { 8 | const context = useContext(AuthContext); 9 | 10 | if (context === undefined) { 11 | throw new Error("useAuth must be used within an AuthProvider"); 12 | } 13 | 14 | return context; 15 | }; 16 | 17 | export default useAuth; 18 | -------------------------------------------------------------------------------- /src/hooks/useSignalR.js: -------------------------------------------------------------------------------- 1 | import SignalRContext from "@/context/SignalRProvider"; 2 | import { useContext } from "react"; 3 | 4 | const useSignalR = () => { 5 | const context = useContext(SignalRContext); 6 | 7 | if (context === undefined) { 8 | throw new Error("useSignalR must be used within a SignalRProvider"); 9 | } 10 | 11 | return context; 12 | }; 13 | 14 | export default useSignalR; 15 | -------------------------------------------------------------------------------- /src/index.jsx: -------------------------------------------------------------------------------- 1 | // import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App.jsx"; 4 | import { BrowserRouter } from "react-router-dom"; 5 | import { AuthProvider } from "./context/AuthProvider.jsx"; 6 | import ScrollToTop from "./components/ScrollToTop.jsx"; 7 | import { SignalRProvider } from "./context/SignalRProvider.jsx"; 8 | 9 | ReactDOM.createRoot(document.getElementById("root")).render( 10 | // 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | // 20 | ); 21 | -------------------------------------------------------------------------------- /src/layout/components/FooterLinks.jsx: -------------------------------------------------------------------------------- 1 | import { footerLinks } from "@/data/footerLinks"; 2 | import { Link } from "react-router-dom"; 3 | import PropTypes from 'prop-types'; // Import PropTypes 4 | 5 | export default function FooterLinks({ allClasses }) { 6 | return ( 7 | <> 8 | {footerLinks.map((elm, i) => ( 9 |
10 |
{elm.title}
11 |
12 | {elm.links.map((itm, index) => ( 13 | 14 | {itm.label} 15 | 16 | ))} 17 |
18 |
19 | ))} 20 | 21 | ); 22 | } 23 | 24 | // Define prop types 25 | FooterLinks.propTypes = { 26 | allClasses: PropTypes.string, // allClasses is an optional string 27 | }; 28 | -------------------------------------------------------------------------------- /src/layout/components/Links.jsx: -------------------------------------------------------------------------------- 1 | import { links } from "@/data/links"; 2 | import { Link } from "react-router-dom"; 3 | import PropTypes from 'prop-types'; 4 | 5 | export default function Links({ allClasses }) { 6 | return ( 7 | <> 8 | {links.map((link, index) => ( 9 | 14 | {link.label} 15 | 16 | ))} 17 | 18 | ); 19 | } 20 | 21 | // Define prop types 22 | Links.propTypes = { 23 | allClasses: PropTypes.string, // allClasses is an optional string 24 | }; 25 | -------------------------------------------------------------------------------- /src/layout/components/MobileFooter.jsx: -------------------------------------------------------------------------------- 1 | export default function MobileFooter() { 2 | return ( 3 |
4 | {/* Copyright Text */} 5 |
6 | © {new Date().getFullYear()} PWNEU. All Rights Reserved. 7 |
8 |
9 | New Era University 10 |
11 |
12 |
13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /src/layout/footers/Footer.jsx: -------------------------------------------------------------------------------- 1 | import Socials from "@/components/Socials"; 2 | import FooterLinks from "../components/FooterLinks"; 3 | import Links from "../components/Links"; 4 | 5 | export default function Footer() { 6 | 7 | return ( 8 |
9 |
10 |
11 |
12 |
13 |
14 | logo 15 |
16 |
17 |
18 |
19 |
20 | Follow us on social media 21 |
22 |
23 | 24 |
25 |
26 |
27 |
28 |
29 | 30 |
31 |
32 | 35 |
36 |
37 | 38 |
39 |
40 |
41 |
42 | © {new Date().getFullYear()} PWNEU. All Right Reserved. 43 |
44 |
45 | 46 |
47 |
48 |
49 |
50 | 51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /src/layout/footers/FooterProfile.jsx: -------------------------------------------------------------------------------- 1 | import Links from "../components/Links"; 2 | 3 | export default function FooterProfile() { 4 | return ( 5 |
6 |
7 |
8 |
9 |
10 |
11 | © {new Date().getFullYear()} PWNEU. All Right Reserved. 12 |
13 |
14 | {"PWNEU{$treN9Th$_4Nd_wE@Kn3$$ES}"} 15 |
16 |
17 | 18 |
19 |
20 |
21 | 22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/layout/headers/HeaderAdmin.jsx: -------------------------------------------------------------------------------- 1 | import { Navbar, Nav, Container } from "react-bootstrap"; 2 | import { useNavigate } from "react-router-dom"; 3 | import useAuth from "@/hooks/useAuth"; 4 | import { api } from "@/api"; 5 | import { toast } from "react-toastify"; 6 | 7 | export default function HeaderAdmin() { 8 | const { auth, setAuth } = useAuth(); 9 | const navigate = useNavigate(); 10 | 11 | const handleLogout = async () => { 12 | try { 13 | await api.post("/identity/logout"); 14 | setAuth(null); 15 | navigate("/"); 16 | toast.success("Logged out successfully"); 17 | } catch { 18 | toast.error("Error logging out"); 19 | } 20 | }; 21 | 22 | const isAdmin = auth?.roles?.includes("Admin"); 23 | 24 | return ( 25 | 26 | 27 | PWNEU 28 | 29 | 30 | 42 | 56 | 57 | 58 | 59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /src/pages/achievements/AchievementDetailsPage.jsx: -------------------------------------------------------------------------------- 1 | import { useParams } from "react-router-dom"; 2 | import MetaComponent from "@/components/MetaComponent"; 3 | import Preloader from "@/components/Preloader"; 4 | import Header from "@/layout/headers/Header"; 5 | import Footer from "@/layout/footers/Footer"; 6 | import { AchievementsDetails } from "@/features/achievements"; 7 | 8 | const metadata = { 9 | title: "Achievements Details || PWNEU", 10 | description: 11 | "Delve into the specifics of our key accomplishments and milestones that define our success.", 12 | }; 13 | 14 | export default function AchievementDetailsPage() { 15 | let params = useParams(); 16 | return ( 17 |
18 | 19 | 20 |
21 | 22 |
23 | 24 |
25 |
26 |
27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /src/pages/achievements/AchievementsListPage.jsx: -------------------------------------------------------------------------------- 1 | import MetaComponent from "@/components/MetaComponent"; 2 | import Preloader from "@/components/Preloader"; 3 | import Header from "@/layout/headers/Header"; 4 | import Footer from "@/layout/footers/Footer"; 5 | import { AchievementsOne } from "@/features/achievements"; 6 | 7 | const metadata = { 8 | title: "Achievements List || PWNEU", 9 | description: 10 | "Explore our milestones and accomplishments that showcase our journey and success.", 11 | }; 12 | 13 | export default function AchievementsListPage() { 14 | return ( 15 |
16 | 17 | 18 |
19 | 20 |
21 | 22 |
23 |
24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/pages/achievements/index.js: -------------------------------------------------------------------------------- 1 | import AchievementsListPage from "./AchievementsListPage"; 2 | import AchievementDetailsPage from "./AchievementDetailsPage"; 3 | 4 | export { AchievementsListPage, AchievementDetailsPage }; 5 | -------------------------------------------------------------------------------- /src/pages/admin/index.js: -------------------------------------------------------------------------------- 1 | import AccessKeysPage from "./AccessKeysPage"; 2 | import AdminPage from "./AdminPage"; 3 | import AuditsPage from "./AuditsPage"; 4 | import CategoriesPage from "./CategoriesPage"; 5 | import ChallengeDetailsAdminPage from "./ChallengeDetailsAdminPage"; 6 | import ChallengesAdminPage from "./ChallengesAdminPage"; 7 | import ConfigurationsPage from "./ConfigurationsPage"; 8 | import LeaderboardsAdminPage from "./LeaderboardsAdminPage"; 9 | import UserDetailsPage from "./UserDetailsPage"; 10 | import UsersPage from "./UsersPage"; 11 | 12 | export { 13 | AccessKeysPage, 14 | AdminPage, 15 | AuditsPage, 16 | CategoriesPage, 17 | ChallengeDetailsAdminPage, 18 | ChallengesAdminPage, 19 | ConfigurationsPage, 20 | LeaderboardsAdminPage, 21 | UserDetailsPage, 22 | UsersPage, 23 | }; 24 | -------------------------------------------------------------------------------- /src/pages/authentication/ForgotPasswordPage.jsx: -------------------------------------------------------------------------------- 1 | import Preloader from "@/components/Preloader"; 2 | import MetaComponent from "@/components/MetaComponent"; 3 | import { 4 | ForgotPassword, 5 | AuthImageMove, 6 | HeaderAuth, 7 | PasswordResetSent, 8 | } from "@/features/authentication"; 9 | import { useState } from "react"; 10 | 11 | const metadata = { 12 | title: "Forgot Password || PWNEU", 13 | description: 14 | "Reset your password to regain access to your account and continue exploring our platform.", 15 | }; 16 | 17 | export default function ForgotPasswordPage() { 18 | const [passwordResetSent, setPasswordResetSent] = useState(false); // gawing true para mag appear yung PasswordResetSent. originally false 19 | 20 | return ( 21 |
22 | 23 | 24 | 25 | 26 |
27 |
28 | 29 | {passwordResetSent ? ( 30 | 31 | ) : ( 32 | 33 | )} 34 |
35 |
36 |
37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /src/pages/authentication/LoginPage.jsx: -------------------------------------------------------------------------------- 1 | import Preloader from "@/components/Preloader"; 2 | import MetaComponent from "@/components/MetaComponent"; 3 | import { 4 | LoginForm, 5 | AuthImageMove, 6 | HeaderAuth, 7 | } from "@/features/authentication"; 8 | 9 | const metadata = { 10 | title: "Login || PWNEU", 11 | description: 12 | "Access your account to explore our platform and participate in challenges.", 13 | }; 14 | 15 | export default function LoginPage() { 16 | return ( 17 |
18 | 19 | 20 | 21 | 22 |
23 |
24 | 25 | 26 |
27 |
28 |
29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/pages/authentication/ResetPasswordPage.jsx: -------------------------------------------------------------------------------- 1 | import Preloader from "@/components/Preloader"; 2 | import MetaComponent from "@/components/MetaComponent"; 3 | import { 4 | ResetPassword, 5 | PasswordResetCompleted, 6 | AuthImageMove, 7 | HeaderAuth, 8 | } from "@/features/authentication"; 9 | import { useEffect, useState } from "react"; 10 | import { useNavigate } from "react-router-dom"; 11 | 12 | const metadata = { 13 | title: "Set New Password || PWNEU", 14 | description: 15 | "Create a new password to secure your account and gain access to our platform.", 16 | }; 17 | 18 | export default function ResetPasswordPage() { 19 | const [resetToken, setResetToken] = useState(undefined); 20 | const [email, setEmail] = useState(undefined); 21 | 22 | const [passwordResetHasCompleted, setPasswordResetHasCompleted] = 23 | useState(false); // gawing false para mag appear yung ResetPassword, true para mag appear yung PasswordResetCompleted. originally false 24 | 25 | const navigate = useNavigate(); 26 | 27 | const validateEmail = (email) => { 28 | const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; 29 | return emailRegex.test(email); 30 | }; 31 | 32 | useEffect(() => { 33 | const queryParams = new URLSearchParams(window.location.search); 34 | const emailParam = queryParams.get("email"); 35 | const resetToken = queryParams.get("resetToken"); 36 | 37 | if (emailParam && validateEmail(emailParam) && resetToken) { 38 | setEmail(emailParam); 39 | setResetToken(resetToken); 40 | } else { 41 | // If email is invalid or token is missing, navigate back to home page 42 | navigate("/"); 43 | } 44 | }, [navigate]); 45 | 46 | return ( 47 |
48 | 49 | 50 | 51 | 52 |
53 |
54 | 55 | {passwordResetHasCompleted ? ( 56 | 57 | ) : ( 58 | 63 | )} 64 |
65 |
66 |
67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /src/pages/authentication/SignupPage.jsx: -------------------------------------------------------------------------------- 1 | import Preloader from "@/components/Preloader"; 2 | import MetaComponent from "@/components/MetaComponent"; 3 | import { 4 | SignUpForm, 5 | AuthImageMove, 6 | HeaderAuth, 7 | AccountHasbeenCreated, 8 | } from "@/features/authentication"; 9 | import { useState, useEffect } from "react"; 10 | 11 | const metadata = { 12 | title: "Sign Up || PWNEU", 13 | description: 14 | "Create your account to join our community and start participating in exciting challenges.", 15 | }; 16 | 17 | export default function SignupPage() { 18 | const [hasRegistered, setHasRegistered] = useState(false); // gawing true para mag appear yung AccountHasbeenCreated. Originally false 19 | const [accessKey, setAccessKey] = useState(""); 20 | 21 | useEffect(() => { 22 | const queryParams = new URLSearchParams(window.location.search); 23 | const accessKeyParams = queryParams.get("accessKey"); 24 | 25 | if (accessKeyParams !== null) { 26 | setAccessKey(accessKeyParams); 27 | } 28 | }, []); 29 | 30 | return ( 31 |
32 | 33 | 34 | 35 | 36 |
37 |
38 | 39 | {hasRegistered ? ( 40 | 41 | ) : ( 42 | 46 | )} 47 |
48 |
49 |
50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /src/pages/authentication/VerifyEmailPage.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { api } from "@/api"; 3 | import Preloader from "@/components/Preloader"; 4 | import MetaComponent from "@/components/MetaComponent"; 5 | import { 6 | AccountHasVerified, 7 | AccountVerificationFailed, 8 | AccountVerificationLoading, 9 | AuthImageMove, 10 | HeaderAuth, 11 | } from "@/features/authentication"; 12 | import { toast } from "react-toastify"; 13 | 14 | const VERIFY_EMAIL_API = "/identity/verify"; 15 | 16 | const metadata = { 17 | title: "Verify Email || PWNEU", 18 | description: "Verify your email to join PWNEU", 19 | }; 20 | 21 | export default function VerifyEmailPage() { 22 | const [verificationStatus, setVerificationStatus] = useState("null"); // gawing "success" para mag appear yung AccountHasVerified, "failed" para mag appear yung AccountVerificationFailed. originally null 23 | const [email, setEmail] = useState(null); 24 | 25 | // comment out useEffect 26 | useEffect(() => { 27 | const queryParams = new URLSearchParams(window.location.search); 28 | const emailParam = queryParams.get("email"); 29 | const confirmationToken = queryParams.get("confirmationToken"); 30 | 31 | if (emailParam && confirmationToken) { 32 | setEmail(emailParam); 33 | verifyEmail(emailParam, confirmationToken); 34 | } else { 35 | setVerificationStatus("failed"); 36 | } 37 | }, []); 38 | 39 | const verifyEmail = async (email, token) => { 40 | try { 41 | await api.post(VERIFY_EMAIL_API, { email, confirmationToken: token }); 42 | setVerificationStatus("success"); 43 | } catch (error) { 44 | const status = error?.response?.status; 45 | 46 | if (status === 400) { 47 | toast.error(error.response.data.message || "Error verifying user"); 48 | } else if (status === 429) { 49 | toast.error( 50 | "Too many request in your IP address. Please try again later" 51 | ); 52 | } else { 53 | toast.error( 54 | "Something went wrong verifying user. Please try again later" 55 | ); 56 | } 57 | 58 | setVerificationStatus("failed"); 59 | } 60 | }; 61 | 62 | return ( 63 |
64 | 65 | 66 | 67 |
68 |
69 | 70 | {verificationStatus === "success" ? ( 71 | 72 | ) : verificationStatus === "failed" ? ( 73 | 74 | ) : ( 75 | 76 | )} 77 |
78 |
79 |
80 | ); 81 | } 82 | -------------------------------------------------------------------------------- /src/pages/authentication/index.js: -------------------------------------------------------------------------------- 1 | import ForgotPasswordPage from "./ForgotPasswordPage"; 2 | import LoginPage from "./LoginPage"; 3 | import ResetPasswordPage from "./ResetPasswordPage"; 4 | import SignupPage from "./SignupPage"; 5 | import VerifyEmailPage from "./VerifyEmailPage"; 6 | 7 | export { 8 | ForgotPasswordPage, 9 | LoginPage, 10 | ResetPasswordPage, 11 | SignupPage, 12 | VerifyEmailPage, 13 | }; 14 | -------------------------------------------------------------------------------- /src/pages/chatbot/ChatBotPage.jsx: -------------------------------------------------------------------------------- 1 | import MetaComponent from "@/components/MetaComponent"; 2 | import Preloader from "@/components/Preloader"; 3 | import Header from "@/layout/headers/Header"; 4 | import Footer from "@/layout/footers/Footer"; 5 | import { 6 | ChatHeader, 7 | ChatInterface, 8 | } from "@/features/chatbot"; 9 | 10 | const metadata = { 11 | title: "Dash AI || PWNEU", 12 | description: 13 | "Dash AI: An advanced AI chatbot designed for CTF challenges, providing hints, guidance, and interactive problem-solving to enhance your cybersecurity skills.", 14 | }; 15 | 16 | export default function ChatBotPage() { 17 | return ( 18 |
19 | 20 | 21 |
22 | 23 |
24 | 25 | 26 |
27 |
28 |
29 |
30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/pages/chatbot/index.js: -------------------------------------------------------------------------------- 1 | import ChatBotPage from "./ChatBotPage"; 2 | 3 | export { ChatBotPage }; -------------------------------------------------------------------------------- /src/pages/contact/ContactPage.jsx: -------------------------------------------------------------------------------- 1 | import MetaComponent from "@/components/MetaComponent"; 2 | import Preloader from "@/components/Preloader"; 3 | import Header from "@/layout/headers/Header"; 4 | import Footer from "@/layout/footers/Footer"; 5 | import { Contact } from "@/features/contact"; 6 | 7 | const metadata = { 8 | title: "Contact Us || PWNEU", 9 | description: 10 | "Get in touch with us for any questions, support, or feedback. We're here to help!", 11 | }; 12 | 13 | export default function ContactPage() { 14 | return ( 15 |
16 |
17 | 18 | 19 |
20 | 21 |
22 | 23 |
24 |
25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /src/pages/contact/DiscussionForumsPage.jsx: -------------------------------------------------------------------------------- 1 | import MetaComponent from "@/components/MetaComponent"; 2 | import Preloader from "@/components/Preloader"; 3 | import Header from "@/layout/headers/Header"; 4 | import Footer from "@/layout/footers/Footer"; 5 | import { DiscussionForum } from "@/features/contact"; 6 | 7 | const metadata = { 8 | title: "Discussion Forum || PWNEU", 9 | description: 10 | "A platform for students and faculty at PWNEU to engage in discussions, share knowledge, and collaborate on academic projects.", 11 | }; 12 | 13 | export default function DiscussionForumsPage() { 14 | return ( 15 |
16 | 17 | 18 |
19 | 20 |
21 | 22 |
23 |
24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/pages/contact/HelpCenterPage.jsx: -------------------------------------------------------------------------------- 1 | import MetaComponent from "@/components/MetaComponent"; 2 | import Preloader from "@/components/Preloader"; 3 | import Faq from "@/components/Faq"; 4 | import Header from "@/layout/headers/Header"; 5 | import Footer from "@/layout/footers/Footer"; 6 | 7 | const metadata = { 8 | title: "Help Center || PWNEU", 9 | description: 10 | "Find answers to your questions, explore tutorials, and get the support you need.", 11 | }; 12 | 13 | export default function HelpCenterPage() { 14 | return ( 15 |
16 | 17 | 18 |
19 | 20 |
21 | 22 |
23 |
24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/pages/contact/index.js: -------------------------------------------------------------------------------- 1 | import ContactPage from "./ContactPage"; 2 | import DiscussionForumsPage from "./DiscussionForumsPage"; 3 | import HelpCenterPage from "./HelpCenterPage"; 4 | 5 | export { ContactPage, DiscussionForumsPage, HelpCenterPage }; 6 | -------------------------------------------------------------------------------- /src/pages/main/CampusesPage.jsx: -------------------------------------------------------------------------------- 1 | import MetaComponent from "@/components/MetaComponent"; 2 | import Preloader from "@/components/Preloader"; 3 | import Header from "@/layout/headers/Header"; 4 | import CampusesBranch from "@/features/campuses/components/CampusesBranch"; 5 | import Footer from "@/layout/footers/Footer"; 6 | 7 | const metadata = { 8 | title: "University || PWNEU", 9 | description: 10 | "Get in touch with us for any questions, support, or feedback. We're here to help!", 11 | }; 12 | 13 | export default function CampusesPage() { 14 | return ( 15 |
16 | 17 | 18 |
19 | 20 |
21 | 22 |
23 |
24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/pages/main/HomePage.jsx: -------------------------------------------------------------------------------- 1 | import Footer from "@/layout/footers/Footer"; 2 | import Preloader from "@/components/Preloader"; 3 | import MetaComponent from "@/components/MetaComponent"; 4 | import Header from "@/layout/headers/Header"; 5 | 6 | import { 7 | HomeHero, 8 | LearningFeatures, 9 | FindLearningPath, 10 | CampusesTag, 11 | WhyPWNEU, 12 | Features, 13 | TopCategories, 14 | Testimonials, 15 | // Achievements 16 | } from "@/features/home"; 17 | 18 | const metadata = { 19 | title: "Welcome to Our Platform || PWNEU", 20 | description: 21 | "Explore our features, participate in challenges, and enjoy your experience on our platform.", 22 | }; 23 | 24 | export default function HomePage() { 25 | return ( 26 | <> 27 | 28 | 29 |
30 | 31 |
32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | {/* */} 41 |
42 |
43 | 44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /src/pages/main/MissionVisionPage.jsx: -------------------------------------------------------------------------------- 1 | import MetaComponent from "@/components/MetaComponent"; 2 | import Preloader from "@/components/Preloader"; 3 | import Header from "@/layout/headers/Header"; 4 | // import Mission from "@/features/about/components/Mission"; 5 | import MissionVision from "@/features/about/components/MissionVision"; 6 | import Footer from "@/layout/footers/Footer"; 7 | 8 | const metadata = { 9 | title: "Mission and Vision || PWNEU", 10 | description: 11 | "Discover our core values, our mission to drive innovation, and our vision for the future.", 12 | }; 13 | 14 | export default function MissionVisionPage() { 15 | return ( 16 |
17 | 18 | 19 |
20 | 21 |
22 | {/* */} 23 | 24 |
25 |
26 |
27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /src/pages/main/NotFoundPage.jsx: -------------------------------------------------------------------------------- 1 | import MetaComponent from "@/components/MetaComponent"; 2 | import Preloader from "@/components/Preloader"; 3 | import Header from "@/layout/headers/Header"; 4 | import Footer from "@/layout/footers/Footer"; 5 | import { NotFound } from "@/features/contact"; 6 | 7 | const metadata = { 8 | title: "Not Found || PWNEU", 9 | description: "The page you're looking for could not be found. Please check the URL or go back to the homepage.", 10 | }; 11 | 12 | export default function NotFoundPage() { 13 | return ( 14 |
15 | 16 | 17 |
18 | 19 |
20 | 21 |
22 |
23 |
24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /src/pages/main/OurStoryPage.jsx: -------------------------------------------------------------------------------- 1 | import MetaComponent from "@/components/MetaComponent"; 2 | import Preloader from "@/components/Preloader"; 3 | import Header from "@/layout/headers/Header"; 4 | import OurStory from "@/features/about/components/OurStory"; 5 | import HeroStory from "@/features/about/components/HeroStory"; 6 | import OurApproach from "@/features/about/components/OurApproach"; 7 | import Footer from "@/layout/footers/Footer"; 8 | 9 | const metadata = { 10 | title: "Our Story || PWNEU", 11 | description: 12 | "Learn about our journey, from humble beginnings to where we are today, and the values that drive us.", 13 | }; 14 | 15 | export default function OurStoryPage() { 16 | return ( 17 |
18 | 19 | 20 |
21 | 22 |
23 | 24 | 25 | 26 |
27 |
28 |
29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/pages/main/WhoWeArePage.jsx: -------------------------------------------------------------------------------- 1 | import MetaComponent from "@/components/MetaComponent"; 2 | import Preloader from "@/components/Preloader"; 3 | import Header from "@/layout/headers/Header"; 4 | import Footer from "@/layout/footers/Footer"; 5 | import { DevFounder} from "@/features/about"; 6 | 7 | const metadata = { 8 | title: "Who We Are || PWNEU", 9 | description: 10 | "Get to know PWNEU—our team, our values, and what makes us unique in the world of competitive challenges.", 11 | }; 12 | 13 | export default function WhoWeArePage() { 14 | return ( 15 |
16 | 17 | 18 |
19 | 20 |
21 | 22 |
23 |
24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/pages/main/index.js: -------------------------------------------------------------------------------- 1 | import CampusesPage from "./CampusesPage"; 2 | import HomePage from "./HomePage"; 3 | import MissionVisionPage from "./MissionVisionPage"; 4 | import NotFoundPage from "./NotFoundPage"; 5 | import OurStoryPage from "./OurStoryPage"; 6 | import WhoWeArePage from "./WhoWeArePage"; 7 | 8 | export { 9 | CampusesPage, 10 | HomePage, 11 | MissionVisionPage, 12 | NotFoundPage, 13 | OurStoryPage, 14 | WhoWeArePage, 15 | }; 16 | -------------------------------------------------------------------------------- /src/pages/ownership/PrivacyPolicyPage.jsx: -------------------------------------------------------------------------------- 1 | import MetaComponent from "@/components/MetaComponent"; 2 | import Preloader from "@/components/Preloader"; 3 | import Header from "@/layout/headers/Header"; 4 | import Footer from "@/layout/footers/Footer"; 5 | import { PrivacyPolicyTabs } from "@/features/ownership"; 6 | 7 | const metadata = { 8 | title: "Privacy Policy || PWNEU", 9 | description: 10 | "This Privacy Policy outlines how PWNEU collects, uses, and protects your personal information. By using our services, you agree to the collection and use of information in accordance with this policy.", 11 | }; 12 | 13 | export default function PrivacyPolicyPage() { 14 | return ( 15 |
16 | 17 | 18 |
19 | 20 |
21 | 22 |
23 |
24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/pages/ownership/TermsAndConditionsPage.jsx: -------------------------------------------------------------------------------- 1 | import MetaComponent from "@/components/MetaComponent"; 2 | import Preloader from "@/components/Preloader"; 3 | import Header from "@/layout/headers/Header"; 4 | import Footer from "@/layout/footers/Footer"; 5 | import { TermsAndConditionTabs } from "@/features/ownership"; 6 | 7 | const metadata = { 8 | title: "Terms and Conditions || PWNEU", 9 | description: 10 | "These Terms and Conditions govern your use of the PWNEU website and services. By accessing or using our site, you agree to comply with these terms. Please read them carefully.", 11 | }; 12 | 13 | export default function TermsAndConditionsPage() { 14 | return ( 15 |
16 | 17 | 18 |
19 | 20 |
21 | 22 |
23 |
24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/pages/ownership/index.js: -------------------------------------------------------------------------------- 1 | import PrivacyPolicyPage from "./PrivacyPolicyPage"; 2 | import TermsAndConditionsPage from "./TermsAndConditionsPage"; 3 | 4 | export { PrivacyPolicyPage, TermsAndConditionsPage }; 5 | -------------------------------------------------------------------------------- /src/pages/play/ChallengeDetailsPage.jsx: -------------------------------------------------------------------------------- 1 | import { useParams } from "react-router-dom"; 2 | import Preloader from "@/components/Preloader"; 3 | import MetaComponent from "@/components/MetaComponent"; 4 | import Header from "@/layout/headers/Header"; 5 | import Footer from "@/layout/footers/Footer"; 6 | // import { ChallengeDetailsOld } from "@/features/challengeDetails"; 7 | import { ChallengeDetails } from "@/features/challengeDetails"; 8 | 9 | const metadata = { 10 | title: "Challenge Details || PWNEU", 11 | description: 12 | "This challenge is part of the PWNEU competition, focusing on advanced problem-solving and technical expertise in cybersecurity and related domains.", 13 | }; 14 | 15 | export default function ChallengeDetailsPage() { 16 | let params = useParams(); 17 | 18 | return ( 19 |
20 | 21 | ; 22 |
23 |
24 | {/* */} 25 | 26 |
27 |
28 |
29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/pages/play/ChallengesPage.jsx: -------------------------------------------------------------------------------- 1 | import MetaComponent from "@/components/MetaComponent"; 2 | import Preloader from "@/components/Preloader"; 3 | import Header from "@/layout/headers/Header"; 4 | import ChallengesHeader from "@/features/challenges/layout/ChallengesHeader"; 5 | import ChallengesContainer from "@/features/challenges/layout/ChallengesContainer"; 6 | import Footer from "@/layout/footers/Footer"; 7 | 8 | const metadata = { 9 | title: "Play || PWNEU", 10 | description: 11 | "Join the competition, test your skills, and climb the leaderboard in exciting challenges.", 12 | }; 13 | 14 | export default function ChallengesPage() { 15 | return ( 16 |
17 | 18 | 19 |
20 | 21 |
22 | 23 | 24 |
25 |
26 |
27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /src/pages/play/LeaderboardsPage.jsx: -------------------------------------------------------------------------------- 1 | import Preloader from "@/components/Preloader"; 2 | import MetaComponent from "@/components/MetaComponent"; 3 | import Header from "@/layout/headers/Header"; 4 | import Footer from "@/layout/footers/Footer"; 5 | import { useState, useEffect, useLayoutEffect } from "react"; 6 | import useAuth from "@/hooks/useAuth"; 7 | import { toast } from "react-toastify"; 8 | import { 9 | LeaderboardGraph, 10 | LeaderboardsLoading, 11 | NoLeaderboards, 12 | UserRanks, 13 | } from "@/features/leaderboards"; 14 | import LeaderboardOverview from "@/features/leaderboards/layout/LeaderboardOverview"; 15 | import { api } from "@/api"; 16 | 17 | const metadata = { 18 | title: "Leaderboards", 19 | description: "PWNEU Leaderboards", 20 | }; 21 | 22 | export default function LeaderboardsPage() { 23 | useLayoutEffect(() => { 24 | const link = document.createElement("link"); 25 | link.rel = "stylesheet"; 26 | link.href = "/assets/css/leaderboards.css"; 27 | document.head.appendChild(link); 28 | 29 | return () => { 30 | document.head.removeChild(link); 31 | }; 32 | }, []); 33 | 34 | const [leaderboards, setLeaderboards] = useState(); 35 | 36 | const { auth } = useAuth(); 37 | const isManager = auth?.roles?.includes("Manager"); 38 | 39 | const getLeaderboards = async () => { 40 | const queryParams = new URLSearchParams(window.location.search); 41 | const countParams = queryParams.get("count"); 42 | 43 | try { 44 | const response = await api.get("/play/leaderboards", { 45 | params: countParams ? { count: countParams } : {}, 46 | }); 47 | setLeaderboards(response.data); 48 | } catch { 49 | toast.error("Something went wrong getting leaderboards"); 50 | setLeaderboards([]); 51 | } 52 | }; 53 | 54 | useEffect(() => { 55 | getLeaderboards(); 56 | }, []); 57 | 58 | return ( 59 |
60 | 61 | 62 |
63 | 64 |
65 | {leaderboards ? ( 66 | leaderboards.userRanks && leaderboards.userRanks.length > 0 ? ( 67 | <> 68 | 69 | 75 | 76 | ) : ( 77 |
78 | 79 |
80 | ) 81 | ) : ( 82 |
83 | 84 |
85 | )} 86 |
87 |
88 |
89 | ); 90 | } 91 | -------------------------------------------------------------------------------- /src/pages/play/index.js: -------------------------------------------------------------------------------- 1 | import ChallengeDetailsPage from "./ChallengeDetailsPage"; 2 | import ChallengesPage from "./ChallengesPage"; 3 | import LeaderboardsPage from "./LeaderboardsPage"; 4 | 5 | export { ChallengeDetailsPage, ChallengesPage, LeaderboardsPage }; 6 | -------------------------------------------------------------------------------- /src/pages/profile/CertifyPage.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { api } from "@/api"; 3 | import { toast } from "react-toastify"; 4 | 5 | export default function CertifyPage() { 6 | const [certificationStatus, setCertificationStatus] = useState(null); 7 | 8 | useEffect(() => { 9 | const generateCertificate = async () => { 10 | try { 11 | const checkResponse = await api.get("/identity/me/certificate/check"); 12 | 13 | if (checkResponse.data === "WithoutCertificate") { 14 | setCertificationStatus("failed"); 15 | toast.error("Sorry! You haven't received a certificate"); 16 | return; 17 | } 18 | 19 | if (checkResponse.data === "NotAllowed") { 20 | setCertificationStatus("failed"); 21 | toast.error("Sorry! Not allowed to receive a certificate"); 22 | return; 23 | } 24 | 25 | setCertificationStatus("generating"); 26 | const response = await api.get("/identity/me/certificate", { 27 | responseType: "blob", 28 | }); 29 | 30 | const url = window.URL.createObjectURL(new Blob([response.data])); 31 | const link = document.createElement("a"); 32 | link.href = url; 33 | link.setAttribute("download", "pwneu-certificate.pdf"); 34 | document.body.appendChild(link); 35 | link.click(); 36 | link.remove(); 37 | window.URL.revokeObjectURL(url); 38 | setCertificationStatus("success"); 39 | } catch (error) { 40 | const status = error?.response?.status; 41 | 42 | if (status === 400) { 43 | toast.error(error.response.data.message); 44 | } else if (status === 429) { 45 | toast.warn("Slow down on generating certificate!"); 46 | } else { 47 | toast.error( 48 | "Something went wrong getting user certificate. Please try again later" 49 | ); 50 | } 51 | 52 | setCertificationStatus("failed"); 53 | } 54 | }; 55 | 56 | generateCertificate(); 57 | }, []); 58 | 59 | return ( 60 | <> 61 | {certificationStatus === "success" ? ( 62 |
Success
63 | ) : certificationStatus === "failed" ? ( 64 |
Failed
65 | ) : certificationStatus === "generating" ? ( 66 |
Generating...
67 | ) : ( 68 |
Downloading...
69 | )} 70 | 71 | ); 72 | } 73 | -------------------------------------------------------------------------------- /src/pages/profile/UserProfilePage.jsx: -------------------------------------------------------------------------------- 1 | import Preloader from "@/components/Preloader"; 2 | import MetaComponent from "@/components/MetaComponent"; 3 | import Header from "@/layout/headers/Header"; 4 | import Footer from "@/layout/footers/Footer"; 5 | import { 6 | UserProfileOld, 7 | // UserOverview, 8 | UserProfileEvaluationOld, 9 | UserProfileGraphOld, 10 | UserProfileSolvesOld, 11 | UserProfileHintUsagesOld, 12 | } from "@/features/profile"; 13 | import { useState } from "react"; 14 | 15 | const metadata = { 16 | title: "", 17 | description: "", 18 | }; 19 | 20 | export default function UserProfilePage() { 21 | const [userDetails, setUserDetails] = useState(); 22 | const [totalSolveCount, setTotalSolveCount] = useState(0); 23 | const [totalHintUsagesCount, setTotalHintUsagesCount] = useState(0); 24 | 25 | return ( 26 |
27 | 28 | 29 |
30 | 31 |
32 | {/* */} 33 | 39 | 40 | 41 | 45 | 49 |
50 |
51 |
52 | ); 53 | } 54 | -------------------------------------------------------------------------------- /src/pages/profile/index.js: -------------------------------------------------------------------------------- 1 | import CertifyPage from "./CertifyPage"; 2 | import UserProfilePage from "./UserProfilePage"; 3 | 4 | export { CertifyPage, UserProfilePage }; 5 | -------------------------------------------------------------------------------- /src/pages/profile/readme.txt: -------------------------------------------------------------------------------- 1 | This profile folder is deprecated, use user-profile -------------------------------------------------------------------------------- /src/pages/user-profile/UserCertifyPage.jsx: -------------------------------------------------------------------------------- 1 | import MetaComponent from "@/components/MetaComponent"; 2 | import HeaderProfile from "@/layout/headers/HeaderProfile"; 3 | import UserProfileSidebar from "@/features/user-profile/layout/UserProfileSidebar"; 4 | 5 | import UserProfileCertificate from "@/features/user-profile/layout/UserProfileCertificate"; 6 | 7 | const metadata = { 8 | title: "User Certificate || PWNEU", 9 | description: 10 | "Detailed information about the certifications achieved by the user.", 11 | }; 12 | 13 | export default function UserCertifyPage() { 14 | return ( 15 |
16 | 17 |
18 | 19 |
20 |
24 |
25 | 26 |
27 | 28 |
29 |
30 |
31 |
32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/pages/user-profile/UserDashboardPage.jsx: -------------------------------------------------------------------------------- 1 | import MetaComponent from "@/components/MetaComponent"; 2 | import HeaderProfile from "@/layout/headers/HeaderProfile"; 3 | import UserProfileSidebar from "@/features/user-profile/layout/UserProfileSidebar"; 4 | import UserProfileDashboard from "@/features/user-profile/layout/UserProfileDashboard"; 5 | 6 | const metadata = { 7 | title: "Profile || PWNEU", 8 | description: 9 | "Comprehensive overview of the user's profile, including personal details and activity.", 10 | }; 11 | 12 | export default function UserDashboardPage() { 13 | return ( 14 |
15 | 16 |
17 | 18 |
19 |
23 |
24 | 25 |
26 | 27 |
28 |
29 |
30 |
31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /src/pages/user-profile/UserHintUsagePage.jsx: -------------------------------------------------------------------------------- 1 | import MetaComponent from "@/components/MetaComponent"; 2 | import HeaderProfile from "@/layout/headers/HeaderProfile"; 3 | import UserProfileSidebar from "@/features/user-profile/layout/UserProfileSidebar"; 4 | import UserProfileHintUsages from "@/features/user-profile/layout/UserProfileHintUsages"; 5 | 6 | const metadata = { 7 | title: "Hints activity || PWNEU", 8 | description: 9 | "Insights into the user's hint usage, including frequency and effectiveness.", 10 | }; 11 | export default function UserHintUsagePage() { 12 | return ( 13 |
14 | 15 |
16 | 17 |
18 |
22 |
23 | 24 |
25 | 26 |
27 |
28 |
29 |
30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/pages/user-profile/UserSettingsPage.jsx: -------------------------------------------------------------------------------- 1 | import MetaComponent from "@/components/MetaComponent"; 2 | import HeaderProfile from "@/layout/headers/HeaderProfile"; 3 | import UserProfileSidebar from "@/features/user-profile/layout/UserProfileSidebar"; 4 | 5 | import UserProfileSettings from "@/features/user-profile/layout/UserProfileSettings"; 6 | 7 | const metadata = { 8 | title: "Settings || PWNEU", 9 | description: 10 | "Configuration options for personalizing the user's account and application preferences.", 11 | }; 12 | export default function UserSettingsPage() { 13 | return ( 14 |
15 | 16 |
17 | 18 |
19 |
23 |
24 | 25 |
26 | 27 |
28 |
29 |
30 |
31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /src/pages/user-profile/UserSolveOverviewPage.jsx: -------------------------------------------------------------------------------- 1 | import MetaComponent from "@/components/MetaComponent"; 2 | import HeaderProfile from "@/layout/headers/HeaderProfile"; 3 | import UserProfileSidebar from "@/features/user-profile/layout/UserProfileSidebar"; 4 | import UserProfileSolves from "@/features/user-profile/layout/UserProfileSolves"; 5 | 6 | const metadata = { 7 | title: "Solve Overview || PWNEU", 8 | description: 9 | "In-depth analysis of tasks and challenges successfully completed by the user.", 10 | }; 11 | export default function UserSolveOverviewPage() { 12 | return ( 13 |
14 | 15 |
16 | 17 |
18 |
22 |
23 | 24 |
25 | 26 |
27 |
28 |
29 |
30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/pages/user-profile/UserSummaryPage.jsx: -------------------------------------------------------------------------------- 1 | import MetaComponent from "@/components/MetaComponent"; 2 | import HeaderProfile from "@/layout/headers/HeaderProfile"; 3 | import UserProfileSidebar from "@/features/user-profile/layout/UserProfileSidebar"; 4 | import UserReportOld from "@/features/user-profile/components/UserReportOld"; 5 | 6 | const metadata = { 7 | title: 8 | "Summary || PWNEU", 9 | description: 10 | "A concise yet detailed summary of the user's overall activity and performance.", 11 | }; 12 | 13 | export default function UserSummaryPage() { 14 | return ( 15 |
16 | 17 |
18 | 19 | 20 |
21 |
25 |
26 | 27 |
28 | 29 |
30 |
31 |
32 |
33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/pages/user-profile/index.js: -------------------------------------------------------------------------------- 1 | import UserDashboardPage from "./UserDashboardPage"; 2 | import UserSummaryPage from "./UserSummaryPage"; 3 | import UserSolveOverviewPage from "./UserSolveOverviewPage"; 4 | import UserHintUsagePage from "./UserHintUsagePage"; 5 | import UserCertifyPage from "./UserCertifyPage"; 6 | import UserSettingsPage from "./UserSettingsPage" 7 | 8 | export { 9 | UserDashboardPage, 10 | UserSummaryPage, 11 | UserSolveOverviewPage, 12 | UserHintUsagePage, 13 | UserCertifyPage, 14 | UserSettingsPage, 15 | }; 16 | -------------------------------------------------------------------------------- /src/styles/index.jsx: -------------------------------------------------------------------------------- 1 | import ShapeRendering from "./shape-rendering"; 2 | 3 | export { ShapeRendering }; 4 | -------------------------------------------------------------------------------- /src/styles/shape-rendering.jsx: -------------------------------------------------------------------------------- 1 | 2 | const ShapeRendering = () => { 3 | return ( 4 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | ); 26 | }; 27 | 28 | export default ShapeRendering; 29 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "rewrites": [ 3 | { 4 | "source": "/(.*)", 5 | "destination": "/" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | import path from "path"; 4 | import { fileURLToPath } from "url"; 5 | 6 | const __filename = fileURLToPath(import.meta.url); 7 | const __dirname = path.dirname(__filename); 8 | 9 | import dotenv from 'dotenv'; 10 | dotenv.config(); 11 | 12 | // eslint-disable-next-line no-undef 13 | const API_TARGET = process.env.VITE_API_TARGET || "http://localhost:37100"; 14 | 15 | export default defineConfig({ 16 | plugins: [react()], 17 | server: { 18 | port: 37001, 19 | proxy: { 20 | "/api": { 21 | target: API_TARGET, 22 | changeOrigin: true, 23 | ws: true, 24 | secure: false, 25 | rewrite: (path) => path.replace(/^\/api/, ""), 26 | }, 27 | }, 28 | }, 29 | resolve: { 30 | alias: { 31 | "@": path.resolve(__dirname, "./src"), 32 | }, 33 | }, 34 | }); --------------------------------------------------------------------------------