├── .dockerignore ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug-report.md │ ├── data-issue-report.md │ └── feature-request.md ├── auto-label.json ├── pull_request_template.md └── workflows │ ├── deploy-bumblebee.yaml │ ├── deploy-sloths.yaml │ ├── deploy.yaml │ ├── nextjs_bundle_analysis.yml.disabled │ ├── pull_request.yml │ ├── pull_request_close.yml │ ├── renovate.yml │ └── test_report.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── client ├── .dockerignore ├── .env.example ├── Dockerfile ├── Dockerfile.lambda ├── eslint.config.mjs ├── jest.config.mjs ├── lambda │ ├── app.ts │ └── event.json ├── next-env.d.ts ├── next.config.mjs ├── next.config.prod.mjs ├── package.json ├── playwright.config.ts ├── public │ └── static │ │ ├── empty.txt │ │ ├── images │ │ ├── favicon.ico │ │ ├── im-fine.svg │ │ ├── job │ │ │ └── dot.png │ │ ├── logo-rsschool.svg │ │ ├── logo-rsschool2.png │ │ ├── logo-rsschool3.png │ │ ├── logo_rs.png │ │ ├── rs-hero.png │ │ └── united-kingdom.png │ │ └── svg │ │ ├── badges │ │ ├── Congratulations.svg │ │ ├── Contributor.svg │ │ ├── Coordinator.svg │ │ ├── ExpertHelp.svg │ │ ├── GoodJob.svg │ │ ├── GreatSpeaker.svg │ │ ├── HelpingHandSloth.svg │ │ ├── Hero.svg │ │ ├── JobOffer.svg │ │ ├── JuryTeam.svg │ │ ├── Mentor.svg │ │ ├── OutstandingWork.svg │ │ ├── RSActivist.svg │ │ ├── ThankYou.svg │ │ ├── Thanks.svg │ │ └── TopPerformer.svg │ │ ├── disciplines │ │ ├── android-archived.svg │ │ ├── android.svg │ │ ├── angular-archived.svg │ │ ├── angular.svg │ │ ├── ios-archived.svg │ │ ├── ios.svg │ │ ├── javascript-archived.svg │ │ ├── javascript.svg │ │ ├── machine-learning-archived.svg │ │ ├── machine-learning.svg │ │ ├── nodejs-archived.svg │ │ ├── nodejs-aws-archived.svg │ │ ├── nodejs-aws.svg │ │ ├── nodejs.svg │ │ ├── reactjs-archived.svg │ │ └── reactjs.svg │ │ ├── err.svg │ │ ├── jobs │ │ ├── logo-footer-github.svg │ │ ├── logo-footer-rs.svg │ │ ├── rs-github.svg │ │ ├── rs-jobs-logo.svg │ │ ├── rs-logo-big.svg │ │ └── rs-super-sloth.svg │ │ ├── logo-epam.svg │ │ ├── logo-github.svg │ │ ├── logo-rs.svg │ │ ├── master-yoda.svg │ │ ├── sloths │ │ ├── Expert.svg │ │ ├── Thanks.svg │ │ ├── mentor.svg │ │ └── students.svg │ │ ├── solidarity-Ukraine.svg │ │ ├── united-kingdom.svg │ │ └── wanted-mentors.svg ├── specs │ └── smoke.spec.ts ├── src │ ├── __mocks__ │ │ └── axios.js │ ├── __tests__ │ │ ├── ProfilePage.test.tsx │ │ └── __snapshots__ │ │ │ └── ProfilePage.test.tsx.snap │ ├── api │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── .openapi-generator-ignore │ │ ├── .openapi-generator │ │ │ ├── FILES │ │ │ └── VERSION │ │ ├── api.ts │ │ ├── base.ts │ │ ├── common.ts │ │ ├── configuration.ts │ │ ├── git_push.sh │ │ └── index.ts │ ├── components │ │ ├── Analytics.tsx │ │ ├── CommentModal.tsx │ │ ├── CopyToClipboardButton.tsx │ │ ├── CountBadge │ │ │ ├── CountBadge.tsx │ │ │ └── index.tsx │ │ ├── CoursePageLayout.tsx │ │ ├── Footer │ │ │ ├── Donation.tsx │ │ │ ├── Feedback.tsx │ │ │ ├── FooterLayout.tsx │ │ │ ├── Help.tsx │ │ │ ├── Menu.tsx │ │ │ ├── SocialNetworks.tsx │ │ │ └── index.tsx │ │ ├── Forms │ │ │ ├── CommentInput.tsx │ │ │ ├── CourseTaskSelect.tsx │ │ │ ├── GdprCheckbox.tsx │ │ │ ├── Heroes │ │ │ │ └── index.tsx │ │ │ ├── LocationSelect.tsx │ │ │ ├── MarkdownInput.tsx │ │ │ ├── ModalForm.tsx │ │ │ ├── ModalSubmitForm.test.tsx │ │ │ ├── ModalSubmitForm.tsx │ │ │ ├── PreparedComment.tsx │ │ │ ├── ScoreInput.tsx │ │ │ ├── __tests__ │ │ │ │ ├── CourseTaskSelect.test.tsx │ │ │ │ └── __snapshots__ │ │ │ │ │ └── CourseTaskSelect.test.tsx.snap │ │ │ └── index.ts │ │ ├── GithubAvatar.tsx │ │ ├── GithubUserLink.tsx │ │ ├── Header.tsx │ │ ├── Heroes │ │ │ ├── HeroesCountBadge.tsx │ │ │ ├── HeroesRadarTab.tsx │ │ │ └── HeroesRadarTable.tsx │ │ ├── Icons │ │ │ ├── CourseIcon.tsx │ │ │ ├── DeadlineIcon.tsx │ │ │ ├── DiscordFilled.tsx │ │ │ ├── DiscordOutlined.tsx │ │ │ ├── GitHubLogoIcon.tsx │ │ │ ├── HealthMask.tsx │ │ │ ├── LinkedInIcon.tsx │ │ │ ├── RSLogoIcon.tsx │ │ │ ├── ScoreIcon.tsx │ │ │ ├── TelegramIcon.tsx │ │ │ └── index.tsx │ │ ├── LoadingScreen.tsx │ │ ├── MentorOptions.tsx │ │ ├── MentorSearch.tsx │ │ ├── PageLayout.tsx │ │ ├── PersonSelect.tsx │ │ ├── Profile │ │ │ ├── AboutCard.tsx │ │ │ ├── CommonCard.tsx │ │ │ ├── CommonCardWithSettingsModal.tsx │ │ │ ├── ContactsCard.tsx │ │ │ ├── ContactsCardForm.tsx │ │ │ ├── CoreJsIviewsCard.tsx │ │ │ ├── CoreJsIviewsModal.tsx │ │ │ ├── DiscordCard.tsx │ │ │ ├── EducationCard.tsx │ │ │ ├── EmailConfirmation.tsx │ │ │ ├── LanguagesCard.tsx │ │ │ ├── LegacyScreeningFeedback.tsx │ │ │ ├── MainCard.tsx │ │ │ ├── MentorStatsCard.tsx │ │ │ ├── MentorStatsModal.tsx │ │ │ ├── ObfuscateConfirmationModal.tsx │ │ │ ├── PreScreeningIviewCard.tsx │ │ │ ├── PreScreeningIviewModal.tsx │ │ │ ├── PrescreeningFeedback.tsx │ │ │ ├── ProfileSettingsModal.tsx │ │ │ ├── PublicFeedbackCard.tsx │ │ │ ├── PublicFeedbackModal.tsx │ │ │ ├── StudentStatsCard.tsx │ │ │ ├── StudentStatsModal.tsx │ │ │ └── __test__ │ │ │ │ ├── AboutCard.test.tsx │ │ │ │ ├── CommonCard.test.tsx │ │ │ │ ├── CommonCardWithSettingsModal.test.tsx │ │ │ │ ├── ContactsCard.test.tsx │ │ │ │ ├── ContactsCardForm.test.tsx │ │ │ │ ├── CoreJsIviewsCard.test.tsx │ │ │ │ ├── CoreJsIviewsModal.test.tsx │ │ │ │ ├── EducationCard.test.tsx │ │ │ │ ├── MainCard.test.tsx │ │ │ │ ├── MentorStatsCard.test.tsx │ │ │ │ ├── MentorStatsModal.test.tsx │ │ │ │ ├── PreScreeningIviewCard.test.tsx │ │ │ │ ├── PreScreeningIviewModal.test.tsx │ │ │ │ ├── ProfileSettingsModal.test.tsx │ │ │ │ ├── PublicFeedbackCard.test.tsx │ │ │ │ ├── PublicFeedbackModal.test.tsx │ │ │ │ ├── StudentStatsCard.test.tsx │ │ │ │ ├── StudentStatsModal.test.tsx │ │ │ │ └── __snapshots__ │ │ │ │ ├── AboutCard.test.tsx.snap │ │ │ │ ├── CommonCard.test.tsx.snap │ │ │ │ ├── CommonCardWithSettingsModal.test.tsx.snap │ │ │ │ ├── ContactsCard.test.tsx.snap │ │ │ │ ├── ContactsCardForm.test.tsx.snap │ │ │ │ ├── CoreJsIviewsCard.test.tsx.snap │ │ │ │ ├── CoreJsIviewsModal.test.tsx.snap │ │ │ │ ├── EducationCard.test.tsx.snap │ │ │ │ ├── MainCard.test.tsx.snap │ │ │ │ ├── MentorStatsCard.test.tsx.snap │ │ │ │ ├── MentorStatsModal.test.tsx.snap │ │ │ │ ├── PreScreeningIviewCard.test.tsx.snap │ │ │ │ ├── PreScreeningIviewModal.test.tsx.snap │ │ │ │ ├── ProfileSettingsModal.test.tsx.snap │ │ │ │ ├── PublicFeedbackCard.test.tsx.snap │ │ │ │ ├── PublicFeedbackModal.test.tsx.snap │ │ │ │ ├── StudentStatsCard.test.tsx.snap │ │ │ │ └── StudentStatsModal.test.tsx.snap │ │ ├── Rating.tsx │ │ ├── RegistrationPageLayout.tsx │ │ ├── ScoreCard.tsx │ │ ├── ScoreSelector.tsx │ │ ├── SelectLanguages.tsx │ │ ├── Sider │ │ │ ├── AdminSider.test.tsx │ │ │ ├── AdminSider.tsx │ │ │ └── data │ │ │ │ └── menuItems.tsx │ │ ├── SlothImage.tsx │ │ ├── SolidarityUkraine.tsx │ │ ├── Student │ │ │ ├── AssignStudentModal.tsx │ │ │ ├── DashboardDetails.tsx │ │ │ └── index.ts │ │ ├── StudentDiscord.tsx │ │ ├── StudentMentorModal.tsx │ │ ├── StudentSearch.tsx │ │ ├── Table │ │ │ ├── PersonCell.tsx │ │ │ ├── columns.tsx │ │ │ ├── index.ts │ │ │ ├── renderers.tsx │ │ │ └── sorters.ts │ │ ├── TabsWithCounter │ │ │ └── renderers.tsx │ │ ├── Timer.tsx │ │ ├── TooltipedButton.tsx │ │ ├── UserSearch.tsx │ │ ├── Warning │ │ │ └── index.tsx │ │ ├── WelcomeCard.tsx │ │ ├── __tests__ │ │ │ └── StudenDiscrod.test.tsx │ │ ├── common │ │ │ └── CustomPopconfirm.tsx │ │ ├── useLoading.tsx │ │ ├── withGoogleMaps.tsx │ │ └── withSession.tsx │ ├── configs │ │ ├── cdn.ts │ │ ├── course-icons.ts │ │ ├── discord-integration.ts │ │ ├── gcp.ts │ │ ├── heroes-badges.ts │ │ ├── registry.ts │ │ └── timezones.ts │ ├── data │ │ ├── english.ts │ │ ├── eventTypes.ts │ │ ├── index.ts │ │ ├── interviews │ │ │ ├── angular.ts │ │ │ ├── corejs1.ts │ │ │ ├── corejs2.ts │ │ │ ├── index.ts │ │ │ ├── react.ts │ │ │ ├── shortTrackJavaScript.ts │ │ │ ├── shortTrackPerformance.ts │ │ │ ├── shortTrackScreening.ts │ │ │ ├── shortTrackTypeScript.ts │ │ │ ├── technical-screening.tsx │ │ │ └── types.ts │ │ ├── skills.ts │ │ ├── taskTypes.ts │ │ └── tshirts.ts │ ├── domain │ │ ├── course.test.ts │ │ ├── course.ts │ │ ├── interview.test.ts │ │ ├── interview.tsx │ │ ├── user.test.tsx │ │ └── user.ts │ ├── favicon.ico │ ├── hooks │ │ ├── index.ts │ │ └── useModal │ │ │ ├── useModalForm.test.tsx │ │ │ └── useModalForm.tsx │ ├── index.d.ts │ ├── modules │ │ ├── AutoTest │ │ │ ├── components │ │ │ │ ├── AttemptsAnswers │ │ │ │ │ └── AttemptsAnswers.tsx │ │ │ │ ├── AutoTestTaskCard │ │ │ │ │ └── AutoTestTaskCard.tsx │ │ │ │ ├── Coding │ │ │ │ │ ├── Coding.test.tsx │ │ │ │ │ └── Coding.tsx │ │ │ │ ├── Exercise │ │ │ │ │ └── Exercise.tsx │ │ │ │ ├── JupyterNotebook │ │ │ │ │ └── JupyterNotebook.tsx │ │ │ │ ├── Question │ │ │ │ │ └── Question.tsx │ │ │ │ ├── SelfEducation │ │ │ │ │ ├── SelfEducation.test.tsx │ │ │ │ │ └── SelfEducation.tsx │ │ │ │ ├── StatusTabs │ │ │ │ │ ├── StatusTabs.test.tsx │ │ │ │ │ ├── StatusTabs.tsx │ │ │ │ │ └── renderers.tsx │ │ │ │ ├── TaskCard │ │ │ │ │ ├── TaskCard.test.tsx │ │ │ │ │ └── TaskCard.tsx │ │ │ │ ├── TaskCardColumn │ │ │ │ │ └── TaskCardColumn.tsx │ │ │ │ ├── TaskDeadlineDate │ │ │ │ │ ├── TaskDeadlineDate.test.tsx │ │ │ │ │ └── TaskDeadlineDate.tsx │ │ │ │ ├── TaskDescription │ │ │ │ │ └── TaskDescription.tsx │ │ │ │ ├── VerificationInformation │ │ │ │ │ ├── VerificationInformation.test.tsx │ │ │ │ │ └── VerificationInformation.tsx │ │ │ │ ├── VerificationsTable │ │ │ │ │ ├── VerificationsTable.test.tsx │ │ │ │ │ ├── VerificationsTable.tsx │ │ │ │ │ └── renderers.tsx │ │ │ │ └── index.tsx │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ ├── useAttemptsMessage │ │ │ │ │ ├── useAttemptsMessage.test.ts │ │ │ │ │ └── useAttemptsMessage.ts │ │ │ │ ├── useCourseTaskSubmit │ │ │ │ │ ├── useCourseTaskSubmit.test.ts │ │ │ │ │ └── useCourseTaskSubmit.ts │ │ │ │ ├── useCourseTaskVerifications │ │ │ │ │ └── useCourseTaskVerifications.ts │ │ │ │ └── useVerificationsAnswers │ │ │ │ │ └── useVerificationsAnswers.ts │ │ │ ├── pages │ │ │ │ ├── AutoTests │ │ │ │ │ └── AutoTests.tsx │ │ │ │ ├── Task │ │ │ │ │ └── Task.tsx │ │ │ │ └── index.tsx │ │ │ ├── types.ts │ │ │ └── utils │ │ │ │ └── map.ts │ │ ├── Contributor │ │ │ ├── components │ │ │ │ ├── ContributorModal.tsx │ │ │ │ └── ContributorsTable.tsx │ │ │ └── pages │ │ │ │ └── ContributorPage.tsx │ │ ├── Course │ │ │ ├── components │ │ │ │ ├── CourseNoAccess.tsx │ │ │ │ └── NoSubmissionAvailable │ │ │ │ │ └── index.tsx │ │ │ ├── contexts │ │ │ │ ├── ActiveCourseContext.tsx │ │ │ │ ├── SessionContext.test.tsx │ │ │ │ ├── SessionContext.tsx │ │ │ │ └── index.ts │ │ │ └── pages │ │ │ │ ├── CouseNoAccess │ │ │ │ └── index.tsx │ │ │ │ └── Student │ │ │ │ └── CrossCheckSubmit │ │ │ │ └── index.tsx │ │ ├── CourseManagement │ │ │ └── components │ │ │ │ ├── CertificateCriteriaModal │ │ │ │ ├── CertificateCriteriaModal.test.tsx │ │ │ │ └── CertificateCriteriaModal.tsx │ │ │ │ ├── CourseEventModal │ │ │ │ ├── formState.ts │ │ │ │ └── index.tsx │ │ │ │ ├── CourseModal │ │ │ │ └── index.tsx │ │ │ │ ├── CourseTaskModal │ │ │ │ └── index.tsx │ │ │ │ ├── CoursesListModal │ │ │ │ └── index.tsx │ │ │ │ ├── ExpelCriteriaModal │ │ │ │ ├── ExpelCriteriaModal.test.tsx │ │ │ │ └── ExpelCriteriaModal.tsx │ │ │ │ ├── SelectCourseTasks │ │ │ │ ├── SelectCourseTasks.test.tsx │ │ │ │ └── SelectCourseTasks.tsx │ │ │ │ └── index.ts │ │ ├── CourseStatistics │ │ │ ├── components │ │ │ │ ├── CountriesChart │ │ │ │ │ └── CountriesChart.tsx │ │ │ │ ├── DonutChart │ │ │ │ │ └── DonutChart.tsx │ │ │ │ ├── EpamMentorsStatsCard │ │ │ │ │ ├── EpamMentorsStatsCard.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── LiquidChart │ │ │ │ │ └── LiquidChart.tsx │ │ │ │ ├── MentorsCountriesCard │ │ │ │ │ ├── MentorsCountriesCard.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── StudentsCertificatesCountriesCard │ │ │ │ │ ├── StudentsCertificatesCountriesCard.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── StudentsCountriesCard │ │ │ │ │ ├── StudentsCountriesCard.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── StudentsEligibleForCertificationCard │ │ │ │ │ ├── StudentsEligibleForCertificationCard.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── StudentsStatsCard │ │ │ │ │ ├── StudentsStatsCard.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── StudentsWithCertificateCard │ │ │ │ │ ├── StudentsWithCertificateCard.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── StudentsWithMentorsCard │ │ │ │ │ ├── StudentsWithMentorsCard.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── TaskPerformanceCard │ │ │ │ │ ├── TaskPerformanceCard.tsx │ │ │ │ │ └── index.tsx │ │ │ ├── data.ts │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ └── useCourseStats │ │ │ │ │ └── useCourseStats.tsx │ │ │ ├── index.tsx │ │ │ └── pages │ │ │ │ └── CourseStatistics.tsx │ │ ├── CrossCheck │ │ │ ├── AddCriteriaForCrossCheck.tsx │ │ │ ├── CriteriaActions.tsx │ │ │ ├── CriteriaTypeSelect.tsx │ │ │ ├── DeleteAllCrossCheckCriteriaButton.tsx │ │ │ ├── EditableCellForCrossCheck.tsx │ │ │ ├── EditableCriteriaInput.tsx │ │ │ ├── EditableTableForCrossCheck.tsx │ │ │ ├── ExportJSONButton.tsx │ │ │ ├── UploadCriteriaJSON.tsx │ │ │ ├── __tests__ │ │ │ │ ├── AddCriteriaForCrossCheck.test.tsx │ │ │ │ ├── ExportJSONButton.test.tsx │ │ │ │ ├── UploadCriteriaJSON.test.tsx │ │ │ │ └── __snapshots__ │ │ │ │ │ └── AddCriteriaForCrossCheck.test.tsx.snap │ │ │ ├── components │ │ │ │ ├── CriteriaForm.tsx │ │ │ │ ├── CrossCheckAssignmentLink.tsx │ │ │ │ ├── CrossCheckCriteriaForm.tsx │ │ │ │ ├── CrossCheckHistory.tsx │ │ │ │ ├── SolutionReview │ │ │ │ │ ├── Message │ │ │ │ │ │ ├── Message.test.tsx │ │ │ │ │ │ ├── Message.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── MessageSendingPanel │ │ │ │ │ │ ├── MessageSendingPanel.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── SolutionReview.tsx │ │ │ │ │ ├── UserAvatar │ │ │ │ │ │ ├── UserAvatar.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Username │ │ │ │ │ │ ├── Username.test.tsx │ │ │ │ │ │ ├── Username.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── helpers.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── SolutionReviewSettingsPanel │ │ │ │ │ ├── SolutionReviewSettingsPanel.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── SubmittedStatus.tsx │ │ │ │ └── criteria │ │ │ │ │ ├── CrossCheckCriteria.tsx │ │ │ │ │ ├── CrossCheckCriteriaModal.tsx │ │ │ │ │ ├── PenaltyCriteria.tsx │ │ │ │ │ ├── SubtaskCriteria.tsx │ │ │ │ │ └── TitleCriteria.tsx │ │ │ ├── constants.ts │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ └── useSolutionReviewSettings.ts │ │ │ ├── index.tsx │ │ │ └── utils │ │ │ │ ├── addKeyAndIndex.tsx │ │ │ │ ├── arrayMoveImmutable.tsx │ │ │ │ └── getCriteriaStatusColor.ts │ │ ├── CrossCheckPairs │ │ │ ├── components │ │ │ │ ├── BadReview │ │ │ │ │ ├── BadReviewControllers.tsx │ │ │ │ │ └── BadReviewTable.tsx │ │ │ │ └── CrossCheckPairsTable │ │ │ │ │ └── CrossCheckPairsTable.tsx │ │ │ ├── data │ │ │ │ └── getCrossCheckPairsColumns.tsx │ │ │ └── pages │ │ │ │ └── CrossCheckPairs │ │ │ │ ├── CrossCheckPairs.tsx │ │ │ │ └── index.ts │ │ ├── Discipline │ │ │ ├── components │ │ │ │ ├── DisciplineModal.tsx │ │ │ │ └── DisciplineTable.tsx │ │ │ └── pages │ │ │ │ └── DisciplinePage.tsx │ │ ├── Feedback │ │ │ ├── components │ │ │ │ └── FeedbackForm.tsx │ │ │ └── data │ │ │ │ └── softSkills.ts │ │ ├── Home │ │ │ ├── components │ │ │ │ ├── CourseSelector │ │ │ │ │ └── index.tsx │ │ │ │ ├── HomeSummary │ │ │ │ │ └── index.tsx │ │ │ │ ├── NoCourse │ │ │ │ │ └── index.tsx │ │ │ │ ├── RegistryBanner │ │ │ │ │ └── index.tsx │ │ │ │ └── SystemAlerts │ │ │ │ │ └── index.tsx │ │ │ ├── data │ │ │ │ ├── links.tsx │ │ │ │ └── loadHomeData.ts │ │ │ ├── hooks │ │ │ │ ├── useActiveCourse.test.tsx │ │ │ │ ├── useActiveCourse.tsx │ │ │ │ └── useStudentSummary.tsx │ │ │ └── pages │ │ │ │ └── HomePage │ │ │ │ └── index.tsx │ │ ├── Interview │ │ │ └── Student │ │ │ │ ├── components │ │ │ │ ├── AlertDescription.tsx │ │ │ │ ├── ExtraInfo.tsx │ │ │ │ ├── InterviewCard.tsx │ │ │ │ ├── InterviewDescription.tsx │ │ │ │ ├── NoInterviewsAlert.tsx │ │ │ │ └── StatusLabel.tsx │ │ │ │ ├── data │ │ │ │ └── getInterviewCardDetails.tsx │ │ │ │ └── index.ts │ │ ├── Interviews │ │ │ ├── data │ │ │ │ ├── getInterviewData.ts │ │ │ │ ├── getStageInterviewData.ts │ │ │ │ └── index.ts │ │ │ └── pages │ │ │ │ ├── InterviewFeedback │ │ │ │ └── index.tsx │ │ │ │ └── StageInterviewFeedback │ │ │ │ ├── CustomQuestion.tsx │ │ │ │ ├── FormItem.tsx │ │ │ │ ├── NestedRadio.tsx │ │ │ │ ├── QuestionCard.tsx │ │ │ │ ├── QuestionList.tsx │ │ │ │ ├── QuestionsPicker.tsx │ │ │ │ ├── StageInterviewFeedback.tsx │ │ │ │ ├── StepContext.tsx │ │ │ │ ├── StepForm.tsx │ │ │ │ ├── Steps.tsx │ │ │ │ ├── StepsContent.tsx │ │ │ │ ├── StudentInfo.tsx │ │ │ │ ├── SubHeader.tsx │ │ │ │ ├── feedbackTemplateHandler.test.ts │ │ │ │ ├── feedbackTemplateHandler.ts │ │ │ │ └── index.ts │ │ ├── Mentor │ │ │ ├── components │ │ │ │ ├── Instructions │ │ │ │ │ ├── Instructions.tsx │ │ │ │ │ ├── constants.ts │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── renderers.tsx │ │ │ │ ├── MentorDashboard │ │ │ │ │ ├── MentorDashboard.test.tsx │ │ │ │ │ ├── MentorDashboard.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── Notification │ │ │ │ │ ├── Notification.test.tsx │ │ │ │ │ ├── Notification.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── ReviewRandomTask │ │ │ │ │ ├── ReviewRandomTask.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── SubmitReviewModal │ │ │ │ │ ├── SubmitReviewModal.test.tsx │ │ │ │ │ ├── SubmitReviewModal.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── TaskSolutionsTable │ │ │ │ │ ├── TaskSolutionsTable.test.tsx │ │ │ │ │ ├── TaskSolutionsTable.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── renderers.tsx │ │ │ │ ├── TaskStatusTabs │ │ │ │ │ ├── TaskStatusTabs.test.tsx │ │ │ │ │ ├── TaskStatusTabs.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ └── renderers.tsx │ │ │ │ └── index.tsx │ │ │ ├── constants.ts │ │ │ ├── data │ │ │ │ └── softSkills.ts │ │ │ ├── hooks │ │ │ │ ├── useMentorDashboard.tsx │ │ │ │ └── useMentorStudents.tsx │ │ │ └── pages │ │ │ │ ├── InterviewWaitingList │ │ │ │ └── index.tsx │ │ │ │ ├── Interviews │ │ │ │ ├── components │ │ │ │ │ ├── InterviewCard.tsx │ │ │ │ │ ├── InterviewDetails.tsx │ │ │ │ │ ├── InterviewsList.tsx │ │ │ │ │ ├── InterviewsSummary.tsx │ │ │ │ │ ├── MentorPreferencesModal.tsx │ │ │ │ │ ├── RegistrationNotice.test.tsx │ │ │ │ │ ├── RegistrationNoticeAlert.tsx │ │ │ │ │ ├── SelectMentorModal.tsx │ │ │ │ │ ├── StudentInterview.tsx │ │ │ │ │ └── WaitListAlert.tsx │ │ │ │ ├── hooks │ │ │ │ │ └── useAlert.ts │ │ │ │ └── index.tsx │ │ │ │ ├── StudentFeedback │ │ │ │ └── index.tsx │ │ │ │ └── Students │ │ │ │ └── index.tsx │ │ ├── MentorRegistry │ │ │ ├── components │ │ │ │ ├── InviteMentorsModal.tsx │ │ │ │ ├── MentorRegistryDeleteModal.tsx │ │ │ │ ├── MentorRegistryResendModal.tsx │ │ │ │ ├── MentorRegistryTable.tsx │ │ │ │ └── MentorRegistryTableContainer.tsx │ │ │ ├── constants.ts │ │ │ └── index.ts │ │ ├── MentorTasksReview │ │ │ ├── components │ │ │ │ ├── AssignReviewerModal │ │ │ │ │ ├── AssignReviewerModal.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── ReviewsTable │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── renderers.tsx │ │ │ └── pages │ │ │ │ ├── MentorTasksReview.constants.ts │ │ │ │ └── MentorTasksReview.tsx │ │ ├── NotAccess │ │ │ ├── NotAccess.tsx │ │ │ └── index.ts │ │ ├── Notifications │ │ │ ├── components │ │ │ │ ├── Consents.tsx │ │ │ │ ├── NotificationSettingsModal.tsx │ │ │ │ ├── NotificationSettingsTable.tsx │ │ │ │ └── NotificationsUserSettingsTable.tsx │ │ │ ├── pages │ │ │ │ ├── AdminNotificationsPage │ │ │ │ │ ├── AdminNotificationsSettingsPage.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── ConnectionConfirmedPage.tsx │ │ │ │ └── UserNotificationsSettingsPage.tsx │ │ │ └── services │ │ │ │ └── notifications.ts │ │ ├── Opportunities │ │ │ ├── components │ │ │ │ ├── AvatarCv │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── EditCv │ │ │ │ │ ├── ContactsForm │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── GeneralInfoForm │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── VisibleCoursesForm │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── form-validation.ts │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── EditViewCv │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── ExpirationTooltip │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── Link │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── NameTitle │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── NoConsentView │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── PublicLink │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── SidebarSectionHeader │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── StudentStatus │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── ViewCv │ │ │ │ │ ├── AboutSection │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── ActionButtons │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── BaseSection │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── ContactsSection │ │ │ │ │ ├── ContactsList │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── CoursesSection │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── DataTextValue │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── FeedbackSection │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── GratitudeSection │ │ │ │ │ ├── GratitudeList │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── PersonalSection │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ └── index.tsx │ │ │ ├── constants.ts │ │ │ ├── data │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── getPersonalToRender.test.tsx.snap │ │ │ │ ├── getContactsToRender.test.tsx │ │ │ │ ├── getContactsToRender.ts │ │ │ │ ├── getPersonalToRender.test.tsx │ │ │ │ └── getPersonalToRender.tsx │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ ├── useExpiration.test.tsx │ │ │ │ ├── useExpiration.ts │ │ │ │ ├── useResumeData.test.tsx │ │ │ │ ├── useResumeData.tsx │ │ │ │ ├── useViewData.test.tsx │ │ │ │ └── useViewData.tsx │ │ │ ├── models.ts │ │ │ ├── pages │ │ │ │ ├── EditPage │ │ │ │ │ └── index.tsx │ │ │ │ └── PublicPage │ │ │ │ │ ├── getServerSideProps.ts │ │ │ │ │ └── index.tsx │ │ │ └── transformers │ │ │ │ ├── index.ts │ │ │ │ ├── splitDataForForms.test.ts │ │ │ │ ├── splitDataForForms.ts │ │ │ │ ├── transformFieldsData.test.ts │ │ │ │ ├── transformFieldsData.ts │ │ │ │ ├── transformInitialCvData.test.ts │ │ │ │ └── transformInitialCvData.ts │ │ ├── Profile │ │ │ └── components │ │ │ │ └── MentorEndorsement │ │ │ │ ├── MentorEndorsement.tsx │ │ │ │ └── index.tsx │ │ ├── Prompts │ │ │ ├── components │ │ │ │ ├── PromptModal.tsx │ │ │ │ └── PromptTable.tsx │ │ │ └── pages │ │ │ │ └── PromptPage.tsx │ │ ├── Registry │ │ │ ├── components │ │ │ │ ├── Cards │ │ │ │ │ ├── AdditionalInfo │ │ │ │ │ │ ├── AdditionalInfo.test.tsx │ │ │ │ │ │ └── AdditionalInfo.tsx │ │ │ │ │ ├── ContactInfo │ │ │ │ │ │ ├── ContactInfo.test.tsx │ │ │ │ │ │ └── ContactInfo.tsx │ │ │ │ │ ├── CourseDetails │ │ │ │ │ │ ├── CourseDetails.test.tsx │ │ │ │ │ │ └── CourseDetails.tsx │ │ │ │ │ ├── Disciplines │ │ │ │ │ │ ├── Disciplines.test.tsx │ │ │ │ │ │ └── Disciplines.tsx │ │ │ │ │ ├── PersonalInfo │ │ │ │ │ │ ├── PersonalInfo.test.tsx │ │ │ │ │ │ └── PersonalInfo.tsx │ │ │ │ │ └── Preferences │ │ │ │ │ │ ├── Preferences.test.tsx │ │ │ │ │ │ └── Preferences.tsx │ │ │ │ ├── CourseLabel │ │ │ │ │ └── CourseLabel.tsx │ │ │ │ ├── DataProcessingCheckbox │ │ │ │ │ ├── DataProcessingCheckbox.test.tsx │ │ │ │ │ └── DataProcessingCheckbox.tsx │ │ │ │ ├── Footer │ │ │ │ │ └── Footer.tsx │ │ │ │ ├── FormButtons │ │ │ │ │ ├── FormButtons.test.tsx │ │ │ │ │ └── FormButtons.tsx │ │ │ │ ├── FormCard │ │ │ │ │ └── FormCard.tsx │ │ │ │ ├── FormSections │ │ │ │ │ ├── DoneSection │ │ │ │ │ │ ├── DoneSection.test.tsx │ │ │ │ │ │ └── DoneSection.tsx │ │ │ │ │ ├── GeneralSection │ │ │ │ │ │ ├── GeneralSection.test.tsx │ │ │ │ │ │ └── GeneralSection.tsx │ │ │ │ │ └── MentorshipSection │ │ │ │ │ │ ├── MentorshipSection.test.tsx │ │ │ │ │ │ └── MentorshipSection.tsx │ │ │ │ ├── Header │ │ │ │ │ └── Header.tsx │ │ │ │ ├── LanguagesMentoring │ │ │ │ │ ├── LanguagesMentoring.test.tsx │ │ │ │ │ └── LanguagesMentoring.tsx │ │ │ │ ├── NoCourses │ │ │ │ │ └── NoCourses.tsx │ │ │ │ ├── RegistrationForm │ │ │ │ │ ├── RegistrationForm.test.tsx │ │ │ │ │ └── RegistrationForm.tsx │ │ │ │ └── index.tsx │ │ │ ├── constants │ │ │ │ └── index.ts │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ ├── useFormLayout │ │ │ │ │ └── useFormLayout.ts │ │ │ │ ├── useMentorData │ │ │ │ │ └── useMentorData.tsx │ │ │ │ └── useStudentData │ │ │ │ │ └── useStudentData.tsx │ │ │ └── pages │ │ │ │ ├── Mentor │ │ │ │ └── Mentor.tsx │ │ │ │ ├── Student │ │ │ │ └── Student.tsx │ │ │ │ └── index.ts │ │ ├── Schedule │ │ │ ├── components │ │ │ │ ├── AdditionalActions │ │ │ │ │ ├── AdditionalActions.test.tsx │ │ │ │ │ ├── AdditionalActions.tsx │ │ │ │ │ ├── helpers.ts │ │ │ │ │ └── index.tsx │ │ │ │ ├── EventDetails │ │ │ │ │ ├── EventDetails.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── FilteredTags │ │ │ │ │ ├── FilteredTags.test.tsx │ │ │ │ │ ├── FilteredTags.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── MobileItemCard │ │ │ │ │ ├── MobileItemCard.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── SettingsDrawer │ │ │ │ │ ├── ChangeTagColors.tsx │ │ │ │ │ ├── SettingsDrawer.tsx │ │ │ │ │ ├── SettingsItem.tsx │ │ │ │ │ ├── ShowTableColumns.tsx │ │ │ │ │ ├── TimeZone.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── SettingsPanel │ │ │ │ │ ├── SettingsPanel.test.tsx │ │ │ │ │ ├── SettingsPanel.tsx │ │ │ │ │ ├── helpers.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── StatusTabs │ │ │ │ │ ├── StatusTabs.test.tsx │ │ │ │ │ ├── StatusTabs.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ └── renderers.tsx │ │ │ │ └── TableView │ │ │ │ │ ├── TableView.test.tsx │ │ │ │ │ ├── TableView.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ └── renderers.tsx │ │ │ ├── constants.ts │ │ │ ├── hooks │ │ │ │ └── useScheduleSettings.ts │ │ │ ├── index.ts │ │ │ ├── pages │ │ │ │ └── SchedulePage │ │ │ │ │ └── index.tsx │ │ │ └── utils.ts │ │ ├── Score │ │ │ ├── components │ │ │ │ ├── ExportCsvButton │ │ │ │ │ └── index.tsx │ │ │ │ ├── ScoreTable │ │ │ │ │ ├── Summary.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── SettingsModal │ │ │ │ │ └── index.tsx │ │ │ ├── data │ │ │ │ ├── getColumns.tsx │ │ │ │ ├── getExportCsvUrl.ts │ │ │ │ ├── getTaskColumns.tsx │ │ │ │ └── isExportEnabled.ts │ │ │ ├── hooks │ │ │ │ ├── types.ts │ │ │ │ └── useScorePaging.tsx │ │ │ └── pages │ │ │ │ └── ScorePage │ │ │ │ └── index.tsx │ │ ├── StudentDashboard │ │ │ ├── components │ │ │ │ ├── AvailableReviewCard │ │ │ │ │ ├── AvailableReviewCard.test.tsx │ │ │ │ │ ├── AvailableReviewCard.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── CommonDashboardCard.tsx │ │ │ │ ├── MainStatsCard.tsx │ │ │ │ ├── MentorCard │ │ │ │ │ ├── MentorCard.test.tsx │ │ │ │ │ ├── MentorCard.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── MentorInfo │ │ │ │ │ ├── MentorInfo.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── NextEventCard │ │ │ │ │ ├── NextEventCard.test.tsx │ │ │ │ │ ├── NextEventCard.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── renderers.tsx │ │ │ │ ├── RepositoryCard.tsx │ │ │ │ ├── SubmitTaskSolution │ │ │ │ │ ├── SubmitTaskSolution.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── TasksChart.tsx │ │ │ │ ├── TasksStatsCard.tsx │ │ │ │ ├── TasksStatsModal.tsx │ │ │ │ └── index.ts │ │ │ ├── hooks │ │ │ │ ├── useDashboardData.ts │ │ │ │ ├── useSubmitTaskSolution.test.ts │ │ │ │ └── useSubmitTaskSolution.ts │ │ │ └── index.ts │ │ ├── Students │ │ │ ├── Pages │ │ │ │ └── Students.tsx │ │ │ └── components │ │ │ │ ├── CourseItem │ │ │ │ └── index.tsx │ │ │ │ ├── StudentInfo │ │ │ │ └── index.tsx │ │ │ │ └── StudentsTable │ │ │ │ ├── index.tsx │ │ │ │ └── renderers.tsx │ │ ├── Tasks │ │ │ ├── components │ │ │ │ ├── CrossCheckTaskCriteriaPanel │ │ │ │ │ ├── CrossCheckTaskCriteriaPanel.test.tsx │ │ │ │ │ └── CrossCheckTaskCriteriaPanel.tsx │ │ │ │ ├── GitHubPanel │ │ │ │ │ ├── GitHubPanel.test.tsx │ │ │ │ │ └── GitHubPanel.tsx │ │ │ │ ├── JsonAttributesPanel │ │ │ │ │ ├── JsonAttributesPanel.test.tsx │ │ │ │ │ └── JsonAttributesPanel.tsx │ │ │ │ ├── TaskModal │ │ │ │ │ ├── TaskModal.test.tsx │ │ │ │ │ └── TaskModal.tsx │ │ │ │ ├── TaskSettings │ │ │ │ │ ├── TaskSettings.test.tsx │ │ │ │ │ └── TaskSettings.tsx │ │ │ │ ├── TasksTable │ │ │ │ │ ├── TasksTable.test.tsx │ │ │ │ │ └── TasksTable.tsx │ │ │ │ └── index.ts │ │ │ ├── constants.ts │ │ │ ├── pages │ │ │ │ ├── TasksPage │ │ │ │ │ └── TasksPage.tsx │ │ │ │ └── index.ts │ │ │ ├── types.ts │ │ │ └── utils │ │ │ │ └── test-utils.ts │ │ ├── TeamDistribution │ │ │ ├── components │ │ │ │ ├── SubmitScoreModal │ │ │ │ │ ├── SubmitScoreModal.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── TeamDistributionCard │ │ │ │ │ ├── Actions.test.tsx │ │ │ │ │ ├── Actions.tsx │ │ │ │ │ ├── CardTitle.test.tsx │ │ │ │ │ ├── CardTitle.tsx │ │ │ │ │ ├── DistributionPeriod.tsx │ │ │ │ │ ├── TeamDistributionCard.test.tsx │ │ │ │ │ ├── TeamDistributionCard.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ └── renderers.tsx │ │ │ │ ├── TeamDistributionModal │ │ │ │ │ ├── TeamDistributionModal.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── WelcomeCard │ │ │ │ │ ├── WelcomeCard.test.tsx │ │ │ │ │ ├── WelcomeCard.tsx │ │ │ │ │ └── index.ts │ │ │ ├── hooks │ │ │ │ ├── useSubmitTeamScore.test.tsx │ │ │ │ └── useSubmitTeamScore.tsx │ │ │ └── pages │ │ │ │ └── TeamDistributions │ │ │ │ ├── TeamDistributions.tsx │ │ │ │ └── index.tsx │ │ └── Teams │ │ │ ├── Pages │ │ │ └── Teams.tsx │ │ │ ├── components │ │ │ ├── JoinTeamModal │ │ │ │ └── JoinTeamModal.tsx │ │ │ ├── MyTeamSection │ │ │ │ └── MyTeamSection.tsx │ │ │ ├── StudentsTable │ │ │ │ ├── StudentsTable.tsx │ │ │ │ └── renderers.tsx │ │ │ ├── StudentsWithoutTeamSection │ │ │ │ └── StudentsWithoutTeamSection.tsx │ │ │ ├── TeamModal │ │ │ │ ├── TeamModal.test.tsx │ │ │ │ └── TeamModal.tsx │ │ │ ├── TeamsHeader │ │ │ │ ├── ActionCard.tsx │ │ │ │ └── TeamsHeader.tsx │ │ │ ├── TeamsSection │ │ │ │ ├── TeamsSection.tsx │ │ │ │ └── renderers.tsx │ │ │ └── index.ts │ │ │ ├── constants.ts │ │ │ ├── hooks │ │ │ ├── index.ts │ │ │ └── useDistribution │ │ │ │ ├── useDistribution.test.ts │ │ │ │ └── useDistribution.ts │ │ │ ├── index.tsx │ │ │ └── utils │ │ │ └── showConfirmationModals.tsx │ ├── pages │ │ ├── 404.tsx │ │ ├── _app.tsx │ │ ├── _document.tsx │ │ ├── admin │ │ │ ├── auto-test-task │ │ │ │ └── [taskId].tsx │ │ │ ├── auto-test.tsx │ │ │ ├── contributors.tsx │ │ │ ├── courses.tsx │ │ │ ├── disciplines.tsx │ │ │ ├── discord-server.tsx │ │ │ ├── events.tsx │ │ │ ├── mentor-registry.tsx │ │ │ ├── notifications.tsx │ │ │ ├── prompts.tsx │ │ │ ├── registrations.tsx │ │ │ ├── students.tsx │ │ │ ├── tasks.tsx │ │ │ ├── user-group.tsx │ │ │ └── users.tsx │ │ ├── applicants │ │ │ └── index.tsx │ │ ├── course │ │ │ ├── 403.tsx │ │ │ ├── admin │ │ │ │ ├── certified-students.tsx │ │ │ │ ├── cross-check-table.tsx │ │ │ │ ├── events.tsx │ │ │ │ ├── interviews.tsx │ │ │ │ ├── mentor-tasks-review.tsx │ │ │ │ ├── mentors.tsx │ │ │ │ ├── stage-interviews.tsx │ │ │ │ ├── students.tsx │ │ │ │ ├── tasks.tsx │ │ │ │ └── users.tsx │ │ │ ├── interview │ │ │ │ └── [type] │ │ │ │ │ └── feedback.tsx │ │ │ ├── mentor │ │ │ │ ├── auto-confirm.tsx │ │ │ │ ├── confirm.tsx │ │ │ │ ├── dashboard.tsx │ │ │ │ ├── expel-student.tsx │ │ │ │ ├── feedback │ │ │ │ │ └── index.tsx │ │ │ │ ├── interview-technical-screening.tsx │ │ │ │ ├── interview-wait-list.tsx │ │ │ │ ├── interviews.tsx │ │ │ │ └── students.tsx │ │ │ ├── schedule.tsx │ │ │ ├── score.tsx │ │ │ ├── stats.tsx │ │ │ ├── student │ │ │ │ ├── auto-test │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── task.tsx │ │ │ │ ├── cross-check-review.tsx │ │ │ │ ├── cross-check-submit.tsx │ │ │ │ ├── dashboard.tsx │ │ │ │ └── interviews.tsx │ │ │ ├── submit-scores.tsx │ │ │ ├── team-distributions.tsx │ │ │ └── teams.tsx │ │ ├── cv │ │ │ ├── [uuid].tsx │ │ │ └── edit.tsx │ │ ├── gratitude.tsx │ │ ├── heroes.tsx │ │ ├── index.tsx │ │ ├── login │ │ │ └── index.tsx │ │ ├── profile │ │ │ ├── connection-confirmed.tsx │ │ │ ├── index.tsx │ │ │ └── notifications.tsx │ │ └── registry │ │ │ ├── epamlearningjs.tsx │ │ │ ├── mentor.tsx │ │ │ └── student.tsx │ ├── reset.d.ts │ ├── services │ │ ├── cdn.ts │ │ ├── check.ts │ │ ├── course.ts │ │ ├── courses.ts │ │ ├── features.tsx │ │ ├── files.ts │ │ ├── formatter.ts │ │ ├── gratitude.ts │ │ ├── mentorRegistry.ts │ │ ├── models.ts │ │ ├── reference-data │ │ │ └── stageInterview.ts │ │ ├── routes.ts │ │ ├── user.ts │ │ ├── validators.test.ts │ │ └── validators.ts │ ├── setupJest.ts │ ├── styles │ │ └── main.css │ └── utils │ │ ├── axios.ts │ │ ├── onlyDefined.ts │ │ ├── optionalQueryString.ts │ │ ├── profilePageUtils.ts │ │ ├── queryParams-utils.test.ts │ │ ├── queryParams-utils.ts │ │ ├── text-utils.test.ts │ │ ├── text-utils.ts │ │ └── useWindowDimensions.ts └── tsconfig.json ├── common ├── enums │ └── mentor │ │ └── index.ts ├── interfaces │ └── gratitude │ │ └── index.ts ├── models │ ├── index.ts │ ├── interview.ts │ ├── profile.ts │ ├── stage-interview-feedback.ts │ └── user.ts └── types │ └── pagination │ └── index.ts ├── docker-compose.yml ├── docs ├── .nojekyll ├── CNAME ├── README.md ├── _sidebar.md ├── code-of-conduct.md ├── index.html └── platform │ ├── about.md │ ├── adding-tests.md │ ├── choose-kata-languages.md │ ├── cross-check-flow.md │ ├── cross-check-scheduling.md │ ├── cv.md │ ├── img │ ├── adding-tests-1.png │ ├── adding-tests-2.png │ ├── adding-tests-3.png │ ├── adding-tests-4.png │ ├── adding-tests-5.png │ ├── adding-tests-6.png │ ├── adding-tests-7.png │ ├── adding-tests-8.png │ ├── autotest-details.jpg │ ├── choose-kata-languages │ │ ├── task-json.JPG │ │ └── task-settings.JPG │ ├── cross-check-scheduling │ │ └── course-task-modal.JPG │ ├── cv │ │ ├── applicants-page-link.JPG │ │ ├── applicants-table.JPG │ │ ├── cv-form-filled-1.JPG │ │ ├── cv-form-filled.JPG │ │ ├── cv-view-filled.JPG │ │ ├── cv-view-print.JPG │ │ ├── edit-cv-buttons.JPG │ │ ├── employer-page.JPG │ │ ├── header-dropdown.JPG │ │ ├── no-consent-modal.JPG │ │ ├── no-consent.JPG │ │ ├── view-control-buttons.JPG │ │ └── view-delete-cv.JPG │ ├── no-access.png │ ├── schedule-1.png │ ├── schedule-10.png │ ├── schedule-2.png │ ├── schedule-3.png │ ├── schedule-4.png │ ├── schedule-5.png │ ├── schedule-6.png │ ├── schedule-7.png │ ├── schedule-8.png │ ├── schedule-9.png │ └── tasks-1.jpg │ ├── notifications.md │ ├── pull-request-review-process.md │ ├── shedule.md │ ├── tasks.md │ └── typical-problems.md ├── eslint.config.mjs ├── nestjs ├── .dockerignore ├── .env.example ├── .prettierrc ├── Dockerfile ├── Dockerfile.lambda ├── README.md ├── eslint.config.mjs ├── jest.config.mjs ├── lambda │ └── app.js ├── nest-cli.json ├── openapitools.json ├── package.json ├── src │ ├── activity │ │ ├── activity.controller.ts │ │ ├── activity.module.ts │ │ └── dto │ │ │ ├── activity.dto.ts │ │ │ ├── create-activity-webhook.dto.ts │ │ │ └── create-activity.dto.ts │ ├── alerts │ │ ├── alerts.controller.ts │ │ ├── alerts.module.ts │ │ ├── alerts.service.ts │ │ └── dto │ │ │ ├── alert.dto.ts │ │ │ ├── create-alert.dto.ts │ │ │ ├── index.ts │ │ │ └── update-alert.dto.ts │ ├── app.module.ts │ ├── auth │ │ ├── auth-user.model.spec.ts │ │ ├── auth-user.model.ts │ │ ├── auth.controller.spec.ts │ │ ├── auth.controller.ts │ │ ├── auth.module.ts │ │ ├── auth.service.spec.ts │ │ ├── auth.service.ts │ │ ├── constants.ts │ │ ├── course.guard.ts │ │ ├── default.guard.ts │ │ ├── dto │ │ │ └── auth-connection.dto.ts │ │ ├── index.ts │ │ ├── role.decorator.ts │ │ ├── role.guard.ts │ │ └── strategies │ │ │ ├── basic.strategy.ts │ │ │ ├── dev.strategy.ts │ │ │ ├── github.strategy.ts │ │ │ └── jwt.strategy.ts │ ├── auto-test │ │ ├── auto-test.controller.ts │ │ ├── auto-test.module.ts │ │ ├── auto-test.service.ts │ │ └── dto │ │ │ ├── auto-test-task.dto.ts │ │ │ ├── basic-auto-test-task.dto.ts │ │ │ └── task-solution.dto.ts │ ├── certificates │ │ ├── certificates.controller.ts │ │ ├── certificates.module.ts │ │ ├── certificates.service.ts │ │ └── dto │ │ │ ├── certificate-metadata.dto.ts │ │ │ └── save-certificate-dto.ts │ ├── cloud-api │ │ ├── cloud-api.module.ts │ │ └── cloud-api.service.ts │ ├── config │ │ ├── config.module.ts │ │ ├── config.service.ts │ │ └── index.ts │ ├── constants.ts │ ├── contributors │ │ ├── contributors.controller.ts │ │ ├── contributors.module.ts │ │ ├── contributors.service.ts │ │ ├── dto │ │ │ ├── contributor.dto.ts │ │ │ ├── create-contributor.dto.ts │ │ │ ├── index.ts │ │ │ └── update-contributor.dto.ts │ │ └── index.ts │ ├── core │ │ ├── core.module.ts │ │ ├── decorators │ │ │ ├── index.ts │ │ │ └── student-id.decorator.ts │ │ ├── dto │ │ │ ├── id-name.dto.ts │ │ │ ├── index.ts │ │ │ ├── pagination.dto.ts │ │ │ └── person.dto.ts │ │ ├── filters │ │ │ ├── entity-not-found.filter.ts │ │ │ ├── index.ts │ │ │ └── sentry.filter.ts │ │ ├── jwt │ │ │ └── jwt.service.ts │ │ ├── middlewares │ │ │ ├── index.ts │ │ │ ├── logger.middleware.ts │ │ │ └── no-cache.middleware.ts │ │ ├── paginate │ │ │ ├── dto │ │ │ │ └── Paginate.dto.ts │ │ │ ├── index.ts │ │ │ └── test.ts │ │ ├── pino.ts │ │ ├── subscribers │ │ │ ├── base-subscriber.ts │ │ │ ├── course-event.subscriber.ts │ │ │ └── course-task.subscriber.ts │ │ ├── templates │ │ │ └── index.ts │ │ └── validation │ │ │ ├── index.ts │ │ │ ├── validation.exception.ts │ │ │ └── validation.filter.ts │ ├── courses │ │ ├── course-access.service.ts │ │ ├── course-events │ │ │ ├── course-events.controller.ts │ │ │ ├── course-events.service.ts │ │ │ ├── dto │ │ │ │ ├── course-event.dto.ts │ │ │ │ ├── create-course-event.dto.ts │ │ │ │ └── update-course-event.dto.ts │ │ │ └── index.ts │ │ ├── course-mentors │ │ │ ├── course-mentors.controller.ts │ │ │ ├── course-mentors.service.ts │ │ │ ├── dto │ │ │ │ ├── mentor-details.dto.ts │ │ │ │ └── search-mentor.dto.ts │ │ │ └── index.ts │ │ ├── course-schedule │ │ │ ├── course-icalendar.controller.ts │ │ │ ├── course-icalendar.service.ts │ │ │ ├── course-schedule.controller.ts │ │ │ ├── course-schedule.service.spec.ts │ │ │ ├── course-schedule.service.ts │ │ │ ├── dto │ │ │ │ ├── course-copy-from.dto.ts │ │ │ │ ├── course-schedule-hash.dto.ts │ │ │ │ ├── course-schedule-item.dto.ts │ │ │ │ └── index.ts │ │ │ └── index.ts │ │ ├── course-students │ │ │ ├── course-students.controller.ts │ │ │ ├── course-students.service.ts │ │ │ └── dto │ │ │ │ ├── mentor-student-summary.dto.ts │ │ │ │ ├── result.dto.ts │ │ │ │ └── student-summary.dto.ts │ │ ├── course-tasks │ │ │ ├── course-tasks.controller.ts │ │ │ ├── course-tasks.service.ts │ │ │ ├── dto │ │ │ │ ├── course-task-detailed.dto.ts │ │ │ │ ├── course-task.dto.ts │ │ │ │ ├── create-course-task.dto.ts │ │ │ │ ├── index.ts │ │ │ │ └── update-course-task.dto.ts │ │ │ └── index.ts │ │ ├── course-users │ │ │ ├── course-users.controller.spec.ts │ │ │ ├── course-users.controller.ts │ │ │ ├── course-users.service.spec.ts │ │ │ ├── course-users.service.ts │ │ │ ├── dto │ │ │ │ ├── course-roles.dto.ts │ │ │ │ ├── course-user.dto.ts │ │ │ │ └── update-user.dto.ts │ │ │ └── types.ts │ │ ├── courses.controller.ts │ │ ├── courses.module.ts │ │ ├── courses.service.ts │ │ ├── cross-checks │ │ │ ├── course-cross-checks.controller.ts │ │ │ ├── course-cross-checks.service.spec.ts │ │ │ ├── course-cross-checks.service.ts │ │ │ ├── cross-check-feedback.guard.ts │ │ │ ├── dto │ │ │ │ ├── available-review-stats.dto.ts │ │ │ │ ├── check-tasks-pairs.dto.ts │ │ │ │ ├── cross-check-criteria-data.dto.ts │ │ │ │ ├── cross-check-feedback.dto.ts │ │ │ │ └── index.ts │ │ │ └── index.ts │ │ ├── dto │ │ │ ├── course.dto.ts │ │ │ ├── create-course.dto.ts │ │ │ ├── export-course.dto.ts │ │ │ ├── index.ts │ │ │ ├── leave-course.dto.ts │ │ │ ├── update-course.dto.ts │ │ │ └── used-course.dto.ts │ │ ├── index.ts │ │ ├── interviews │ │ │ ├── dto │ │ │ │ ├── available-student.dto.ts │ │ │ │ ├── get-interview-feedback.dto.ts │ │ │ │ ├── index.ts │ │ │ │ ├── interview-pair.dto.ts │ │ │ │ ├── interview.dto.ts │ │ │ │ ├── put-interview-feedback.dto.ts │ │ │ │ └── registration-interview.dto.ts │ │ │ ├── index.ts │ │ │ ├── interviewFeedback.service.ts │ │ │ ├── interviews.controller.ts │ │ │ └── interviews.service.ts │ │ ├── mentor-reviews │ │ │ ├── dto │ │ │ │ ├── index.ts │ │ │ │ ├── mentor-review-assign.dto.ts │ │ │ │ ├── mentor-reviews-query.dto.ts │ │ │ │ └── mentor-reviews.dto.ts │ │ │ ├── index.ts │ │ │ ├── mentor-reviews.controller.ts │ │ │ └── mentor-reviews.service.ts │ │ ├── mentors │ │ │ ├── dto │ │ │ │ ├── index.ts │ │ │ │ ├── mentor-dashboard.dto.ts │ │ │ │ ├── mentor-options.dto.ts │ │ │ │ └── mentor-student.dto.ts │ │ │ ├── index.ts │ │ │ ├── mentors.controller.ts │ │ │ └── mentors.service.ts │ │ ├── score │ │ │ ├── dto │ │ │ │ ├── score-query.dto.ts │ │ │ │ └── score.dto.ts │ │ │ ├── index.ts │ │ │ ├── score.controller.ts │ │ │ ├── score.service.ts │ │ │ └── write-score.service.ts │ │ ├── stats │ │ │ ├── course-stats.controller.ts │ │ │ ├── course-stats.service.ts │ │ │ ├── dto │ │ │ │ ├── countries-stats.dto.ts │ │ │ │ ├── course-mentors-stats.dto.ts │ │ │ │ ├── course-stats.dto.ts │ │ │ │ ├── index.ts │ │ │ │ └── task-performance-stats.dto.ts │ │ │ └── index.ts │ │ ├── students │ │ │ ├── dto │ │ │ │ ├── index.ts │ │ │ │ ├── student.dto.ts │ │ │ │ ├── user-students-query.dto.ts │ │ │ │ └── user-students.dto.ts │ │ │ ├── feedbacks │ │ │ │ ├── dto │ │ │ │ │ ├── create-student-feedback.dto.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── student-feedback.dto.ts │ │ │ │ │ └── update-student-feedback.dto.ts │ │ │ │ ├── feedbacks.controller.ts │ │ │ │ ├── feedbacks.service.ts │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── students.controller.ts │ │ │ └── students.service.ts │ │ ├── task-solutions │ │ │ ├── dto │ │ │ │ ├── create-task-solution.dto.ts │ │ │ │ ├── index.ts │ │ │ │ └── task-solution.dto.ts │ │ │ ├── index.ts │ │ │ ├── task-solutions.controller.ts │ │ │ └── task-solutions.service.ts │ │ ├── task-verifications │ │ │ ├── dto │ │ │ │ ├── create-task-verification.dto.ts │ │ │ │ ├── index.ts │ │ │ │ ├── self-education.dto.ts │ │ │ │ └── task-verifications-attempts.dto.ts │ │ │ ├── self-education.service.test.ts │ │ │ ├── self-education.service.ts │ │ │ ├── task-verifications.controller.ts │ │ │ └── task-verifications.service.ts │ │ ├── tasks │ │ │ ├── dto │ │ │ │ └── check-tasks-deadline.ts │ │ │ ├── tasks.controller.ts │ │ │ └── tasks.service.ts │ │ └── team-distribution │ │ │ ├── distribute-students.service.ts │ │ │ ├── dto │ │ │ ├── create-team-distribution.dto.ts │ │ │ ├── create-team.dto.ts │ │ │ ├── index.ts │ │ │ ├── join-team.dto.ts │ │ │ ├── team-distribution-student.dto.ts │ │ │ ├── team-distribution.dto.ts │ │ │ ├── team.dto.ts │ │ │ ├── update-team-distribution.dto.ts │ │ │ └── update-team-dto.ts │ │ │ ├── registered-student-guard.ts │ │ │ ├── team-distribution-student.service.ts │ │ │ ├── team-distribution.controller.ts │ │ │ ├── team-distribution.service.ts │ │ │ ├── team-lead-or-manager.guard.ts │ │ │ ├── team.controller.ts │ │ │ └── team.service.ts │ ├── cross-check │ │ ├── cross-check.module.ts │ │ ├── cross-check.service.spec.ts │ │ ├── cross-check.service.ts │ │ └── tasks-filtering.ts │ ├── disciplines │ │ ├── disciplines.controller.test.ts │ │ ├── disciplines.controller.ts │ │ ├── disciplines.module.ts │ │ ├── disciplines.service.test.ts │ │ ├── disciplines.service.ts │ │ ├── dto │ │ │ ├── create-discipline.dto.ts │ │ │ ├── discipline.dto.ts │ │ │ ├── index.ts │ │ │ └── update-discipline.dto.ts │ │ └── index.ts │ ├── discord-servers │ │ ├── discord-servers.controller.ts │ │ ├── discord-servers.module.ts │ │ ├── discord-servers.service.ts │ │ └── dto │ │ │ ├── create-discord-server.dto.ts │ │ │ ├── discord-server.dto.ts │ │ │ ├── index.ts │ │ │ └── update-discord-server.dto.ts │ ├── events │ │ ├── dto │ │ │ ├── create-event.dto.ts │ │ │ ├── event.dto.ts │ │ │ ├── index.ts │ │ │ └── update-event.dto.ts │ │ ├── events.controller.ts │ │ ├── events.module.ts │ │ └── events.service.ts │ ├── gratitudes │ │ ├── discord.service.ts │ │ ├── dto │ │ │ ├── badge.dto.ts │ │ │ ├── country.dto.ts │ │ │ ├── create-gratitude.dto.ts │ │ │ ├── gratitude.dto.ts │ │ │ ├── hero-radar.dto.ts │ │ │ ├── heroes-radar-badge.dto.ts │ │ │ ├── heroes-radar-query.dto.ts │ │ │ ├── heroes-radar.dto.ts │ │ │ └── index.ts │ │ ├── gratitudes.controller.ts │ │ ├── gratitudes.module.ts │ │ ├── gratitudes.service.ts │ │ └── index.ts │ ├── listeners │ │ ├── course.listener.ts │ │ ├── index.ts │ │ └── listeners.module.ts │ ├── main.lambda.ts │ ├── main.ts │ ├── migrations.ts │ ├── notifications │ │ ├── dto │ │ │ ├── notification.dto.ts │ │ │ └── update-notification.dto.ts │ │ ├── email-template.ts │ │ ├── notifications.controller.ts │ │ ├── notifications.module.ts │ │ └── notifications.service.ts │ ├── openapi-spec.ts │ ├── opportunities │ │ ├── dto │ │ │ ├── applicant-resume.dto.ts │ │ │ ├── consent.dto.ts │ │ │ ├── form-data.dto.ts │ │ │ ├── give-consent-dto.ts │ │ │ ├── resume.dto.ts │ │ │ ├── status.dto.ts │ │ │ └── visibility.dto.ts │ │ ├── opportunities.controller.ts │ │ ├── opportunities.module.ts │ │ └── opportunities.service.ts │ ├── ormconfig.ts │ ├── profile │ │ ├── dto │ │ │ ├── endorsement.dto.ts │ │ │ ├── index.ts │ │ │ ├── personal-profile.dto.ts │ │ │ ├── profile-course.dto.ts │ │ │ ├── profile.dto.ts │ │ │ ├── update-profile.dto.ts │ │ │ └── update-user.dto.ts │ │ ├── endorsement.service.ts │ │ ├── index.ts │ │ ├── profile.controller.ts │ │ ├── profile.module.ts │ │ └── profile.service.ts │ ├── prompts │ │ ├── dto │ │ │ ├── create-prompt.dto.ts │ │ │ ├── index.ts │ │ │ ├── prompt.dto.ts │ │ │ └── update-prompt.dto.ts │ │ ├── prompts.controller.ts │ │ ├── prompts.module.ts │ │ └── prompts.service.ts │ ├── registry │ │ ├── constants.ts │ │ ├── dto │ │ │ ├── approve-mentor.dto.ts │ │ │ ├── comment-mentor-registry.dto.ts │ │ │ ├── invite-mentors.dto.ts │ │ │ └── mentor-registry.dto.ts │ │ ├── registry.controller.ts │ │ ├── registry.module.ts │ │ └── registry.service.ts │ ├── reset.d.ts │ ├── schedule │ │ ├── dto │ │ │ └── check-schedule-changes.ts │ │ ├── schedule.controller.ts │ │ ├── schedule.module.ts │ │ └── schedule.service.ts │ ├── session │ │ ├── dto │ │ │ └── auth-user.dto.ts │ │ ├── session.controller.ts │ │ └── session.module.ts │ ├── setup.ts │ ├── spec.json │ ├── tasks │ │ ├── dto │ │ │ ├── create-task.dto.ts │ │ │ ├── index.ts │ │ │ ├── task.dto.ts │ │ │ └── update-task.dto.ts │ │ ├── tasks-criteria │ │ │ ├── dto │ │ │ │ ├── criteria.dto.ts │ │ │ │ └── task-criteria.dto.ts │ │ │ ├── index.ts │ │ │ ├── tasks-criteria.controller.ts │ │ │ └── tasks-criteria.service.ts │ │ ├── tasks.controller.ts │ │ ├── tasks.module.ts │ │ └── tasks.service.ts │ ├── user-groups │ │ ├── dto │ │ │ ├── create-user-group.dto.ts │ │ │ ├── index.ts │ │ │ ├── update-user-group.dto.ts │ │ │ └── user-group.dto.ts │ │ ├── index.ts │ │ ├── user-groups.controller.ts │ │ ├── user-groups.module.ts │ │ └── user-groups.service.ts │ ├── users-notifications │ │ ├── dto │ │ │ ├── notification-connection-exists.dto.ts │ │ │ ├── notification-connection.dto.ts │ │ │ ├── notification-user-connections.dto.ts │ │ │ ├── notification-user-settings.dto.ts │ │ │ ├── send-user-notification.dto.ts │ │ │ ├── update-notification-user-settings.dto.ts │ │ │ └── upsert-notification-connection.dto.ts │ │ ├── index.ts │ │ ├── users-notifications.module.ts │ │ ├── users.notifications.controller.ts │ │ └── users.notifications.service.ts │ └── users │ │ ├── dto │ │ ├── index.ts │ │ └── user-search.dto.ts │ │ ├── index.ts │ │ ├── users.controller.ts │ │ ├── users.module.ts │ │ └── users.service.ts ├── test │ ├── app.e2e-spec.ts │ └── jest-e2e.json ├── tsconfig.build.json └── tsconfig.json ├── package-lock.json ├── package.json ├── renovate.json ├── server ├── .dockerignore ├── .env.example ├── Dockerfile ├── Dockerfile.lambda ├── eslint.config.mjs ├── jest.config.mjs ├── lambda │ └── app.js ├── package.json ├── public │ └── swagger.yml ├── src │ ├── app.ts │ ├── config.ts │ ├── dataSource.ts │ ├── dataSourceOptions.ts │ ├── index.ts │ ├── logger.ts │ ├── migrations │ │ ├── 1630340371992-UserMigration.ts │ │ ├── 1630341383942-TaskResult.ts │ │ ├── 1630342025950-StudentMigration.ts │ │ ├── 1630342266002-UserMigration.ts │ │ ├── 1630347897950-StudentMigration.ts │ │ ├── 1632333725126-ResumeMigration.ts │ │ ├── 1635365797478-User.ts │ │ ├── 1637591194886-StageInterview.ts │ │ ├── 1638302439645-CourseMigration.ts │ │ ├── 1639418471577-Indicies.ts │ │ ├── 1639427578702-Update.ts │ │ ├── 1639502600339-Student.ts │ │ ├── 1642884123347-ResumeSelectCourses.ts │ │ ├── 1643481312933-Task.ts │ │ ├── 1643550350939-LoginState.ts │ │ ├── 1643926895264-Notifications.ts │ │ ├── 1644695410918-NotificationConnection.ts │ │ ├── 1645364514538-RepositoryEvent.ts │ │ ├── 1645654601903-Opportunitites.ts │ │ ├── 1647103154082-CrossCheckScheduling.ts │ │ ├── 1647175301446-TaskSolutionConstraint.ts │ │ ├── 1647550751147-NotificationType.ts │ │ ├── 1647885219936-LoginStateUserId.ts │ │ ├── 1649505252996-CourseLogo.ts │ │ ├── 1649868994688-CourseLogo.ts │ │ ├── 1650652882300-DiscordChannel.ts │ │ ├── 1652870756742-Resume.ts │ │ ├── 1656326258991-History.ts │ │ ├── 1661034658479-Feedback.ts │ │ ├── 1661087975938-Discipline.ts │ │ ├── 1661106736439-Disciplines.ts │ │ ├── 1661107174477-Disciplines.ts │ │ ├── 1661616212488-NotificationCategory.ts │ │ ├── 1662275601017-CourseTask.ts │ │ ├── 1664183799115-CourseEvent.ts │ │ ├── 1666348642811-TaskCriteria.ts │ │ ├── 1666621080327-TaskSolutionResult.ts │ │ ├── 1671475396333-Tasks.ts │ │ ├── 1672142743107-TeamDistribution.ts │ │ ├── 1672386450861-TeamDistribution.ts │ │ ├── 1673090827105-TaskVerification.ts │ │ ├── 1673692838338-User.ts │ │ ├── 1674128274839-Team.ts │ │ ├── 1674377676805-TeamDistributionStudent.ts │ │ ├── 1674755854609-Resume.ts │ │ ├── 1675245424426-UserGroup.ts │ │ ├── 1675345245770-Course.ts │ │ ├── 1676139987317-User.ts │ │ ├── 1685197747051-MentorRegistry.ts │ │ ├── 1686657350908-InterviewScore.ts │ │ ├── 1687009744110-Prompt.ts │ │ ├── 1691520611773-Temperature.ts │ │ ├── 1691524327332-Temperature.ts │ │ ├── 1693930286280-CourseUsersActivist.ts │ │ ├── 1699808604000-AddMinStudentPerMentorColumnToCourse.ts │ │ ├── 1700391857109-Obfuscation.ts │ │ ├── 1712137476312-Course.ts │ │ ├── 1730926720293-CourseTask.ts │ │ ├── 1734874453585-Contributor.ts │ │ ├── 1736458672717-Course.ts │ │ ├── 1738250779923-CoursePersonalMentoringDates.ts │ │ ├── 1747380525126-CourseTaskInterviewCreatingPairs.ts │ │ └── index.ts │ ├── models │ │ ├── alert.ts │ │ ├── certificate.ts │ │ ├── contributor.ts │ │ ├── course.ts │ │ ├── courseEvent.ts │ │ ├── courseManager.ts │ │ ├── courseTask.ts │ │ ├── courseUser.ts │ │ ├── data │ │ │ ├── available-languages.data.ts │ │ │ ├── index.ts │ │ │ └── language-levels.data.ts │ │ ├── discipline.ts │ │ ├── discordServer.ts │ │ ├── event.ts │ │ ├── feedback.ts │ │ ├── history.ts │ │ ├── index.ts │ │ ├── loginState.ts │ │ ├── mentor.ts │ │ ├── mentorRegistry.ts │ │ ├── notification.ts │ │ ├── notificationChannel.ts │ │ ├── notificationChannelSettings.ts │ │ ├── notificationUserConnection.ts │ │ ├── notificationUserSettings.ts │ │ ├── privateFeedback.ts │ │ ├── profilePermissions.ts │ │ ├── prompt.ts │ │ ├── registry.ts │ │ ├── repositoryEvent.ts │ │ ├── resume.ts │ │ ├── session.ts │ │ ├── stageInterview.ts │ │ ├── stageInterviewFeedback.ts │ │ ├── stageInterviewStudent.ts │ │ ├── student-feedback.ts │ │ ├── student.ts │ │ ├── task.ts │ │ ├── taskArtefact.ts │ │ ├── taskChecker.ts │ │ ├── taskCriteria.ts │ │ ├── taskInterviewResult.ts │ │ ├── taskInterviewStudent.ts │ │ ├── taskResult.ts │ │ ├── taskSolution.ts │ │ ├── taskSolutionChecker.ts │ │ ├── taskSolutionResult.ts │ │ ├── taskVerification.ts │ │ ├── team.ts │ │ ├── teamDistribution.ts │ │ ├── teamDistributionStudent.ts │ │ ├── user.ts │ │ └── userGroup.ts │ ├── repositories │ │ ├── courseTask.repository.ts │ │ ├── crossCheck.repository.ts │ │ ├── feedback.repository.ts │ │ ├── interview.repository.ts │ │ ├── mentor.repository.ts │ │ ├── mentorRegistry.repository.ts │ │ ├── repositoryEvent.repository.ts │ │ ├── stageInterview.repository.ts │ │ ├── stageInterviewFeedback.repository.ts │ │ ├── student.repository.ts │ │ └── user.repository.ts │ ├── reset.d.ts │ ├── routes │ │ ├── checks │ │ │ ├── getBadComment.ts │ │ │ ├── getMaxScoreCheckers.ts │ │ │ └── index.ts │ │ ├── common.ts │ │ ├── course │ │ │ ├── certificates.ts │ │ │ ├── crossCheck │ │ │ │ ├── createCompletion.ts │ │ │ │ ├── createDistribution.ts │ │ │ │ ├── createMessage.ts │ │ │ │ ├── createResult.ts │ │ │ │ ├── createSolution.ts │ │ │ │ ├── deleteSolution.ts │ │ │ │ ├── getAssignments.ts │ │ │ │ ├── getResult.ts │ │ │ │ ├── getSolution.ts │ │ │ │ ├── getTaskDetails.ts │ │ │ │ ├── index.ts │ │ │ │ └── updateMessage.ts │ │ │ ├── events.ts │ │ │ ├── index.ts │ │ │ ├── interviews.ts │ │ │ ├── mentor.ts │ │ │ ├── repository.ts │ │ │ ├── schedule.ts │ │ │ ├── score │ │ │ │ ├── createMultipleScores.ts │ │ │ │ ├── createSingleScore.ts │ │ │ │ ├── getScoreByStudent.ts │ │ │ │ ├── getScoreCsv.ts │ │ │ │ ├── index.ts │ │ │ │ └── recalculateScore.ts │ │ │ ├── stageInterview │ │ │ │ ├── cancelInterview.ts │ │ │ │ ├── createFeedback.ts │ │ │ │ ├── createInterview.ts │ │ │ │ ├── createInterviews.ts │ │ │ │ ├── getFeedback.ts │ │ │ │ ├── getInterviewStudent.ts │ │ │ │ ├── getInterviewerStudents.ts │ │ │ │ ├── getInterviews.ts │ │ │ │ ├── index.ts │ │ │ │ └── updateInterview.ts │ │ │ ├── student.ts │ │ │ ├── students.ts │ │ │ ├── taskArtefact.ts │ │ │ ├── taskVerifications.ts │ │ │ └── tasks │ │ │ │ ├── createCourseTaskDistribution.ts │ │ │ │ ├── getCourseTasksDetails.ts │ │ │ │ └── index.ts │ │ ├── feedback.ts │ │ ├── file │ │ │ ├── index.ts │ │ │ └── upload.ts │ │ ├── guards.ts │ │ ├── index.ts │ │ ├── logging.ts │ │ ├── me.ts │ │ ├── middlewares.ts │ │ ├── profile │ │ │ ├── __test__ │ │ │ │ └── permissions.test.ts │ │ │ ├── index.ts │ │ │ ├── info.ts │ │ │ ├── me.ts │ │ │ ├── mentor-stats.ts │ │ │ ├── permissions.ts │ │ │ ├── public-feedback.ts │ │ │ ├── stage-interview-feedback.ts │ │ │ ├── student-stats.ts │ │ │ └── user-info.ts │ │ ├── registry │ │ │ └── index.ts │ │ ├── repository │ │ │ ├── events.ts │ │ │ └── index.ts │ │ ├── task │ │ │ └── index.ts │ │ ├── taskVerification │ │ │ └── index.ts │ │ ├── tasks │ │ │ └── index.ts │ │ ├── users │ │ │ └── index.ts │ │ ├── utils.ts │ │ └── validators.ts │ ├── rules │ │ ├── __tests__ │ │ │ └── mentors.test.ts │ │ ├── index.ts │ │ ├── interviews.ts │ │ ├── mentors.ts │ │ ├── name.ts │ │ └── types.ts │ ├── schedule.ts │ └── services │ │ ├── aws.service.ts │ │ ├── check.service.ts │ │ ├── course.service.ts │ │ ├── crossCheck.service.ts │ │ ├── distribution │ │ ├── __tests__ │ │ │ ├── crossCheck.test.ts │ │ │ ├── crossMentor.test.ts │ │ │ └── shuffle.test.ts │ │ ├── crossCheckDistribution.service.ts │ │ ├── crossMentorDistribution.service.ts │ │ ├── index.ts │ │ └── shuffle.ts │ │ ├── github.service.ts │ │ ├── index.ts │ │ ├── interview.service.ts │ │ ├── notification.service.ts │ │ ├── operationResult.ts │ │ ├── repository.service.ts │ │ ├── score │ │ ├── index.ts │ │ └── score.service.ts │ │ ├── stageInterview.service.ts │ │ ├── student.service.ts │ │ ├── taskResults.service.ts │ │ ├── taskVerification.service.ts │ │ ├── tasks.service.ts │ │ └── user.service.ts ├── swaggerDef.js └── tsconfig.json ├── setup ├── backup-local.sql ├── cdk │ ├── App.ts │ ├── DockerFunctionConstruct.ts │ ├── Stack.ts │ ├── cdk.json │ ├── package-lock.json │ └── package.json ├── docker-compose.yml ├── dump-local.sh ├── dump.sh ├── nginx │ └── nginx.conf ├── restore-local.sh └── restore.sh ├── tools ├── bumblebee │ ├── .env.example │ ├── .gitignore │ ├── Dockerfile │ ├── LICENSE │ ├── README.md │ ├── docker-compose.yml │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── constants │ │ ├── defaults.js │ │ └── discord-bridge.js │ │ ├── discord │ │ ├── discord-bridge.js │ │ ├── events │ │ │ ├── index.js │ │ │ ├── message-delete.js │ │ │ ├── message-update.js │ │ │ ├── message.js │ │ │ └── ready.js │ │ ├── filters.js │ │ ├── index.js │ │ ├── time-logger.js │ │ └── utils.js │ │ ├── index.js │ │ ├── lib │ │ └── common.js │ │ └── telegram │ │ ├── events │ │ ├── help.js │ │ ├── index.js │ │ ├── ping.js │ │ └── start.js │ │ ├── index.js │ │ └── telegram-bot.js └── sloths │ ├── .env.example │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── .prettierrc │ ├── README.md │ ├── env.d.ts │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── cdn │ │ ├── cleaned │ │ │ ├── codewars.svg │ │ │ ├── congrats.svg │ │ │ ├── deadline.svg │ │ │ └── filelist.json │ │ └── stickers │ │ │ ├── activist │ │ │ ├── image.svg │ │ │ └── metadata.json │ │ │ ├── codewars │ │ │ ├── image.svg │ │ │ └── metadata.json │ │ │ ├── congrats │ │ │ ├── image.svg │ │ │ └── metadata.json │ │ │ └── metadata.json │ ├── favicon │ │ ├── favicon-32x32.png │ │ └── favicon.ico │ └── img │ │ ├── catalog │ │ ├── download-dark.svg │ │ └── download-light.svg │ │ ├── guess │ │ ├── bg.svg │ │ ├── painted │ │ │ ├── 01.svg │ │ │ ├── 02.svg │ │ │ ├── 03.svg │ │ │ ├── 04.svg │ │ │ ├── 05.svg │ │ │ ├── 06.svg │ │ │ ├── 07.svg │ │ │ ├── 08.svg │ │ │ ├── 09.svg │ │ │ ├── 10.svg │ │ │ ├── 11.svg │ │ │ ├── 12.svg │ │ │ ├── 13.svg │ │ │ ├── 14.svg │ │ │ ├── 15.svg │ │ │ ├── 16.svg │ │ │ ├── 17.svg │ │ │ ├── 18.svg │ │ │ ├── 19.svg │ │ │ ├── 20.svg │ │ │ ├── 21.svg │ │ │ ├── 22.svg │ │ │ ├── 23.svg │ │ │ ├── 24.svg │ │ │ ├── 25.svg │ │ │ ├── 26.svg │ │ │ ├── 27.svg │ │ │ ├── 28.svg │ │ │ ├── 29.svg │ │ │ ├── 30.svg │ │ │ ├── 31.svg │ │ │ ├── 32.svg │ │ │ ├── 33.svg │ │ │ ├── 34.svg │ │ │ ├── 35.svg │ │ │ ├── 36.svg │ │ │ ├── 37.svg │ │ │ ├── 38.svg │ │ │ ├── 39.svg │ │ │ └── 40.svg │ │ └── winner2.svg │ │ ├── home │ │ ├── catalog-down.svg │ │ ├── catalog-up.svg │ │ ├── create.svg │ │ ├── guess.svg │ │ ├── memory.svg │ │ ├── merch.svg │ │ └── sloth.svg │ │ ├── logo.svg │ │ ├── memory │ │ ├── memory-level-junior.svg │ │ ├── memory-level-middle.svg │ │ ├── memory-level-senior.svg │ │ ├── memory-sprite.svg │ │ ├── reload-dark.svg │ │ ├── reload-light.svg │ │ └── winner1.svg │ │ ├── merch │ │ ├── hoodie.png │ │ ├── merch-cleaned.svg │ │ ├── merch-original.svg │ │ ├── mug.png │ │ ├── thermo.png │ │ └── tshirt.png │ │ ├── photo │ │ ├── ogimly.png │ │ ├── vanet.png │ │ └── wiijoy.png │ │ ├── preview.svg │ │ └── team │ │ └── wwt.svg │ ├── src │ ├── App.vue │ ├── assets │ │ ├── backgrounds │ │ │ ├── bg-404-dark.svg │ │ │ ├── bg-404-light.svg │ │ │ ├── bg-about-dark.svg │ │ │ ├── bg-about-light.svg │ │ │ ├── bg-catalog-dark.svg │ │ │ ├── bg-catalog-light.svg │ │ │ ├── bg-create-dark.svg │ │ │ ├── bg-create-light.svg │ │ │ ├── bg-footer-dark.svg │ │ │ ├── bg-footer-light.svg │ │ │ ├── bg-guess-dark.svg │ │ │ ├── bg-guess-light.svg │ │ │ ├── bg-home-dark.svg │ │ │ ├── bg-home-light.svg │ │ │ ├── bg-memory-dark.svg │ │ │ ├── bg-memory-light.svg │ │ │ ├── bg-merch-dark.svg │ │ │ ├── bg-merch-light.svg │ │ │ ├── bg-stars-dark.svg │ │ │ └── bg-stars-light.svg │ │ ├── fonts │ │ │ ├── Arial-Black.woff │ │ │ ├── Arial-Black.woff2 │ │ │ ├── NunitoSans-Black.woff │ │ │ ├── NunitoSans-Black.woff2 │ │ │ ├── NunitoSans-Bold.woff │ │ │ ├── NunitoSans-Bold.woff2 │ │ │ ├── NunitoSans-Light.woff │ │ │ ├── NunitoSans-Light.woff2 │ │ │ ├── NunitoSans-Regular.woff │ │ │ └── NunitoSans-Regular.woff2 │ │ ├── icons │ │ │ ├── 404 │ │ │ │ └── 404.svg │ │ │ ├── btn │ │ │ │ ├── center-square-black.svg │ │ │ │ ├── center-square.svg │ │ │ │ ├── check-circle-fill.svg │ │ │ │ ├── check-circle.svg │ │ │ │ ├── check-square-black.svg │ │ │ │ ├── check-square.svg │ │ │ │ ├── clipboard-black.svg │ │ │ │ ├── clipboard.svg │ │ │ │ ├── dash-square-black.svg │ │ │ │ ├── dash-square.svg │ │ │ │ ├── download-black.svg │ │ │ │ ├── download.svg │ │ │ │ ├── plus-square-black.svg │ │ │ │ └── plus-square.svg │ │ │ ├── loader │ │ │ │ └── loader-point.svg │ │ │ ├── rs-school.png │ │ │ ├── sounds │ │ │ │ ├── sound-dark-off.svg │ │ │ │ ├── sound-dark-on.svg │ │ │ │ ├── sound-light-off.svg │ │ │ │ └── sound-light-on.svg │ │ │ └── themes │ │ │ │ ├── dark.svg │ │ │ │ └── light.svg │ │ ├── locales │ │ │ └── en.json │ │ ├── sounds │ │ │ ├── fail.mp3 │ │ │ ├── flip.mp3 │ │ │ ├── ovation.mp3 │ │ │ ├── sad-trombone.mp3 │ │ │ ├── slide.mp3 │ │ │ ├── success.mp3 │ │ │ └── ta-da.mp3 │ │ └── styles │ │ │ ├── base.css │ │ │ ├── fonts.css │ │ │ ├── main.css │ │ │ └── variables.css │ ├── common │ │ ├── const.ts │ │ └── types.ts │ ├── components │ │ ├── about │ │ │ ├── AboutSection.vue │ │ │ └── AboutTeammate.vue │ │ ├── background │ │ │ └── BackgroundView.vue │ │ ├── buttons │ │ │ ├── CustomBtn.vue │ │ │ ├── IconBtn.vue │ │ │ └── ImageBtn.vue │ │ ├── catalog │ │ │ ├── SlothCard.vue │ │ │ └── SlothInfo.vue │ │ ├── controls-list │ │ │ ├── ControlsList.vue │ │ │ ├── PaginationList.vue │ │ │ ├── SearchText.vue │ │ │ ├── SortingList.vue │ │ │ └── TagCloud.vue │ │ ├── footer │ │ │ └── FooterView.vue │ │ ├── guess │ │ │ └── GuessInfo.vue │ │ ├── header │ │ │ └── HeaderView.vue │ │ ├── home │ │ │ ├── HomeAbout.vue │ │ │ ├── HomeCatalog.vue │ │ │ └── HomeCategory.vue │ │ ├── loader │ │ │ └── LoaderView.vue │ │ ├── memory │ │ │ ├── GameField.vue │ │ │ └── MemoryInfo.vue │ │ ├── mixins │ │ │ └── sort-mixin.ts │ │ ├── modal │ │ │ ├── AlertModal.vue │ │ │ └── ModalWindow.vue │ │ └── switchers │ │ │ ├── SoundSwitcher.vue │ │ │ └── ThemeSwitcher.vue │ ├── i18n.ts │ ├── main.ts │ ├── router │ │ └── index.ts │ ├── services │ │ ├── error-handler.ts │ │ └── sloths-service.ts │ ├── shims-vue.d.ts │ ├── stores │ │ ├── alert-modal.ts │ │ ├── audio-on.ts │ │ ├── cleaned.ts │ │ ├── counter.ts │ │ ├── loader.ts │ │ ├── pages-store.ts │ │ ├── pagination.ts │ │ ├── search-text.ts │ │ ├── sloth-info.ts │ │ ├── sloths.ts │ │ ├── sorting-list.ts │ │ ├── tag-cloud.ts │ │ └── theme.ts │ ├── utils │ │ ├── audio.ts │ │ ├── canvas-utils.ts │ │ ├── game-utils.ts │ │ └── userTheme.ts │ └── views │ │ ├── 404.vue │ │ ├── About.vue │ │ ├── Catalog.vue │ │ ├── Create.vue │ │ ├── Guess.vue │ │ ├── Home.vue │ │ ├── Memory.vue │ │ └── Merch.vue │ ├── tsconfig.config.json │ ├── tsconfig.json │ └── vite.config.ts └── turbo.json /.dockerignore: -------------------------------------------------------------------------------- 1 | client/.next/cache 2 | client/.next/static 3 | client/.turbo 4 | node_modules 5 | npm-debug.log 6 | .turbo 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | insert_final_newline = false 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # don't ever lint node_modules 2 | node_modules 3 | # don't lint build output (make sure it's set to your correct build folder name) 4 | dist -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/data-issue-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🗃 Data issue 3 | about: Create a issue to fix data 4 | title: '' 5 | labels: data issue 6 | --- 7 | 8 | **Course** 9 | Specify your course (e.g. rs-2019-q1) 10 | 11 | **What is wrong** 12 | Describe data issue 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 Feature request 3 | about: Request a feature to implement 4 | title: '' 5 | labels: feature 6 | --- 7 | 8 | **Describe the feature** 9 | Please describe the requested feature in details. 10 | 11 | **Additional context** 12 | Add any other context about the feature here. 13 | -------------------------------------------------------------------------------- /.github/auto-label.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "code:client": ["client/"], 4 | "code:server": ["server/"], 5 | "⛓ dependencies": "**/package.json" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | [Pull Request Guidelines](https://github.com/rolling-scopes/rsschool-app/blob/master/CONTRIBUTING.md#pull-requests) 2 | 3 | **Issue**: 4 | _Link to the relevant GitHub Issue (e.g., `#123` for issue number 123)._ 5 | 6 | **Description**: 7 | _Please provide a description of the changes in this Pull Request. Include screenshots or GIFs if applicable. The description should clearly explain the purpose of this PR._ 8 | 9 | **Self-Check**: 10 | 11 | - [ ] Database migration added (if required) 12 | - [ ] Changes tested locally 13 | -------------------------------------------------------------------------------- /.github/workflows/renovate.yml: -------------------------------------------------------------------------------- 1 | name: Renovate 2 | on: 3 | schedule: 4 | # once a month 5 | - cron: '0 0 1 * *' 6 | workflow_dispatch: 7 | jobs: 8 | renovate: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v4 13 | - name: Self-hosted Renovate 14 | uses: renovatebot/github-action@v34.82.0 15 | with: 16 | configurationFile: renovate.json 17 | token: ${{ secrets.RENOVATE_TOKEN }} 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .env 3 | .history/ 4 | .idea/ 5 | .next/ 6 | .sentryclirc 7 | .swc/ 8 | .turbo 9 | .vscode 10 | app/ 11 | coverage 12 | dist/ 13 | node_modules/ 14 | out 15 | playwright-report/ 16 | reports/*.xml 17 | setup/backup.sql 18 | setup/cdk/cdk.out 19 | test-results/ 20 | client/tsconfig.tsbuildinfo 21 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | client/src/.next 2 | client/.next 3 | client/dist 4 | server/dist 5 | nestjs/dist 6 | client/src/api/ 7 | client/playwright-report/ 8 | package-lock.json 9 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "tabWidth": 2, 4 | "semi": true, 5 | "singleQuote": true, 6 | "printWidth": 120, 7 | "arrowParens": "avoid" 8 | } 9 | -------------------------------------------------------------------------------- /client/.dockerignore: -------------------------------------------------------------------------------- 1 | .next/cache 2 | .next/static 3 | node_modules 4 | .turbo 5 | -------------------------------------------------------------------------------- /client/.env.example: -------------------------------------------------------------------------------- 1 | RSSHCOOL_UI_GCP_MAPS_API_KEY= 2 | -------------------------------------------------------------------------------- /client/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:20-alpine 2 | 3 | EXPOSE 8080 4 | 5 | ENV NODE_ENV production 6 | ENV NODE_PORT 8080 7 | 8 | WORKDIR /app 9 | 10 | COPY client/next.config.mjs /app/client/next.config.mjs 11 | COPY client/next.config.prod.mjs /app/client/next.config.prod.mjs 12 | COPY client/package.json /app/client/package.json 13 | COPY client/public /app/client/public 14 | 15 | COPY package.json /app 16 | COPY package-lock.json /app 17 | 18 | RUN npm ci --production --no-optional 19 | 20 | COPY client/.next /app/client/.next 21 | 22 | CMD cd /app/client && npm run prod 23 | -------------------------------------------------------------------------------- /client/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import testingLibrary from 'eslint-plugin-testing-library'; 2 | import defaultConfig from '../eslint.config.mjs'; 3 | 4 | export default [ 5 | ...defaultConfig, 6 | { 7 | ...testingLibrary.configs['flat/react'], 8 | files: ['**/__tests__/**/*.ts?(x)', '**/?(*.)+(spec|test).ts?(x)'], 9 | }, 10 | ]; 11 | -------------------------------------------------------------------------------- /client/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /client/next.config.prod.mjs: -------------------------------------------------------------------------------- 1 | const isProd = process.env.NODE_ENV === 'production'; 2 | 3 | const nextConfig = { 4 | serverRuntimeConfig: { 5 | rsHost: process.env.RS_HOST || 'http://localhost:3000', 6 | }, 7 | assetPrefix: isProd ? 'https://cdn.rs.school' : '', 8 | env: { 9 | BUILD_VERSION: process.env.BUILD_VERSION || '0.0.0.0.0', 10 | APP_VERSION: process.env.APP_VERSION, 11 | RSSHCOOL_UI_GCP_MAPS_API_KEY: process.env.RSSHCOOL_UI_GCP_MAPS_API_KEY, 12 | CDN_HOST: process.env.CDN_HOST || '', 13 | }, 14 | }; 15 | 16 | export default nextConfig; 17 | -------------------------------------------------------------------------------- /client/public/static/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/client/public/static/empty.txt -------------------------------------------------------------------------------- /client/public/static/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/client/public/static/images/favicon.ico -------------------------------------------------------------------------------- /client/public/static/images/job/dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/client/public/static/images/job/dot.png -------------------------------------------------------------------------------- /client/public/static/images/logo-rsschool2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/client/public/static/images/logo-rsschool2.png -------------------------------------------------------------------------------- /client/public/static/images/logo-rsschool3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/client/public/static/images/logo-rsschool3.png -------------------------------------------------------------------------------- /client/public/static/images/logo_rs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/client/public/static/images/logo_rs.png -------------------------------------------------------------------------------- /client/public/static/images/rs-hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/client/public/static/images/rs-hero.png -------------------------------------------------------------------------------- /client/public/static/images/united-kingdom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/client/public/static/images/united-kingdom.png -------------------------------------------------------------------------------- /client/src/__mocks__/axios.js: -------------------------------------------------------------------------------- 1 | import mockAxios from 'jest-mock-axios'; 2 | 3 | export default mockAxios; 4 | -------------------------------------------------------------------------------- /client/src/api/.gitignore: -------------------------------------------------------------------------------- 1 | wwwroot/*.js 2 | node_modules 3 | typings 4 | dist 5 | -------------------------------------------------------------------------------- /client/src/api/.npmignore: -------------------------------------------------------------------------------- 1 | # empty npmignore to ensure all required files (e.g., in the dist folder) are published by npm -------------------------------------------------------------------------------- /client/src/api/.openapi-generator/FILES: -------------------------------------------------------------------------------- 1 | .gitignore 2 | .npmignore 3 | api.ts 4 | base.ts 5 | common.ts 6 | configuration.ts 7 | git_push.sh 8 | index.ts 9 | -------------------------------------------------------------------------------- /client/src/api/.openapi-generator/VERSION: -------------------------------------------------------------------------------- 1 | 5.4.0 -------------------------------------------------------------------------------- /client/src/api/index.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * 5 | * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) 6 | * 7 | * The version of the OpenAPI document: 1.0.0 8 | * 9 | * 10 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). 11 | * https://openapi-generator.tech 12 | * Do not edit the class manually. 13 | */ 14 | 15 | 16 | export * from "./api"; 17 | export * from "./configuration"; 18 | 19 | -------------------------------------------------------------------------------- /client/src/components/CountBadge/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as CountBadge } from './CountBadge'; 2 | -------------------------------------------------------------------------------- /client/src/components/Footer/index.tsx: -------------------------------------------------------------------------------- 1 | export { FooterLayout } from './FooterLayout'; 2 | -------------------------------------------------------------------------------- /client/src/components/Forms/CommentInput.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Input, Form } from 'antd'; 3 | 4 | export function CommentInput(props: { [key: string]: any; notRequired?: boolean }) { 5 | const { notRequired, ...otherProps } = props; 6 | return ( 7 | 13 | 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /client/src/components/Forms/index.ts: -------------------------------------------------------------------------------- 1 | export * from './GdprCheckbox'; 2 | export * from './ScoreInput'; 3 | export * from './CommentInput'; 4 | export * from './CourseTaskSelect'; 5 | export * from './ModalForm'; 6 | export * from './LocationSelect'; 7 | -------------------------------------------------------------------------------- /client/src/components/GithubAvatar.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Avatar } from 'antd'; 3 | import { CDN_AVATARS_URL } from 'configs/cdn'; 4 | 5 | type Props = { 6 | githubId?: string; 7 | size: 24 | 32 | 48 | 96; 8 | style?: React.CSSProperties; 9 | alt?: string; 10 | }; 11 | 12 | export function GithubAvatar({ githubId, size, style }: Props) { 13 | if (!githubId || githubId.startsWith('gdpr-')) { 14 | return ; 15 | } 16 | return ; 17 | } 18 | -------------------------------------------------------------------------------- /client/src/components/Icons/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './CourseIcon'; 2 | export * from './DeadlineIcon'; 3 | export * from './DiscordFilled'; 4 | export * from './DiscordOutlined'; 5 | export * from './HealthMask'; 6 | export * from './LinkedInIcon'; 7 | export * from './ScoreIcon'; 8 | export * from './TelegramIcon'; 9 | export * from './GitHubLogoIcon'; 10 | export * from './RSLogoIcon'; 11 | -------------------------------------------------------------------------------- /client/src/components/Profile/__test__/__snapshots__/CoreJsIviewsModal.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`CoreJsIviewsModal Should render correctly 1`] = `
`; 4 | -------------------------------------------------------------------------------- /client/src/components/Profile/__test__/__snapshots__/MentorStatsModal.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`MentorStatsModal Should render correctly 1`] = `
`; 4 | -------------------------------------------------------------------------------- /client/src/components/Profile/__test__/__snapshots__/PreScreeningIviewModal.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`PreScreeningIviewModal Should render correctly 1`] = `
`; 4 | -------------------------------------------------------------------------------- /client/src/components/Profile/__test__/__snapshots__/PublicFeedbackModal.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`PublicFeedbackModal Should render correctly 1`] = `
`; 4 | -------------------------------------------------------------------------------- /client/src/components/Profile/__test__/__snapshots__/StudentStatsModal.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`StudentStatsModal Should render correctly 1`] = `
`; 4 | -------------------------------------------------------------------------------- /client/src/components/SolidarityUkraine.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image'; 2 | 3 | export function SolidarityUkraine() { 4 | return Stand With Ukraine; 5 | } 6 | -------------------------------------------------------------------------------- /client/src/components/Student/index.ts: -------------------------------------------------------------------------------- 1 | export { AssignStudentModal } from './AssignStudentModal'; 2 | export { DashboardDetails } from './DashboardDetails'; 3 | -------------------------------------------------------------------------------- /client/src/components/Table/index.ts: -------------------------------------------------------------------------------- 1 | export * from './renderers'; 2 | export * from './sorters'; 3 | export * from './columns'; 4 | export { PersonCell } from './PersonCell'; 5 | -------------------------------------------------------------------------------- /client/src/components/common/CustomPopconfirm.tsx: -------------------------------------------------------------------------------- 1 | import { Popconfirm, PopconfirmProps } from 'antd'; 2 | 3 | export const CustomPopconfirm = ({ placement, ...props }: PopconfirmProps) => { 4 | return ; 5 | }; 6 | -------------------------------------------------------------------------------- /client/src/components/withSession.tsx: -------------------------------------------------------------------------------- 1 | import { CourseRole } from 'services/models'; 2 | 3 | export interface CourseInfo { 4 | mentorId?: number; 5 | studentId?: number; 6 | roles: CourseRole[]; 7 | isExpelled?: boolean; 8 | } 9 | 10 | export interface Session { 11 | id: number; 12 | githubId: string; 13 | isAdmin: boolean; 14 | isHirer: boolean; 15 | courses: { [courseId: string]: CourseInfo | undefined }; 16 | } 17 | -------------------------------------------------------------------------------- /client/src/configs/cdn.ts: -------------------------------------------------------------------------------- 1 | export const CDN_AVATARS_URL = 'https://cdn.rs.school/avatars'; 2 | -------------------------------------------------------------------------------- /client/src/configs/discord-integration.ts: -------------------------------------------------------------------------------- 1 | const isDevMode = process.env.NODE_ENV !== 'production'; 2 | const clientId = isDevMode ? '625945676009963521' : '978920245743976448'; 3 | const redirectUrl = isDevMode ? 'http://localhost:3000/profile' : 'https://app.rs.school/profile'; 4 | 5 | export default { 6 | api: { 7 | auth: `https://discord.com/api/oauth2/authorize?client_id=${clientId}&redirect_uri=${encodeURIComponent( 8 | redirectUrl, 9 | )}&response_type=token&scope=identify`, 10 | me: 'https://discordapp.com/api/users/@me', 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /client/src/configs/gcp.ts: -------------------------------------------------------------------------------- 1 | export const mapsApiKey = process.env.RSSHCOOL_UI_GCP_MAPS_API_KEY; 2 | -------------------------------------------------------------------------------- /client/src/configs/registry.ts: -------------------------------------------------------------------------------- 1 | import { Session } from 'components/withSession'; 2 | import { Course } from 'services/models'; 3 | 4 | export const TYPES = { 5 | MENTOR: 'mentor', 6 | STUDENT: 'student', 7 | }; 8 | 9 | export type Props = { 10 | courses?: Course[]; 11 | session: Session; 12 | }; 13 | -------------------------------------------------------------------------------- /client/src/data/english.ts: -------------------------------------------------------------------------------- 1 | export const ENGLISH_LEVELS = ['A0', 'A1', 'A1+', 'A2', 'A2+', 'B1', 'B1+', 'B2', 'B2+', 'C1', 'C1+', 'C2'] as const; 2 | -------------------------------------------------------------------------------- /client/src/data/index.ts: -------------------------------------------------------------------------------- 1 | import { EVENT_TYPES_MAP } from './eventTypes'; 2 | import { TASK_TYPES_MAP } from './taskTypes'; 3 | 4 | export const TASK_EVENT_TYPES_MAP = { 5 | ...TASK_TYPES_MAP, 6 | ...EVENT_TYPES_MAP, 7 | }; 8 | -------------------------------------------------------------------------------- /client/src/data/skills.ts: -------------------------------------------------------------------------------- 1 | export const SKILLS = [ 2 | 'react', 3 | 'angular', 4 | 'unit-tests', 5 | 'jest', 6 | 'javascript-core', 7 | 'redux', 8 | 'html', 9 | 'css', 10 | 'scss', 11 | 'less', 12 | 'nestjs', 13 | ]; 14 | -------------------------------------------------------------------------------- /client/src/data/tshirts.ts: -------------------------------------------------------------------------------- 1 | export const TSHIRT_SIZES = [ 2 | { 3 | id: 'xs', 4 | name: 'XS', 5 | }, 6 | { 7 | id: 's', 8 | name: 'S', 9 | }, 10 | { 11 | id: 'm', 12 | name: 'M', 13 | }, 14 | { 15 | id: 'l', 16 | name: 'L', 17 | }, 18 | { 19 | id: 'xl', 20 | name: 'XL', 21 | }, 22 | { 23 | id: 'xxl', 24 | name: 'XXL', 25 | }, 26 | { 27 | id: 'xxxl', 28 | name: 'XXXL', 29 | }, 30 | ]; 31 | -------------------------------------------------------------------------------- /client/src/domain/course.test.ts: -------------------------------------------------------------------------------- 1 | import { CourseTaskDto } from 'api'; 2 | import { getTasksTotalScore } from './course'; 3 | 4 | describe('getTasksTotalScore', () => { 5 | test('should calculate total score', () => { 6 | expect( 7 | getTasksTotalScore([ 8 | { 9 | maxScore: 100, 10 | scoreWeight: 1, 11 | }, 12 | { 13 | maxScore: 100, 14 | scoreWeight: 0.5, 15 | }, 16 | ] as CourseTaskDto[]), 17 | ).toBe(100 + 50); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /client/src/domain/course.ts: -------------------------------------------------------------------------------- 1 | import { CourseTaskDto } from 'api'; 2 | 3 | export function getTasksTotalScore(courseTasks: CourseTaskDto[]) { 4 | return courseTasks.reduce((score, task) => score + (task.maxScore ?? 0) * task.scoreWeight, 0); 5 | } 6 | -------------------------------------------------------------------------------- /client/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/client/src/favicon.ico -------------------------------------------------------------------------------- /client/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export { useModalForm } from './useModal/useModalForm'; 2 | export type { ModalFormMode } from './useModal/useModalForm'; 3 | -------------------------------------------------------------------------------- /client/src/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'mq-polyfill'; 2 | -------------------------------------------------------------------------------- /client/src/modules/AutoTest/components/TaskCardColumn/TaskCardColumn.tsx: -------------------------------------------------------------------------------- 1 | import { Space, Typography } from 'antd'; 2 | import { ReactNode } from 'react'; 3 | 4 | type TaskCardColumnProps = { 5 | label: string; 6 | value: ReactNode; 7 | }; 8 | 9 | const { Text } = Typography; 10 | 11 | function TaskCardColumn({ label, value }: TaskCardColumnProps) { 12 | return ( 13 | 14 | {label} 15 | {value} 16 | 17 | ); 18 | } 19 | 20 | export default TaskCardColumn; 21 | -------------------------------------------------------------------------------- /client/src/modules/AutoTest/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export { useAttemptsMessage } from './useAttemptsMessage/useAttemptsMessage'; 2 | export { useCourseTaskSubmit } from './useCourseTaskSubmit/useCourseTaskSubmit'; 3 | export { useCourseTaskVerifications } from './useCourseTaskVerifications/useCourseTaskVerifications'; 4 | export { useVerificationsAnswers } from './useVerificationsAnswers/useVerificationsAnswers'; 5 | -------------------------------------------------------------------------------- /client/src/modules/AutoTest/pages/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as AutoTests } from './AutoTests/AutoTests'; 2 | export { default as Task, type AutoTestTaskProps } from './Task/Task'; 3 | -------------------------------------------------------------------------------- /client/src/modules/Course/components/NoSubmissionAvailable/index.tsx: -------------------------------------------------------------------------------- 1 | import { Typography } from 'antd'; 2 | import Link from 'next/link'; 3 | 4 | export function NoSubmissionAvailable({ courseAlias }: { courseAlias: string }) { 5 | return ( 6 | <> 7 | No tasks available for submission now 8 | Check start dates in Schedule 9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /client/src/modules/Course/contexts/index.ts: -------------------------------------------------------------------------------- 1 | export * from './SessionContext'; 2 | export * from './ActiveCourseContext'; 3 | -------------------------------------------------------------------------------- /client/src/modules/Course/pages/CouseNoAccess/index.tsx: -------------------------------------------------------------------------------- 1 | import { CourseNoAccess } from '../../components/CourseNoAccess'; 2 | import React from 'react'; 3 | 4 | export function CouseNoAccessPage() { 5 | return ; 6 | } 7 | -------------------------------------------------------------------------------- /client/src/modules/CourseManagement/components/index.ts: -------------------------------------------------------------------------------- 1 | export { SelectCourseTasks } from './SelectCourseTasks/SelectCourseTasks'; 2 | export { CertificateCriteriaModal } from './CertificateCriteriaModal/CertificateCriteriaModal'; 3 | export { ExpelCriteriaModal } from './ExpelCriteriaModal/ExpelCriteriaModal'; 4 | -------------------------------------------------------------------------------- /client/src/modules/CourseStatistics/components/EpamMentorsStatsCard/index.tsx: -------------------------------------------------------------------------------- 1 | export { EpamMentorsStatsCard } from './EpamMentorsStatsCard'; 2 | -------------------------------------------------------------------------------- /client/src/modules/CourseStatistics/components/MentorsCountriesCard/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/client/src/modules/CourseStatistics/components/MentorsCountriesCard/index.ts -------------------------------------------------------------------------------- /client/src/modules/CourseStatistics/components/StudentsCertificatesCountriesCard/index.tsx: -------------------------------------------------------------------------------- 1 | export { StudentsCertificatesCountriesCard } from './StudentsCertificatesCountriesCard'; 2 | -------------------------------------------------------------------------------- /client/src/modules/CourseStatistics/components/StudentsCountriesCard/index.tsx: -------------------------------------------------------------------------------- 1 | export { StudentsCountriesCard } from './StudentsCountriesCard'; 2 | -------------------------------------------------------------------------------- /client/src/modules/CourseStatistics/components/StudentsEligibleForCertificationCard/index.tsx: -------------------------------------------------------------------------------- 1 | export { StudentsEligibleForCertificationCard } from './StudentsEligibleForCertificationCard'; 2 | -------------------------------------------------------------------------------- /client/src/modules/CourseStatistics/components/StudentsStatsCard/index.tsx: -------------------------------------------------------------------------------- 1 | export { StudentsStatsCard } from './StudentsStatsCard'; 2 | -------------------------------------------------------------------------------- /client/src/modules/CourseStatistics/components/StudentsWithCertificateCard/index.tsx: -------------------------------------------------------------------------------- 1 | export { StudentsWithCertificateCard } from './StudentsWithCertificateCard'; 2 | -------------------------------------------------------------------------------- /client/src/modules/CourseStatistics/components/StudentsWithMentorsCard/index.tsx: -------------------------------------------------------------------------------- 1 | export { StudentsWithMentorsCard } from './StudentsWithMentorsCard'; 2 | -------------------------------------------------------------------------------- /client/src/modules/CourseStatistics/components/TaskPerformanceCard/index.tsx: -------------------------------------------------------------------------------- 1 | export { TaskPerformanceCard } from './TaskPerformanceCard'; 2 | -------------------------------------------------------------------------------- /client/src/modules/CourseStatistics/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export { useCourseStats } from './useCourseStats/useCourseStats'; 2 | -------------------------------------------------------------------------------- /client/src/modules/CourseStatistics/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as CourseStatistics } from './pages/CourseStatistics'; 2 | -------------------------------------------------------------------------------- /client/src/modules/CrossCheck/components/SolutionReview/Message/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Message } from './Message'; 2 | export { type MessageProps } from './Message'; 3 | -------------------------------------------------------------------------------- /client/src/modules/CrossCheck/components/SolutionReview/MessageSendingPanel/index.ts: -------------------------------------------------------------------------------- 1 | export { default as MessageSendingPanel } from './MessageSendingPanel'; 2 | export { type MessageSendingPanelProps } from './MessageSendingPanel'; 3 | -------------------------------------------------------------------------------- /client/src/modules/CrossCheck/components/SolutionReview/UserAvatar/index.ts: -------------------------------------------------------------------------------- 1 | export { default as UserAvatar } from './UserAvatar'; 2 | export { type UserAvatarProps } from './UserAvatar'; 3 | -------------------------------------------------------------------------------- /client/src/modules/CrossCheck/components/SolutionReview/Username/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Username } from './Username'; 2 | export { type UsernameProps } from './Username'; 3 | -------------------------------------------------------------------------------- /client/src/modules/CrossCheck/components/SolutionReview/index.ts: -------------------------------------------------------------------------------- 1 | export { default as SolutionReview } from './SolutionReview'; 2 | export { type SolutionReviewProps } from './SolutionReview'; 3 | -------------------------------------------------------------------------------- /client/src/modules/CrossCheck/components/SolutionReviewSettingsPanel/index.ts: -------------------------------------------------------------------------------- 1 | export { default as SolutionReviewSettingsPanel } from './SolutionReviewSettingsPanel'; 2 | export { type SolutionReviewSettingsPanelProps } from './SolutionReviewSettingsPanel'; 3 | -------------------------------------------------------------------------------- /client/src/modules/CrossCheck/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export { useSolutionReviewSettings } from './useSolutionReviewSettings'; 2 | -------------------------------------------------------------------------------- /client/src/modules/CrossCheck/hooks/useSolutionReviewSettings.ts: -------------------------------------------------------------------------------- 1 | import { useLocalStorage } from 'react-use'; 2 | import { LocalStorageKey, SolutionReviewSettings } from '../constants'; 3 | 4 | export function useSolutionReviewSettings(): SolutionReviewSettings { 5 | const [areContactsVisible = true, setAreContactsVisible] = useLocalStorage( 6 | LocalStorageKey.AreContactsVisible, 7 | ); 8 | 9 | return { areContactsVisible, setAreContactsVisible }; 10 | } 11 | -------------------------------------------------------------------------------- /client/src/modules/CrossCheck/index.tsx: -------------------------------------------------------------------------------- 1 | export { addKeyAndIndex } from 'modules/CrossCheck/utils/addKeyAndIndex'; 2 | export { EditableTable } from 'modules/CrossCheck/EditableTableForCrossCheck'; 3 | export { UploadCriteriaJSON } from 'modules/CrossCheck/UploadCriteriaJSON'; 4 | export { AddCriteriaForCrossCheck } from 'modules/CrossCheck/AddCriteriaForCrossCheck'; 5 | export { ExportJSONButton } from 'modules/CrossCheck/ExportJSONButton'; 6 | export { getCriteriaStatusColor } from 'modules/CrossCheck/utils/getCriteriaStatusColor'; 7 | -------------------------------------------------------------------------------- /client/src/modules/CrossCheck/utils/addKeyAndIndex.tsx: -------------------------------------------------------------------------------- 1 | import { CriteriaDto } from 'api'; 2 | 3 | export const addKeyAndIndex = (array: CriteriaDto[]): CriteriaDto[] => { 4 | return array.map((item, index) => ({ 5 | ...item, 6 | key: index.toString(), 7 | index, 8 | })); 9 | }; 10 | -------------------------------------------------------------------------------- /client/src/modules/CrossCheck/utils/getCriteriaStatusColor.ts: -------------------------------------------------------------------------------- 1 | const colors = ['transparent', 'rgba(255, 0, 0, .05)', 'rgba(0, 255, 0, .05)', 'rgba(255, 255, 0, .05)'] as const; 2 | 3 | export function getCriteriaStatusColor(score: number, maxScore?: number) { 4 | const [transparent, red, green, yellow] = colors; 5 | 6 | if (!maxScore) { 7 | return transparent; 8 | } 9 | 10 | if (score === 0) { 11 | return red; 12 | } 13 | 14 | if (score < maxScore) { 15 | return yellow; 16 | } 17 | 18 | if (score === maxScore) { 19 | return green; 20 | } 21 | 22 | return transparent; 23 | } 24 | -------------------------------------------------------------------------------- /client/src/modules/CrossCheckPairs/pages/CrossCheckPairs/index.ts: -------------------------------------------------------------------------------- 1 | export { default as CrossCheckPairs } from './CrossCheckPairs'; 2 | -------------------------------------------------------------------------------- /client/src/modules/Home/components/RegistryBanner/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Alert, Button, AlertProps } from 'antd'; 3 | 4 | export function RegistryBanner(props: Partial) { 5 | return ( 6 | 12 | Register as Mentor 13 | 14 | } 15 | {...props} 16 | /> 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /client/src/modules/Home/components/SystemAlerts/index.tsx: -------------------------------------------------------------------------------- 1 | import { Alert } from 'antd'; 2 | import type { AlertDto } from 'api'; 3 | import React from 'react'; 4 | 5 | type Props = { 6 | alerts: AlertDto[]; 7 | }; 8 | 9 | export function SystemAlerts({ alerts }: Props) { 10 | return ( 11 | <> 12 | {alerts.map(({ text, type }) => { 13 | const alertType = type === 'warn' ? 'warning' : type; 14 | return ; 15 | })} 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /client/src/modules/Home/data/loadHomeData.ts: -------------------------------------------------------------------------------- 1 | import { CoursesTasksApi } from 'api'; 2 | import { CourseService } from 'services/course'; 3 | 4 | export async function loadHomeData(courseId: number, githubId: string) { 5 | const [studentSummary, { data: courseTasks }] = await Promise.all([ 6 | new CourseService(courseId).getStudentSummary(githubId), 7 | new CoursesTasksApi().getCourseTasks(courseId), 8 | ]); 9 | return { 10 | studentSummary, 11 | courseTasks: courseTasks.map(t => ({ id: t.id })), 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /client/src/modules/Interview/Student/components/NoInterviewsAlert.tsx: -------------------------------------------------------------------------------- 1 | import { InfoCircleTwoTone } from '@ant-design/icons'; 2 | import { Row, Col, Alert } from 'antd'; 3 | import { AlertDescription } from './AlertDescription'; 4 | 5 | export const NoInterviewsAlert = () => ( 6 | 7 | 8 | } 12 | message="There are no planned interviews." 13 | description={} 14 | /> 15 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /client/src/modules/Interview/Student/index.ts: -------------------------------------------------------------------------------- 1 | export { NoInterviewsAlert } from './components/NoInterviewsAlert'; 2 | export { InterviewCard } from './components/InterviewCard'; 3 | -------------------------------------------------------------------------------- /client/src/modules/Interviews/data/index.ts: -------------------------------------------------------------------------------- 1 | import { FeedbackProps } from './getInterviewData'; 2 | import { StageFeedbackProps } from './getStageInterviewData'; 3 | 4 | export { getInterviewData, type FeedbackProps } from './getInterviewData'; 5 | export { getStageInterviewData, type StageFeedbackProps } from './getStageInterviewData'; 6 | 7 | export type PageProps = FeedbackProps | StageFeedbackProps; 8 | -------------------------------------------------------------------------------- /client/src/modules/Interviews/pages/StageInterviewFeedback/index.ts: -------------------------------------------------------------------------------- 1 | export { StageInterviewFeedback } from './StageInterviewFeedback'; 2 | -------------------------------------------------------------------------------- /client/src/modules/Mentor/components/Instructions/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Instructions } from './Instructions'; 2 | export * from './constants'; 3 | export * from './renderers'; 4 | -------------------------------------------------------------------------------- /client/src/modules/Mentor/components/MentorDashboard/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as MentorDashboard } from './MentorDashboard'; 2 | -------------------------------------------------------------------------------- /client/src/modules/Mentor/components/Notification/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Notification } from './Notification'; 2 | -------------------------------------------------------------------------------- /client/src/modules/Mentor/components/ReviewRandomTask/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as ReviewRandomTask } from './ReviewRandomTask'; 2 | -------------------------------------------------------------------------------- /client/src/modules/Mentor/components/SubmitReviewModal/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as SubmitReviewModal, type SubmitReviewModalProps, MODAL_TITLE } from './SubmitReviewModal'; 2 | -------------------------------------------------------------------------------- /client/src/modules/Mentor/components/TaskSolutionsTable/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as TaskSolutionsTable, type TaskSolutionsTableProps } from './TaskSolutionsTable'; 2 | export * from './renderers'; 3 | -------------------------------------------------------------------------------- /client/src/modules/Mentor/components/TaskStatusTabs/index.ts: -------------------------------------------------------------------------------- 1 | export { default as TaskStatusTabs, type Status } from './TaskStatusTabs'; 2 | -------------------------------------------------------------------------------- /client/src/modules/Mentor/components/index.tsx: -------------------------------------------------------------------------------- 1 | export { MentorDashboard } from './MentorDashboard'; 2 | export { Notification } from './Notification'; 3 | export { Instructions } from './Instructions'; 4 | export { TaskSolutionsTable } from './TaskSolutionsTable'; 5 | export { SubmitReviewModal } from './SubmitReviewModal'; 6 | -------------------------------------------------------------------------------- /client/src/modules/Mentor/hooks/useMentorDashboard.tsx: -------------------------------------------------------------------------------- 1 | import { useRequest } from 'ahooks'; 2 | import { MentorsApi } from 'api'; 3 | 4 | const service = new MentorsApi(); 5 | 6 | export function useMentorDashboard(mentorId: number | undefined, courseId: number) { 7 | const { data, loading, run } = useRequest(async () => { 8 | if (!mentorId) { 9 | return []; 10 | } 11 | const { data = [] } = await service.getMentorDashboardData(mentorId, courseId); 12 | return data; 13 | }); 14 | return [data, loading, run] as const; 15 | } 16 | -------------------------------------------------------------------------------- /client/src/modules/Mentor/pages/Interviews/hooks/useAlert.ts: -------------------------------------------------------------------------------- 1 | import { useCallback } from 'react'; 2 | import { useSessionStorage } from 'react-use'; 3 | 4 | export function useAlert(key: string) { 5 | const [isDismissed, setIsDismissed] = useSessionStorage(key, false); 6 | 7 | const setDismissed = useCallback(() => setIsDismissed(true), [setIsDismissed]); 8 | 9 | return [isDismissed, setDismissed] as const; 10 | } 11 | -------------------------------------------------------------------------------- /client/src/modules/MentorRegistry/index.ts: -------------------------------------------------------------------------------- 1 | export * from './components/MentorRegistryTable'; 2 | export * from './components/MentorRegistryTableContainer'; 3 | export * from './components/MentorRegistryDeleteModal'; 4 | export * from './components/MentorRegistryResendModal'; 5 | export * from './constants'; 6 | -------------------------------------------------------------------------------- /client/src/modules/MentorTasksReview/components/AssignReviewerModal/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './AssignReviewerModal'; 2 | -------------------------------------------------------------------------------- /client/src/modules/MentorTasksReview/pages/MentorTasksReview.constants.ts: -------------------------------------------------------------------------------- 1 | export const sortDirectionMap = { 2 | ascend: 'ASC', 3 | descend: 'DESC', 4 | }; 5 | -------------------------------------------------------------------------------- /client/src/modules/NotAccess/index.ts: -------------------------------------------------------------------------------- 1 | export { default as NotAccess } from './NotAccess'; 2 | -------------------------------------------------------------------------------- /client/src/modules/Opportunities/components/AvatarCv/index.tsx: -------------------------------------------------------------------------------- 1 | import { Avatar } from 'antd'; 2 | import { UserOutlined } from '@ant-design/icons'; 3 | 4 | type Props = { 5 | src: string | null; 6 | }; 7 | 8 | const AVATAR_SIZE = 80; 9 | 10 | export const AvatarCv = (props: Props) => { 11 | const src = props.src ?? undefined; 12 | const icon = src ? null : ; 13 | return ; 14 | }; 15 | -------------------------------------------------------------------------------- /client/src/modules/Opportunities/components/Link/index.tsx: -------------------------------------------------------------------------------- 1 | type Props = { 2 | url: string; 3 | title?: string; 4 | text?: string; 5 | }; 6 | 7 | export const Link = ({ url, text, title }: Props) => { 8 | return ( 9 | 10 | {text} 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /client/src/modules/Opportunities/components/SidebarSectionHeader/index.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import { SidebarSectionHeader } from './index'; 3 | 4 | const mockTitle = 'Some title'; 5 | 6 | describe('SidebarSectionHeader', () => { 7 | test('should render title', () => { 8 | render(); 9 | 10 | const title = screen.getByText(mockTitle); 11 | 12 | expect(title).toBeInTheDocument(); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /client/src/modules/Opportunities/components/ViewCv/ContactsSection/index.tsx: -------------------------------------------------------------------------------- 1 | import { Contacts } from 'modules/Opportunities/models'; 2 | import { ContactsList } from './ContactsList'; 3 | 4 | type Props = { 5 | contacts: Contacts | null; 6 | }; 7 | 8 | export const ContactsSection = ({ contacts }: Props) => { 9 | if (contacts == null) { 10 | return null; 11 | } 12 | 13 | return ( 14 |
15 | 16 |
17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /client/src/modules/Opportunities/components/ViewCv/DataTextValue/index.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import { DataTextValue } from './index'; 3 | 4 | const MockContent = () =>
Mock content
; 5 | 6 | describe('DataTextValue', () => { 7 | test('should display content', () => { 8 | render( 9 | 10 | 11 | , 12 | ); 13 | const mockContent = screen.getByText(/mock content/i); 14 | expect(mockContent).toBeInTheDocument(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /client/src/modules/Opportunities/components/ViewCv/DataTextValue/index.tsx: -------------------------------------------------------------------------------- 1 | import { PropsWithChildren } from 'react'; 2 | import css from 'styled-jsx/css'; 3 | 4 | export const DataTextValue = ({ children }: PropsWithChildren) => { 5 | return ( 6 | <> 7 | {children} 8 | 9 | 10 | ); 11 | }; 12 | 13 | const styles = css` 14 | .course-data-key { 15 | font-size: 14px; 16 | padding-right: 8px; 17 | white-space: nowrap; 18 | width: 80px; 19 | display: inline-block; 20 | } 21 | `; 22 | -------------------------------------------------------------------------------- /client/src/modules/Opportunities/constants.ts: -------------------------------------------------------------------------------- 1 | export const enum ExpirationState { 2 | Expired, 3 | NearlyExpired, 4 | NotExpired, 5 | } 6 | -------------------------------------------------------------------------------- /client/src/modules/Opportunities/data/getContactsToRender.ts: -------------------------------------------------------------------------------- 1 | import { Contacts } from '../models'; 2 | 3 | type EntryOf = { [K in keyof T]: [K, T[K]] }[keyof T]; 4 | 5 | export const getContactsToRender = (contacts: Contacts | null) => { 6 | if (!contacts) { 7 | return []; 8 | } 9 | const contactsEntries = Object.entries(contacts); 10 | return contactsEntries.filter((contact): contact is EntryOf => contact[1] !== null); 11 | }; 12 | -------------------------------------------------------------------------------- /client/src/modules/Opportunities/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export { useExpiration } from './useExpiration'; 2 | export { useResumeData } from './useResumeData'; 3 | export { useViewData } from './useViewData'; 4 | -------------------------------------------------------------------------------- /client/src/modules/Opportunities/transformers/index.ts: -------------------------------------------------------------------------------- 1 | export { splitDataForForms } from './splitDataForForms'; 2 | export { transformFieldsData } from './transformFieldsData'; 3 | export { transformInitialCvData } from './transformInitialCvData'; 4 | -------------------------------------------------------------------------------- /client/src/modules/Profile/components/MentorEndorsement/index.tsx: -------------------------------------------------------------------------------- 1 | export { MentorEndorsement } from './MentorEndorsement'; 2 | -------------------------------------------------------------------------------- /client/src/modules/Registry/components/FormCard/FormCard.tsx: -------------------------------------------------------------------------------- 1 | import { Card } from 'antd'; 2 | import { ReactNode } from 'react'; 3 | 4 | type Props = { 5 | title: ReactNode; 6 | children?: ReactNode; 7 | }; 8 | 9 | export function FormCard({ title, children }: Props) { 10 | return ( 11 | 12 | {children} 13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /client/src/modules/Registry/components/Header/Header.tsx: -------------------------------------------------------------------------------- 1 | import { Space, Typography } from 'antd'; 2 | import { ReactNode } from 'react'; 3 | 4 | const { Title, Text } = Typography; 5 | 6 | type Props = { 7 | title: ReactNode; 8 | }; 9 | 10 | export function Header({ title }: Props) { 11 | return ( 12 | 13 | {title} 14 | Free courses from the developer community 15 | 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /client/src/modules/Registry/components/NoCourses/NoCourses.tsx: -------------------------------------------------------------------------------- 1 | import { MehTwoTone } from '@ant-design/icons'; 2 | import { Button, Result } from 'antd'; 3 | 4 | export function NoCourses() { 5 | return ( 6 | } 9 | title="There are no planned courses." 10 | subTitle="Please come back later." 11 | extra={ 12 | 15 | } 16 | /> 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /client/src/modules/Registry/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useFormLayout/useFormLayout'; 2 | export * from './useMentorData/useMentorData'; 3 | export * from './useStudentData/useStudentData'; 4 | -------------------------------------------------------------------------------- /client/src/modules/Registry/hooks/useFormLayout/useFormLayout.ts: -------------------------------------------------------------------------------- 1 | import { Grid } from 'antd'; 2 | import { FormLayout } from 'antd/lib/form/Form'; 3 | 4 | const { useBreakpoint } = Grid; 5 | 6 | export function useFormLayout() { 7 | const { xs, sm, md, lg, xl, xxl } = useBreakpoint(); 8 | const largeScreenSizes = [md, lg, xl, xxl]; 9 | const isSmallScreen = xs || (sm && !largeScreenSizes.some(Boolean)); 10 | const formLayout: FormLayout = isSmallScreen ? 'vertical' : 'horizontal'; 11 | 12 | return { formLayout, isSmallScreen: xs } as const; 13 | } 14 | -------------------------------------------------------------------------------- /client/src/modules/Registry/pages/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Mentor/Mentor'; 2 | export * from './Student/Student'; 3 | -------------------------------------------------------------------------------- /client/src/modules/Schedule/components/AdditionalActions/helpers.ts: -------------------------------------------------------------------------------- 1 | export const buildICalendarLink = (courseId: number, token: string, timezone: string) => 2 | `/api/v2/courses/${courseId}/icalendar/${token}?timezone=${encodeURIComponent(timezone || '')}`; 3 | 4 | export const buildExportLink = (courseId: number, timezone: string) => 5 | `/api/course/${courseId}/schedule/csv/${timezone.replace('/', '_')}`; 6 | 7 | export const setExportLink = (link: string) => { 8 | window.location.href = link; 9 | }; 10 | -------------------------------------------------------------------------------- /client/src/modules/Schedule/components/AdditionalActions/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as AdditionalActions } from './AdditionalActions'; 2 | export { type AdditionalActionsProps, type MenuItemType } from './AdditionalActions'; 3 | -------------------------------------------------------------------------------- /client/src/modules/Schedule/components/EventDetails/index.ts: -------------------------------------------------------------------------------- 1 | export { EventDetails } from './EventDetails'; 2 | -------------------------------------------------------------------------------- /client/src/modules/Schedule/components/FilteredTags/index.tsx: -------------------------------------------------------------------------------- 1 | import { FilteredTags } from './FilteredTags'; 2 | 3 | export default FilteredTags; 4 | -------------------------------------------------------------------------------- /client/src/modules/Schedule/components/MobileItemCard/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './MobileItemCard'; 2 | -------------------------------------------------------------------------------- /client/src/modules/Schedule/components/SettingsDrawer/index.ts: -------------------------------------------------------------------------------- 1 | export { default as SettingsDrawer } from './SettingsDrawer'; 2 | -------------------------------------------------------------------------------- /client/src/modules/Schedule/components/SettingsPanel/helpers.ts: -------------------------------------------------------------------------------- 1 | import { MenuItemType } from '../AdditionalActions'; 2 | 3 | export const buildMenuItem = (title: string, icon: React.ReactNode, isVisible: boolean): MenuItemType | null => 4 | isVisible 5 | ? { 6 | key: title, 7 | label: title, 8 | icon: icon, 9 | } 10 | : null; 11 | -------------------------------------------------------------------------------- /client/src/modules/Schedule/components/SettingsPanel/index.ts: -------------------------------------------------------------------------------- 1 | export { default as SettingsPanel } from './SettingsPanel'; 2 | export { SettingsButtons, type SettingsPanelProps } from './SettingsPanel'; 3 | -------------------------------------------------------------------------------- /client/src/modules/Schedule/components/StatusTabs/index.ts: -------------------------------------------------------------------------------- 1 | export { default as StatusTabs } from './StatusTabs'; 2 | -------------------------------------------------------------------------------- /client/src/modules/Schedule/components/TableView/index.ts: -------------------------------------------------------------------------------- 1 | export { default as TableView } from './TableView'; 2 | -------------------------------------------------------------------------------- /client/src/modules/Schedule/index.ts: -------------------------------------------------------------------------------- 1 | export { getTaskStatusColor } from './utils'; 2 | -------------------------------------------------------------------------------- /client/src/modules/Score/components/ExportCsvButton/index.tsx: -------------------------------------------------------------------------------- 1 | import { FileExcelOutlined } from '@ant-design/icons'; 2 | import { Button } from 'antd'; 3 | 4 | type Props = { 5 | enabled?: boolean; 6 | onClick: () => void; 7 | }; 8 | 9 | export function ExportCsvButton(props: Props) { 10 | if (!props.enabled) { 11 | return null; 12 | } 13 | return ( 14 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /client/src/modules/Score/data/getExportCsvUrl.ts: -------------------------------------------------------------------------------- 1 | import { getQueryParams, getQueryString } from 'utils/queryParams-utils'; 2 | 3 | export function getExportCsvUrl(courseId: number, cityName?: string | string[], mentor?: string | string[]) { 4 | const queryParams = getQueryString(getQueryParams({ cityName, ['mentor.githubId']: mentor })); 5 | const url = buildUrl(courseId, queryParams); 6 | return url; 7 | } 8 | 9 | const buildUrl = (id: number, params: string): string => { 10 | return `/api/course/${id}/students/score/csv${params}`; 11 | }; 12 | -------------------------------------------------------------------------------- /client/src/modules/Score/hooks/types.ts: -------------------------------------------------------------------------------- 1 | export type ScoreTableFilters = { 2 | githubId?: string[]; 3 | name?: string[]; 4 | 'mentor.githubId'?: string[]; 5 | cityName?: string[]; 6 | activeOnly: boolean; 7 | }; 8 | 9 | export type ScoreOrderField = 10 | | 'rank' 11 | | 'totalScore' 12 | | 'crossCheckScore' 13 | | 'githubId' 14 | | 'name' 15 | | 'cityName' 16 | | 'mentor' 17 | | 'totalScoreChangeDate' 18 | | 'repositoryLastActivityDate'; 19 | 20 | export type ScoreOrder = { 21 | field: ScoreOrderField; 22 | order: 'ascend' | 'descend'; 23 | column?: { sorter: ScoreOrderField }; 24 | }; 25 | -------------------------------------------------------------------------------- /client/src/modules/StudentDashboard/components/AvailableReviewCard/index.ts: -------------------------------------------------------------------------------- 1 | export * from './AvailableReviewCard'; 2 | -------------------------------------------------------------------------------- /client/src/modules/StudentDashboard/components/MentorCard/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './MentorCard'; 2 | -------------------------------------------------------------------------------- /client/src/modules/StudentDashboard/components/MentorInfo/index.ts: -------------------------------------------------------------------------------- 1 | export { default as MentorInfo, type MentorContact } from './MentorInfo'; 2 | -------------------------------------------------------------------------------- /client/src/modules/StudentDashboard/components/NextEventCard/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as NextEventCard } from './NextEventCard'; 2 | export * from './renderers'; 3 | -------------------------------------------------------------------------------- /client/src/modules/StudentDashboard/components/SubmitTaskSolution/index.ts: -------------------------------------------------------------------------------- 1 | export { default as SubmitTaskSolution } from './SubmitTaskSolution'; 2 | -------------------------------------------------------------------------------- /client/src/modules/StudentDashboard/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './CommonDashboardCard'; 2 | export * from './MentorCard'; 3 | export * from './MainStatsCard'; 4 | export * from './TasksStatsCard'; 5 | export * from './TasksStatsModal'; 6 | export * from './NextEventCard'; 7 | export * from './NextEventCard/renderers'; 8 | export * from './RepositoryCard'; 9 | export * from './AvailableReviewCard'; 10 | -------------------------------------------------------------------------------- /client/src/modules/StudentDashboard/index.ts: -------------------------------------------------------------------------------- 1 | export * from './hooks/useDashboardData'; 2 | export * from './hooks/useSubmitTaskSolution'; 3 | export * from './components'; 4 | -------------------------------------------------------------------------------- /client/src/modules/Tasks/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './TasksTable/TasksTable'; 2 | export * from './TaskModal/TaskModal'; 3 | export * from './TaskSettings/TaskSettings'; 4 | export * from './CrossCheckTaskCriteriaPanel/CrossCheckTaskCriteriaPanel'; 5 | export * from './GitHubPanel/GitHubPanel'; 6 | export * from './JsonAttributesPanel/JsonAttributesPanel'; 7 | -------------------------------------------------------------------------------- /client/src/modules/Tasks/pages/index.ts: -------------------------------------------------------------------------------- 1 | export * from './TasksPage/TasksPage'; 2 | -------------------------------------------------------------------------------- /client/src/modules/TeamDistribution/components/SubmitScoreModal/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as SubmitScoreModal } from './SubmitScoreModal'; 2 | -------------------------------------------------------------------------------- /client/src/modules/TeamDistribution/components/TeamDistributionCard/index.ts: -------------------------------------------------------------------------------- 1 | export { default as TeamDistributionCard } from './TeamDistributionCard'; 2 | -------------------------------------------------------------------------------- /client/src/modules/TeamDistribution/components/TeamDistributionModal/index.ts: -------------------------------------------------------------------------------- 1 | export { default as TeamDistributionModal } from './TeamDistributionModal'; 2 | -------------------------------------------------------------------------------- /client/src/modules/TeamDistribution/components/WelcomeCard/index.ts: -------------------------------------------------------------------------------- 1 | export { default as WelcomeCard } from './WelcomeCard'; 2 | -------------------------------------------------------------------------------- /client/src/modules/TeamDistribution/pages/TeamDistributions/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as TeamDistributions } from './TeamDistributions'; 2 | -------------------------------------------------------------------------------- /client/src/modules/Teams/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as TeamsHeader } from './TeamsHeader/TeamsHeader'; 2 | export { default as TeamModal } from './TeamModal/TeamModal'; 3 | export { default as JoinTeamModal } from './JoinTeamModal/JoinTeamModal'; 4 | export { default as TeamsSection } from './TeamsSection/TeamsSection'; 5 | export { default as StudentsTable } from './StudentsTable/StudentsTable'; 6 | export { default as StudentsWithoutTeamSection } from './StudentsWithoutTeamSection/StudentsWithoutTeamSection'; 7 | export { default as MyTeamSection } from './MyTeamSection/MyTeamSection'; 8 | -------------------------------------------------------------------------------- /client/src/modules/Teams/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export { useDistribution } from './useDistribution/useDistribution'; 2 | -------------------------------------------------------------------------------- /client/src/modules/Teams/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Teams } from './Pages/Teams'; 2 | -------------------------------------------------------------------------------- /client/src/pages/admin/contributors.tsx: -------------------------------------------------------------------------------- 1 | import { ContributorPage } from 'modules/Contributor/pages/ContributorPage'; 2 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 3 | 4 | export default function () { 5 | return ( 6 | 7 | 8 | 9 | 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /client/src/pages/admin/disciplines.tsx: -------------------------------------------------------------------------------- 1 | import { DisciplinePage } from 'modules/Discipline/pages/DisciplinePage'; 2 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 3 | 4 | export default function () { 5 | return ( 6 | 7 | 8 | 9 | 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /client/src/pages/admin/notifications.tsx: -------------------------------------------------------------------------------- 1 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 2 | import { AdminPage } from 'modules/Notifications/pages/AdminNotificationsPage'; 3 | 4 | export default function () { 5 | return ( 6 | 7 | 8 | 9 | 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /client/src/pages/admin/prompts.tsx: -------------------------------------------------------------------------------- 1 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 2 | import { PromptsPage } from 'modules/Prompts/pages/PromptPage'; 3 | 4 | export default function () { 5 | return ( 6 | 7 | 8 | 9 | 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /client/src/pages/admin/students.tsx: -------------------------------------------------------------------------------- 1 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 2 | import { Students } from 'modules/Students/Pages/Students'; 3 | 4 | export default function () { 5 | return ( 6 | 7 | 8 | 9 | 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /client/src/pages/admin/tasks.tsx: -------------------------------------------------------------------------------- 1 | import { CourseRole } from 'services/models'; 2 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 3 | import { TasksPage } from 'modules/Tasks/pages'; 4 | 5 | export default function () { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /client/src/pages/course/403.tsx: -------------------------------------------------------------------------------- 1 | import { CouseNoAccessPage } from 'modules/Course/pages/CouseNoAccess'; 2 | 3 | export default CouseNoAccessPage; 4 | -------------------------------------------------------------------------------- /client/src/pages/course/admin/certified-students.tsx: -------------------------------------------------------------------------------- 1 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 2 | import { CourseRole } from 'services/models'; 3 | 4 | export default function () { 5 | return ( 6 | 7 | Certified Students 8 | 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /client/src/pages/course/admin/cross-check-table.tsx: -------------------------------------------------------------------------------- 1 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 2 | import { CrossCheckPairs } from 'modules/CrossCheckPairs/pages/CrossCheckPairs'; 3 | import { CourseRole } from 'services/models'; 4 | 5 | export default function () { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /client/src/pages/course/admin/mentor-tasks-review.tsx: -------------------------------------------------------------------------------- 1 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 2 | import { MentorTasksReview } from 'modules/MentorTasksReview/pages/MentorTasksReview'; 3 | import { CourseRole } from 'services/models'; 4 | 5 | export default function () { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /client/src/pages/course/mentor/dashboard.tsx: -------------------------------------------------------------------------------- 1 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 2 | import { MentorDashboard } from 'modules/Mentor/components'; 3 | import { CourseRole } from 'services/models'; 4 | 5 | export default function () { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /client/src/pages/course/mentor/feedback/index.tsx: -------------------------------------------------------------------------------- 1 | import { CourseRole } from 'services/models'; 2 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 3 | import { StudentFeedback } from 'modules/Mentor/pages/StudentFeedback'; 4 | 5 | export default function () { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /client/src/pages/course/mentor/interview-wait-list.tsx: -------------------------------------------------------------------------------- 1 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 2 | import { InterviewWaitingList } from 'modules/Mentor/pages/InterviewWaitingList'; 3 | import { CourseRole } from 'services/models'; 4 | 5 | function Page() { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | 15 | export default Page; 16 | -------------------------------------------------------------------------------- /client/src/pages/course/mentor/interviews.tsx: -------------------------------------------------------------------------------- 1 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 2 | import { Interviews } from 'modules/Mentor/pages/Interviews'; 3 | 4 | export default function () { 5 | return ( 6 | 7 | 8 | 9 | 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /client/src/pages/course/mentor/students.tsx: -------------------------------------------------------------------------------- 1 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 2 | import { Students } from 'modules/Mentor/pages/Students'; 3 | import { CourseRole } from 'services/models'; 4 | 5 | export default function () { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /client/src/pages/course/schedule.tsx: -------------------------------------------------------------------------------- 1 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 2 | import { SchedulePage } from 'modules/Schedule/pages/SchedulePage'; 3 | 4 | export default function () { 5 | return ( 6 | 7 | 8 | 9 | 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /client/src/pages/course/score.tsx: -------------------------------------------------------------------------------- 1 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 2 | import { ScorePage } from 'modules/Score/pages/ScorePage'; 3 | 4 | function Page() { 5 | return ( 6 | 7 | 8 | 9 | 10 | 11 | ); 12 | } 13 | 14 | export default Page; 15 | -------------------------------------------------------------------------------- /client/src/pages/course/stats.tsx: -------------------------------------------------------------------------------- 1 | import { CourseStatistics } from 'modules/CourseStatistics'; 2 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 3 | 4 | function Page() { 5 | return ( 6 | 7 | 8 | 9 | 10 | 11 | ); 12 | } 13 | 14 | export default Page; 15 | -------------------------------------------------------------------------------- /client/src/pages/course/student/auto-test/index.tsx: -------------------------------------------------------------------------------- 1 | import { AutoTests } from 'modules/AutoTest/pages'; 2 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 3 | 4 | function Page() { 5 | return ( 6 | 7 | 8 | 9 | 10 | 11 | ); 12 | } 13 | 14 | export default Page; 15 | -------------------------------------------------------------------------------- /client/src/pages/course/student/auto-test/task.tsx: -------------------------------------------------------------------------------- 1 | import { Task } from 'modules/AutoTest/pages'; 2 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 3 | import { CourseRole } from 'services/models'; 4 | 5 | function Page() { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | 15 | export default Page; 16 | -------------------------------------------------------------------------------- /client/src/pages/course/student/cross-check-submit.tsx: -------------------------------------------------------------------------------- 1 | import { CrossCheckSubmit } from 'modules/Course/pages/Student/CrossCheckSubmit'; 2 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 3 | import { CourseRole } from '../../../services/models'; 4 | 5 | function Page() { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | 15 | export default Page; 16 | -------------------------------------------------------------------------------- /client/src/pages/course/team-distributions.tsx: -------------------------------------------------------------------------------- 1 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 2 | import { TeamDistributions } from 'modules/TeamDistribution/pages/TeamDistributions'; 3 | 4 | function Page() { 5 | return ( 6 | 7 | 8 | 9 | 10 | 11 | ); 12 | } 13 | 14 | export default Page; 15 | -------------------------------------------------------------------------------- /client/src/pages/course/teams.tsx: -------------------------------------------------------------------------------- 1 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 2 | import { Teams } from 'modules/Teams'; 3 | 4 | export function Page() { 5 | return ( 6 | 7 | 8 | 9 | 10 | 11 | ); 12 | } 13 | 14 | export default Page; 15 | -------------------------------------------------------------------------------- /client/src/pages/cv/[uuid].tsx: -------------------------------------------------------------------------------- 1 | import { PublicPage } from 'modules/Opportunities/pages/PublicPage'; 2 | import { getServerSideProps } from 'modules/Opportunities/pages/PublicPage/getServerSideProps'; 3 | 4 | export { getServerSideProps }; 5 | 6 | export default PublicPage; 7 | -------------------------------------------------------------------------------- /client/src/pages/cv/edit.tsx: -------------------------------------------------------------------------------- 1 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 2 | import { EditPage } from 'modules/Opportunities/pages/EditPage'; 3 | 4 | // force the page to render on the server to fix issue with getting githubId from url 5 | export const getServerSideProps = async () => { 6 | return { props: {} }; 7 | }; 8 | 9 | export default function () { 10 | return ( 11 | 12 | 13 | 14 | 15 | 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /client/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 2 | import { HomePage } from 'modules/Home/pages/HomePage'; 3 | 4 | function Page() { 5 | return ( 6 | 7 | 8 | 9 | 10 | 11 | ); 12 | } 13 | 14 | export default Page; 15 | -------------------------------------------------------------------------------- /client/src/pages/profile/connection-confirmed.tsx: -------------------------------------------------------------------------------- 1 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 2 | import { ConnectionConfirmed } from 'modules/Notifications/pages/ConnectionConfirmedPage'; 3 | 4 | function Page() { 5 | return ( 6 | 7 | 8 | 9 | 10 | 11 | ); 12 | } 13 | 14 | export default Page; 15 | -------------------------------------------------------------------------------- /client/src/pages/profile/notifications.tsx: -------------------------------------------------------------------------------- 1 | import { ActiveCourseProvider, SessionProvider } from 'modules/Course/contexts'; 2 | import { UserNotificationsPage } from 'modules/Notifications/pages/UserNotificationsSettingsPage'; 3 | 4 | function Page() { 5 | return ( 6 | 7 | 8 | 9 | 10 | 11 | ); 12 | } 13 | 14 | export default Page; 15 | -------------------------------------------------------------------------------- /client/src/reset.d.ts: -------------------------------------------------------------------------------- 1 | import '@total-typescript/ts-reset'; 2 | 3 | declare global { 4 | interface URLSearchParams { 5 | append(name: string, value: string | unknown): void; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /client/src/services/files.ts: -------------------------------------------------------------------------------- 1 | import globalAxios, { AxiosInstance } from 'axios'; 2 | 3 | export class FilesService { 4 | private axios: AxiosInstance; 5 | 6 | constructor() { 7 | this.axios = globalAxios.create({ baseURL: `/api` }); 8 | } 9 | 10 | async uploadFile(key: string, data: string) { 11 | const result = await this.axios.post(`/file/upload?key=${key}`, JSON.parse(data)); 12 | return result.data.data as { s3Key: string }; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client/src/services/gratitude.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { IGratitudeGetResponse, IGratitudeGetRequest } from '@common/interfaces/gratitude'; 3 | 4 | export class GratitudeService { 5 | async getGratitude(data?: IGratitudeGetRequest): Promise<{ content: IGratitudeGetResponse[]; count: number }> { 6 | const result = await axios.get(`/api/feedback/gratitude`, { params: data }); 7 | return result.data.data; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /client/src/services/models.ts: -------------------------------------------------------------------------------- 1 | import { Session } from 'components/withSession'; 2 | import { StudentBasic as CommonStudentBasic } from '@common/models'; 3 | import { ProfileCourseDto, UserGroupDtoRolesEnum as CourseRole } from 'api'; 4 | 5 | export type Course = ProfileCourseDto; 6 | export type StudentBasic = CommonStudentBasic; 7 | 8 | export { CourseRole }; 9 | 10 | export interface CoursePageProps { 11 | session?: Session; 12 | course: Course; 13 | params?: Record; 14 | } 15 | 16 | export type CourseOnlyPageProps = { 17 | course: Course; 18 | params?: Record; 19 | }; 20 | -------------------------------------------------------------------------------- /client/src/setupJest.ts: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom'; 2 | import matchMediaPolyfill from 'mq-polyfill'; 3 | 4 | matchMediaPolyfill(window); 5 | -------------------------------------------------------------------------------- /client/src/utils/onlyDefined.ts: -------------------------------------------------------------------------------- 1 | import pickBy from 'lodash/pickBy'; 2 | 3 | export function onlyDefined>(data: T) { 4 | return pickBy(data, val => val !== undefined && val !== '' && val !== null); 5 | } 6 | -------------------------------------------------------------------------------- /client/src/utils/optionalQueryString.ts: -------------------------------------------------------------------------------- 1 | export function optionalQueryString(value: string | string[] | undefined) { 2 | if (Array.isArray(value)) { 3 | const trimmedElements = value.map(elem => elem.trim()).join(','); 4 | if (trimmedElements !== '') { 5 | return trimmedElements; 6 | } 7 | } else if (typeof value === 'string') { 8 | return String(value).trim(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /client/src/utils/text-utils.ts: -------------------------------------------------------------------------------- 1 | const githubIdMatch = '[a-z\\d](?:[a-z\\d]|-*(?=[a-z\\d])){0,38}'; 2 | const stringStartMatch = '(https?:\\/*\\/)?github.com(\\/)?|https?:\\/*\\/|^'; 3 | 4 | const LOGIN_FIND_REGEXP = new RegExp(`(${stringStartMatch})(${githubIdMatch})?`, 'i'); 5 | 6 | export const filterLogin = (login: string) => { 7 | const matches = login.trim().match(LOGIN_FIND_REGEXP) || []; 8 | const [foundLogin = ''] = matches.reverse(); 9 | 10 | return foundLogin; 11 | }; 12 | -------------------------------------------------------------------------------- /common/enums/mentor/index.ts: -------------------------------------------------------------------------------- 1 | export enum PreferredStudentsLocation { 2 | ANY = 'any', 3 | COUNTRY = 'country', 4 | CITY = 'city', 5 | } 6 | -------------------------------------------------------------------------------- /common/models/index.ts: -------------------------------------------------------------------------------- 1 | export * from './user'; 2 | export * from './stage-interview-feedback'; 3 | export * from './profile'; 4 | export * from './interview'; 5 | -------------------------------------------------------------------------------- /common/types/pagination/index.ts: -------------------------------------------------------------------------------- 1 | export type IPaginationInfo = { 2 | total?: number; 3 | totalPages?: number; 4 | current: number; 5 | pageSize: number; 6 | }; 7 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/.nojekyll -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | docs.app.rs.school -------------------------------------------------------------------------------- /docs/platform/img/adding-tests-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/adding-tests-1.png -------------------------------------------------------------------------------- /docs/platform/img/adding-tests-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/adding-tests-2.png -------------------------------------------------------------------------------- /docs/platform/img/adding-tests-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/adding-tests-3.png -------------------------------------------------------------------------------- /docs/platform/img/adding-tests-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/adding-tests-4.png -------------------------------------------------------------------------------- /docs/platform/img/adding-tests-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/adding-tests-5.png -------------------------------------------------------------------------------- /docs/platform/img/adding-tests-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/adding-tests-6.png -------------------------------------------------------------------------------- /docs/platform/img/adding-tests-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/adding-tests-7.png -------------------------------------------------------------------------------- /docs/platform/img/adding-tests-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/adding-tests-8.png -------------------------------------------------------------------------------- /docs/platform/img/autotest-details.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/autotest-details.jpg -------------------------------------------------------------------------------- /docs/platform/img/choose-kata-languages/task-json.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/choose-kata-languages/task-json.JPG -------------------------------------------------------------------------------- /docs/platform/img/choose-kata-languages/task-settings.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/choose-kata-languages/task-settings.JPG -------------------------------------------------------------------------------- /docs/platform/img/cross-check-scheduling/course-task-modal.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/cross-check-scheduling/course-task-modal.JPG -------------------------------------------------------------------------------- /docs/platform/img/cv/applicants-page-link.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/cv/applicants-page-link.JPG -------------------------------------------------------------------------------- /docs/platform/img/cv/applicants-table.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/cv/applicants-table.JPG -------------------------------------------------------------------------------- /docs/platform/img/cv/cv-form-filled-1.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/cv/cv-form-filled-1.JPG -------------------------------------------------------------------------------- /docs/platform/img/cv/cv-form-filled.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/cv/cv-form-filled.JPG -------------------------------------------------------------------------------- /docs/platform/img/cv/cv-view-filled.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/cv/cv-view-filled.JPG -------------------------------------------------------------------------------- /docs/platform/img/cv/cv-view-print.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/cv/cv-view-print.JPG -------------------------------------------------------------------------------- /docs/platform/img/cv/edit-cv-buttons.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/cv/edit-cv-buttons.JPG -------------------------------------------------------------------------------- /docs/platform/img/cv/employer-page.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/cv/employer-page.JPG -------------------------------------------------------------------------------- /docs/platform/img/cv/header-dropdown.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/cv/header-dropdown.JPG -------------------------------------------------------------------------------- /docs/platform/img/cv/no-consent-modal.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/cv/no-consent-modal.JPG -------------------------------------------------------------------------------- /docs/platform/img/cv/no-consent.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/cv/no-consent.JPG -------------------------------------------------------------------------------- /docs/platform/img/cv/view-control-buttons.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/cv/view-control-buttons.JPG -------------------------------------------------------------------------------- /docs/platform/img/cv/view-delete-cv.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/cv/view-delete-cv.JPG -------------------------------------------------------------------------------- /docs/platform/img/no-access.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/no-access.png -------------------------------------------------------------------------------- /docs/platform/img/schedule-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/schedule-1.png -------------------------------------------------------------------------------- /docs/platform/img/schedule-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/schedule-10.png -------------------------------------------------------------------------------- /docs/platform/img/schedule-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/schedule-2.png -------------------------------------------------------------------------------- /docs/platform/img/schedule-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/schedule-3.png -------------------------------------------------------------------------------- /docs/platform/img/schedule-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/schedule-4.png -------------------------------------------------------------------------------- /docs/platform/img/schedule-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/schedule-5.png -------------------------------------------------------------------------------- /docs/platform/img/schedule-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/schedule-6.png -------------------------------------------------------------------------------- /docs/platform/img/schedule-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/schedule-7.png -------------------------------------------------------------------------------- /docs/platform/img/schedule-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/schedule-8.png -------------------------------------------------------------------------------- /docs/platform/img/schedule-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/schedule-9.png -------------------------------------------------------------------------------- /docs/platform/img/tasks-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/docs/platform/img/tasks-1.jpg -------------------------------------------------------------------------------- /nestjs/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .turbo 3 | -------------------------------------------------------------------------------- /nestjs/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "tabWidth": 2, 4 | "semi": true, 5 | "singleQuote": true, 6 | "printWidth": 120, 7 | "arrowParens": "avoid" 8 | } 9 | -------------------------------------------------------------------------------- /nestjs/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:22-alpine 2 | 3 | EXPOSE 8080 4 | 5 | ENV NODE_ENV production 6 | ENV NODE_PORT 8080 7 | ENV TZ utc 8 | ENV RS_ENV production 9 | 10 | WORKDIR /app 11 | 12 | COPY nestjs/package.json /app/nestjs/ 13 | COPY package.json /app 14 | COPY package-lock.json /app 15 | 16 | RUN npm install --production --no-optional 17 | 18 | COPY nestjs/dist /app/nestjs/dist 19 | 20 | CMD [ "node", "/app/nestjs/dist/nestjs/src/main" ] 21 | -------------------------------------------------------------------------------- /nestjs/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import defaultConfig from '../eslint.config.mjs'; 2 | export default defaultConfig; 3 | -------------------------------------------------------------------------------- /nestjs/jest.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | moduleFileExtensions: ['js', 'json', 'ts'], 3 | rootDir: 'src', 4 | testRegex: ['.*\\.spec\\.ts$', 'test\\.ts'], 5 | transform: { 6 | '^.+\\.(t|j)s$': 'ts-jest', 7 | }, 8 | collectCoverageFrom: ['**/*.(t|j)s'], 9 | coverageDirectory: '../coverage', 10 | testEnvironment: 'node', 11 | moduleNameMapper: { 12 | '^@common/(.*)$': '/../../common/$1', 13 | '^@entities(.*)$': '/../../server/src/models/$1', 14 | '^src/(.*)$': '/$1', 15 | }, 16 | clearMocks: true, 17 | }; 18 | -------------------------------------------------------------------------------- /nestjs/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "collection": "@nestjs/schematics", 3 | "sourceRoot": "src", 4 | "entryFile": "nestjs/src/main.js" 5 | } 6 | -------------------------------------------------------------------------------- /nestjs/openapitools.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", 3 | "spaces": 2, 4 | "generator-cli": { 5 | "version": "5.4.0" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /nestjs/src/activity/activity.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { ConfigModule } from 'src/config'; 3 | import { UsersModule } from 'src/users'; 4 | import { ActivityController } from './activity.controller'; 5 | 6 | @Module({ 7 | controllers: [ActivityController], 8 | imports: [UsersModule, ConfigModule], 9 | }) 10 | export class ActivityModule {} 11 | -------------------------------------------------------------------------------- /nestjs/src/activity/dto/activity.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsBoolean, IsNumber } from 'class-validator'; 3 | 4 | export class ActivityDto { 5 | @ApiProperty() 6 | @IsNumber() 7 | public lastActivityTime: number; 8 | 9 | @ApiProperty() 10 | @IsBoolean() 11 | public isActive: boolean; 12 | } 13 | -------------------------------------------------------------------------------- /nestjs/src/activity/dto/create-activity.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsBoolean } from 'class-validator'; 3 | 4 | export class CreateActivityDto { 5 | @ApiProperty() 6 | @IsBoolean() 7 | public isActive: boolean; 8 | } 9 | -------------------------------------------------------------------------------- /nestjs/src/alerts/alerts.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | import { Alert } from '@entities/alert'; 4 | import { AlertsController } from './alerts.controller'; 5 | import { AlertsService } from './alerts.service'; 6 | 7 | @Module({ 8 | imports: [TypeOrmModule.forFeature([Alert])], 9 | controllers: [AlertsController], 10 | providers: [AlertsService], 11 | }) 12 | export class AlertsModule {} 13 | -------------------------------------------------------------------------------- /nestjs/src/alerts/dto/create-alert.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class CreateAlertDto { 4 | @ApiProperty() 5 | type: string; 6 | @ApiProperty() 7 | text: string; 8 | @ApiProperty({ required: false }) 9 | enabled?: boolean; 10 | @ApiProperty({ required: false }) 11 | courseId?: number; 12 | } 13 | -------------------------------------------------------------------------------- /nestjs/src/alerts/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './alert.dto'; 2 | export * from './create-alert.dto'; 3 | export * from './update-alert.dto'; 4 | -------------------------------------------------------------------------------- /nestjs/src/alerts/dto/update-alert.dto.ts: -------------------------------------------------------------------------------- 1 | import { PartialType } from '@nestjs/mapped-types'; 2 | import { CreateAlertDto } from './create-alert.dto'; 3 | 4 | export class UpdateAlertDto extends PartialType(CreateAlertDto) {} 5 | -------------------------------------------------------------------------------- /nestjs/src/auth/constants.ts: -------------------------------------------------------------------------------- 1 | export const JWT_COOKIE_NAME = 'auth-token'; 2 | export const JWT_TOKEN_EXPIRATION = '2d'; 3 | -------------------------------------------------------------------------------- /nestjs/src/auth/course.guard.ts: -------------------------------------------------------------------------------- 1 | import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; 2 | import { CurrentRequest } from './auth.service'; 3 | 4 | @Injectable() 5 | export class CourseGuard implements CanActivate { 6 | public canActivate(context: ExecutionContext) { 7 | const req = context.getArgs<[CurrentRequest]>()[0]; 8 | const { user, params } = req; 9 | 10 | if (user.isAdmin) { 11 | return true; 12 | } 13 | 14 | return Boolean(user.courses[Number(params.courseId)]); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /nestjs/src/auth/default.guard.ts: -------------------------------------------------------------------------------- 1 | import { AuthGuard } from '@nestjs/passport'; 2 | 3 | export const DefaultGuard = AuthGuard(['jwt', 'basic']); 4 | -------------------------------------------------------------------------------- /nestjs/src/auth/dto/auth-connection.dto.ts: -------------------------------------------------------------------------------- 1 | import { NotificationChannelId } from '@entities/notificationChannel'; 2 | import { ApiProperty } from '@nestjs/swagger'; 3 | import { IsNotEmpty } from 'class-validator'; 4 | 5 | export class AuthConnectionDto { 6 | @IsNotEmpty() 7 | @ApiProperty() 8 | channelId: NotificationChannelId; 9 | 10 | @IsNotEmpty() 11 | @ApiProperty() 12 | externalId: string; 13 | } 14 | -------------------------------------------------------------------------------- /nestjs/src/auth/index.ts: -------------------------------------------------------------------------------- 1 | export * from './role.decorator'; 2 | export * from './role.guard'; 3 | export * from './default.guard'; 4 | export * from './auth-user.model'; 5 | export * from './auth.service'; 6 | export * from './course.guard'; 7 | -------------------------------------------------------------------------------- /nestjs/src/auth/role.decorator.ts: -------------------------------------------------------------------------------- 1 | import { SetMetadata } from '@nestjs/common'; 2 | import { Role, CourseRole } from './auth-user.model'; 3 | 4 | export const REQUIRED_ROLES_KEY = 'requiredRoles'; 5 | 6 | export const RequiredRoles = (roles: (CourseRole | Role)[], requireCourseMatch = false) => 7 | SetMetadata(REQUIRED_ROLES_KEY, { roles, requireCourseMatch }); 8 | -------------------------------------------------------------------------------- /nestjs/src/auto-test/auto-test.module.ts: -------------------------------------------------------------------------------- 1 | import { Task } from '@entities/index'; 2 | import { Module } from '@nestjs/common'; 3 | import { TypeOrmModule } from '@nestjs/typeorm'; 4 | import { AutoTestController } from './auto-test.controller'; 5 | import { AutoTestService } from './auto-test.service'; 6 | 7 | @Module({ 8 | imports: [TypeOrmModule.forFeature([Task])], 9 | controllers: [AutoTestController], 10 | providers: [AutoTestService], 11 | }) 12 | export class AutoTestModule {} 13 | -------------------------------------------------------------------------------- /nestjs/src/certificates/dto/save-certificate-dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsNumber, IsDateString, IsString } from 'class-validator'; 3 | 4 | export class SaveCertificateDto { 5 | @ApiProperty() 6 | @IsString() 7 | public publicId: string; 8 | 9 | @ApiProperty() 10 | @IsNumber() 11 | public studentId: number; 12 | 13 | @ApiProperty() 14 | @IsString() 15 | s3Bucket: string; 16 | 17 | @ApiProperty() 18 | @IsString() 19 | s3Key: string; 20 | 21 | @ApiProperty() 22 | @IsDateString() 23 | issueDate: string; 24 | } 25 | -------------------------------------------------------------------------------- /nestjs/src/cloud-api/cloud-api.module.ts: -------------------------------------------------------------------------------- 1 | import { HttpModule } from '@nestjs/axios'; 2 | import { Module } from '@nestjs/common'; 3 | import { ConfigModule } from '../config'; 4 | import { CloudApiService } from './cloud-api.service'; 5 | 6 | @Module({ 7 | imports: [HttpModule, ConfigModule], 8 | controllers: [], 9 | providers: [CloudApiService], 10 | exports: [CloudApiService], 11 | }) 12 | export class CloudApiModule {} 13 | -------------------------------------------------------------------------------- /nestjs/src/config/config.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { ConfigModule as NestConfigModule } from '@nestjs/config'; 3 | import { ConfigService } from './config.service'; 4 | 5 | @Module({ 6 | imports: [NestConfigModule.forRoot({ isGlobal: true })], 7 | controllers: [], 8 | providers: [ConfigService], 9 | exports: [ConfigService], 10 | }) 11 | export class ConfigModule {} 12 | -------------------------------------------------------------------------------- /nestjs/src/config/index.ts: -------------------------------------------------------------------------------- 1 | export * from './config.service'; 2 | export * from './config.module'; 3 | -------------------------------------------------------------------------------- /nestjs/src/constants.ts: -------------------------------------------------------------------------------- 1 | // time in seconds 2 | export const DEFAULT_CACHE_TTL = 60; // 1 minute 3 | 4 | export const ONE_HOUR_CACHE_TTL = 60 * 60; // 1 hour 5 | -------------------------------------------------------------------------------- /nestjs/src/contributors/contributors.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | import { ContributorsController } from './contributors.controller'; 4 | import { ContributorsService } from './contributors.service'; 5 | import { Contributor } from '@entities/contributor'; 6 | 7 | @Module({ 8 | imports: [TypeOrmModule.forFeature([Contributor])], 9 | controllers: [ContributorsController], 10 | providers: [ContributorsService], 11 | exports: [ContributorsService], 12 | }) 13 | export class ContributorsModule {} 14 | -------------------------------------------------------------------------------- /nestjs/src/contributors/dto/create-contributor.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsNotEmpty, IsNumber, IsString } from 'class-validator'; 3 | 4 | export class CreateContributorDto { 5 | @IsNotEmpty() 6 | @IsString() 7 | @ApiProperty() 8 | description: string; 9 | 10 | @IsNotEmpty() 11 | @IsNumber() 12 | @ApiProperty() 13 | userId: number; 14 | } 15 | -------------------------------------------------------------------------------- /nestjs/src/contributors/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './create-contributor.dto'; 2 | export * from './update-contributor.dto'; 3 | export * from './contributor.dto'; 4 | -------------------------------------------------------------------------------- /nestjs/src/contributors/dto/update-contributor.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiPropertyOptional } from '@nestjs/swagger'; 2 | import { IsNotEmpty, IsNumber, IsString } from 'class-validator'; 3 | 4 | export class UpdateContributorDto { 5 | @IsNotEmpty() 6 | @IsString() 7 | @ApiPropertyOptional() 8 | description?: string; 9 | 10 | @IsNotEmpty() 11 | @IsNumber() 12 | @ApiPropertyOptional() 13 | userId?: number; 14 | } 15 | -------------------------------------------------------------------------------- /nestjs/src/contributors/index.ts: -------------------------------------------------------------------------------- 1 | export * from './contributors.module'; 2 | -------------------------------------------------------------------------------- /nestjs/src/core/core.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { ConfigModule } from '../config'; 3 | import { JwtService } from './jwt/jwt.service'; 4 | @Module({ 5 | imports: [ConfigModule], 6 | controllers: [], 7 | providers: [JwtService], 8 | exports: [JwtService], 9 | }) 10 | export class CoreModule {} 11 | -------------------------------------------------------------------------------- /nestjs/src/core/decorators/index.ts: -------------------------------------------------------------------------------- 1 | export * from './student-id.decorator'; 2 | -------------------------------------------------------------------------------- /nestjs/src/core/decorators/student-id.decorator.ts: -------------------------------------------------------------------------------- 1 | import { createParamDecorator, ExecutionContext } from '@nestjs/common'; 2 | 3 | export const StudentId = createParamDecorator((_: unknown, ctx: ExecutionContext): number => { 4 | const request = ctx.switchToHttp().getRequest(); 5 | const courseId = request.params.courseId; 6 | return request.user.courses[courseId]?.studentId; 7 | }); 8 | -------------------------------------------------------------------------------- /nestjs/src/core/dto/id-name.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsString } from 'class-validator'; 3 | 4 | export class IdNameDto { 5 | constructor(obj: { name: string; id: number }) { 6 | this.id = obj.id; 7 | this.name = obj.name; 8 | } 9 | 10 | @IsString() 11 | @ApiProperty() 12 | name: string; 13 | 14 | @ApiProperty() 15 | id: number; 16 | } 17 | -------------------------------------------------------------------------------- /nestjs/src/core/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './person.dto'; 2 | export * from './pagination.dto'; 3 | export * from './id-name.dto'; 4 | -------------------------------------------------------------------------------- /nestjs/src/core/dto/pagination.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class PaginationDto { 4 | constructor(pageSize: number, current: number, total: number, totalPages: number) { 5 | this.pageSize = pageSize; 6 | this.current = current; 7 | this.total = total; 8 | this.totalPages = totalPages; 9 | } 10 | 11 | @ApiProperty() 12 | public pageSize: number; 13 | 14 | @ApiProperty() 15 | public current: number; 16 | 17 | @ApiProperty() 18 | public total: number; 19 | 20 | @ApiProperty() 21 | public totalPages: number; 22 | } 23 | -------------------------------------------------------------------------------- /nestjs/src/core/filters/entity-not-found.filter.ts: -------------------------------------------------------------------------------- 1 | import { ArgumentsHost, Catch, ExceptionFilter, NotFoundException } from '@nestjs/common'; 2 | import { EntityNotFoundError } from 'typeorm'; 3 | 4 | const exception = new NotFoundException(); 5 | 6 | @Catch(EntityNotFoundError) 7 | export class EntityNotFoundFilter implements ExceptionFilter { 8 | catch(_: EntityNotFoundError, host: ArgumentsHost): any { 9 | const context = host.switchToHttp(); 10 | const response = context.getResponse(); 11 | return response.status(404).json(exception.getResponse()); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /nestjs/src/core/filters/index.ts: -------------------------------------------------------------------------------- 1 | export * from './entity-not-found.filter'; 2 | export * from './sentry.filter'; 3 | -------------------------------------------------------------------------------- /nestjs/src/core/filters/sentry.filter.ts: -------------------------------------------------------------------------------- 1 | import { ArgumentsHost, Catch } from '@nestjs/common'; 2 | import { BaseExceptionFilter } from '@nestjs/core'; 3 | import * as Sentry from '@sentry/node'; 4 | 5 | @Catch() 6 | export class SentryFilter extends BaseExceptionFilter { 7 | catch(exception: unknown, host: ArgumentsHost) { 8 | Sentry.captureException(exception); 9 | super.catch(exception, host); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /nestjs/src/core/middlewares/index.ts: -------------------------------------------------------------------------------- 1 | export * from './logger.middleware'; 2 | export * from './no-cache.middleware'; 3 | -------------------------------------------------------------------------------- /nestjs/src/core/middlewares/no-cache.middleware.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NestMiddleware } from '@nestjs/common'; 2 | import { NextFunction, Request, Response } from 'express'; 3 | 4 | @Injectable() 5 | export class NoCacheMiddleware implements NestMiddleware { 6 | public use(_: Request, res: Response, next: NextFunction) { 7 | res.setHeader('Cache-Control', 'no-cache'); 8 | next(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /nestjs/src/core/subscribers/course-event.subscriber.ts: -------------------------------------------------------------------------------- 1 | import { CourseEvent } from '@entities/courseEvent'; 2 | import { EventSubscriber } from 'typeorm'; 3 | import { BaseSubscriber } from './base-subscriber'; 4 | 5 | @EventSubscriber() 6 | export class CourseEventSubscriber extends BaseSubscriber { 7 | listenTo() { 8 | return CourseEvent; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /nestjs/src/core/subscribers/course-task.subscriber.ts: -------------------------------------------------------------------------------- 1 | import { CourseTask } from '@entities/courseTask'; 2 | import { EventSubscriber } from 'typeorm'; 3 | import { BaseSubscriber } from './base-subscriber'; 4 | 5 | @EventSubscriber() 6 | export class CourseTaskSubscriber extends BaseSubscriber { 7 | listenTo() { 8 | return CourseTask; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /nestjs/src/core/validation/index.ts: -------------------------------------------------------------------------------- 1 | export * from './validation.exception'; 2 | export * from './validation.filter'; 3 | -------------------------------------------------------------------------------- /nestjs/src/core/validation/validation.exception.ts: -------------------------------------------------------------------------------- 1 | import { BadRequestException } from '@nestjs/common'; 2 | 3 | export class ValidationException extends BadRequestException { 4 | constructor(public validationErrors: string[]) { 5 | super(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /nestjs/src/courses/course-events/index.ts: -------------------------------------------------------------------------------- 1 | export * from './course-events.service'; 2 | export * from './course-events.controller'; 3 | -------------------------------------------------------------------------------- /nestjs/src/courses/course-mentors/dto/search-mentor.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class SearchMentorDto { 4 | constructor({ id, githubId, name }: { id: number; githubId: string; name: string }) { 5 | this.id = id; 6 | this.githubId = githubId; 7 | this.name = name; 8 | } 9 | 10 | @ApiProperty() 11 | id: number; 12 | 13 | @ApiProperty() 14 | githubId: string; 15 | 16 | @ApiProperty() 17 | name: string; 18 | } 19 | -------------------------------------------------------------------------------- /nestjs/src/courses/course-mentors/index.ts: -------------------------------------------------------------------------------- 1 | export * from './course-mentors.service'; 2 | export * from './course-mentors.controller'; 3 | -------------------------------------------------------------------------------- /nestjs/src/courses/course-schedule/dto/course-copy-from.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsNumber } from 'class-validator'; 3 | 4 | export class CourseCopyFromDto { 5 | @ApiProperty() 6 | @IsNumber() 7 | copyFromCourseId: number; 8 | } 9 | -------------------------------------------------------------------------------- /nestjs/src/courses/course-schedule/dto/course-schedule-hash.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsString } from 'class-validator'; 3 | 4 | export class CourseScheduleTokenDto { 5 | @ApiProperty() 6 | @IsString() 7 | public token: string; 8 | } 9 | -------------------------------------------------------------------------------- /nestjs/src/courses/course-schedule/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './course-schedule-item.dto'; 2 | export * from './course-schedule-hash.dto'; 3 | -------------------------------------------------------------------------------- /nestjs/src/courses/course-schedule/index.ts: -------------------------------------------------------------------------------- 1 | export * from './course-schedule.service'; 2 | export * from './course-schedule.controller'; 3 | export * from './course-icalendar.service'; 4 | export * from './course-icalendar.controller'; 5 | -------------------------------------------------------------------------------- /nestjs/src/courses/course-students/dto/result.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class ResultDto { 4 | @ApiProperty({ type: Number, required: false }) 5 | score?: number; 6 | 7 | @ApiProperty({ type: Number, required: false }) 8 | courseTaskId?: number; 9 | } 10 | -------------------------------------------------------------------------------- /nestjs/src/courses/course-tasks/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './course-task.dto'; 2 | export * from './course-task-detailed.dto'; 3 | -------------------------------------------------------------------------------- /nestjs/src/courses/course-tasks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './course-tasks.service'; 2 | export * from './course-tasks.controller'; 3 | -------------------------------------------------------------------------------- /nestjs/src/courses/course-users/dto/course-roles.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsBoolean, IsOptional } from 'class-validator'; 3 | 4 | export class CourseRolesDto { 5 | @IsOptional() 6 | @IsBoolean() 7 | @ApiProperty() 8 | isManager: boolean; 9 | 10 | @IsOptional() 11 | @IsBoolean() 12 | @ApiProperty() 13 | isSupervisor: boolean; 14 | 15 | @IsOptional() 16 | @IsBoolean() 17 | @ApiProperty() 18 | isDementor: boolean; 19 | 20 | @IsOptional() 21 | @IsBoolean() 22 | @ApiProperty() 23 | isActivist: boolean; 24 | } 25 | -------------------------------------------------------------------------------- /nestjs/src/courses/course-users/dto/update-user.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsNotEmpty, IsNumber } from 'class-validator'; 3 | import { CourseRolesDto } from './course-roles.dto'; 4 | 5 | export class UpdateCourseUserDto extends CourseRolesDto { 6 | @IsNotEmpty() 7 | @IsNumber() 8 | @ApiProperty() 9 | userId: number; 10 | } 11 | -------------------------------------------------------------------------------- /nestjs/src/courses/cross-checks/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './check-tasks-pairs.dto'; 2 | export * from './cross-check-feedback.dto'; 3 | export * from './cross-check-criteria-data.dto'; 4 | -------------------------------------------------------------------------------- /nestjs/src/courses/cross-checks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './course-cross-checks.controller'; 2 | export * from './course-cross-checks.service'; 3 | -------------------------------------------------------------------------------- /nestjs/src/courses/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './course.dto'; 2 | export * from './leave-course.dto'; 3 | export * from './update-course.dto'; 4 | export * from './create-course.dto'; 5 | export * from './export-course.dto'; 6 | -------------------------------------------------------------------------------- /nestjs/src/courses/dto/leave-course.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class LeaveCourseRequestDto { 4 | @ApiProperty({ required: false }) 5 | comment?: string; 6 | } 7 | -------------------------------------------------------------------------------- /nestjs/src/courses/dto/used-course.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsString } from 'class-validator'; 3 | 4 | export class UsedCourseDto { 5 | constructor(course: { name: string; isActive: boolean }) { 6 | this.isActive = course.isActive; 7 | this.name = course.name; 8 | } 9 | 10 | @IsString() 11 | @ApiProperty() 12 | name: string; 13 | 14 | @ApiProperty() 15 | isActive: boolean; 16 | } 17 | -------------------------------------------------------------------------------- /nestjs/src/courses/index.ts: -------------------------------------------------------------------------------- 1 | export * from './course-tasks/course-tasks.service'; 2 | export * from './courses.controller'; 3 | -------------------------------------------------------------------------------- /nestjs/src/courses/interviews/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './interview.dto'; 2 | -------------------------------------------------------------------------------- /nestjs/src/courses/interviews/index.ts: -------------------------------------------------------------------------------- 1 | export * from './interviews.service'; 2 | export * from './interviews.controller'; 3 | export * from './interviewFeedback.service'; 4 | -------------------------------------------------------------------------------- /nestjs/src/courses/mentor-reviews/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './mentor-reviews-query.dto'; 2 | export * from './mentor-reviews.dto'; 3 | -------------------------------------------------------------------------------- /nestjs/src/courses/mentor-reviews/dto/mentor-review-assign.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsInt } from 'class-validator'; 3 | 4 | export class MentorReviewAssignDto { 5 | @ApiProperty() 6 | @IsInt() 7 | courseTaskId: number; 8 | 9 | @ApiProperty() 10 | @IsInt() 11 | mentorId: number; 12 | 13 | @ApiProperty() 14 | @IsInt() 15 | studentId: number; 16 | } 17 | -------------------------------------------------------------------------------- /nestjs/src/courses/mentor-reviews/index.ts: -------------------------------------------------------------------------------- 1 | export * from './mentor-reviews.controller'; 2 | export * from './mentor-reviews.service'; 3 | -------------------------------------------------------------------------------- /nestjs/src/courses/mentors/dto/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/nestjs/src/courses/mentors/dto/index.ts -------------------------------------------------------------------------------- /nestjs/src/courses/mentors/index.ts: -------------------------------------------------------------------------------- 1 | export * from './mentors.service'; 2 | export * from './mentors.controller'; 3 | -------------------------------------------------------------------------------- /nestjs/src/courses/score/index.ts: -------------------------------------------------------------------------------- 1 | export * from './score.controller'; 2 | export * from './score.service'; 3 | export * from './write-score.service'; 4 | -------------------------------------------------------------------------------- /nestjs/src/courses/stats/dto/countries-stats.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString, IsInt, IsArray, ValidateNested } from 'class-validator'; 2 | import { ApiProperty } from '@nestjs/swagger'; 3 | 4 | export class CountryStatDto { 5 | @IsString() 6 | @ApiProperty() 7 | public countryName: string; 8 | 9 | @IsInt() 10 | @ApiProperty() 11 | public count: number; 12 | } 13 | 14 | export class CountriesStatsDto { 15 | @IsArray() 16 | @ValidateNested({ each: true }) 17 | @ApiProperty({ type: [CountryStatDto] }) 18 | public countries: CountryStatDto[]; 19 | } 20 | -------------------------------------------------------------------------------- /nestjs/src/courses/stats/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './course-stats.dto'; 2 | export * from './countries-stats.dto'; 3 | export * from './course-mentors-stats.dto'; 4 | export * from './task-performance-stats.dto'; 5 | -------------------------------------------------------------------------------- /nestjs/src/courses/stats/index.ts: -------------------------------------------------------------------------------- 1 | export * from './course-stats.service'; 2 | export * from './course-stats.controller'; 3 | -------------------------------------------------------------------------------- /nestjs/src/courses/students/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './student.dto'; 2 | export * from './user-students.dto'; 3 | export * from './user-students-query.dto'; 4 | -------------------------------------------------------------------------------- /nestjs/src/courses/students/feedbacks/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './create-student-feedback.dto'; 2 | export * from './student-feedback.dto'; 3 | export * from './update-student-feedback.dto'; 4 | -------------------------------------------------------------------------------- /nestjs/src/courses/students/feedbacks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './feedbacks.controller'; 2 | export * from './feedbacks.service'; 3 | -------------------------------------------------------------------------------- /nestjs/src/courses/students/index.ts: -------------------------------------------------------------------------------- 1 | export * from './students.service'; 2 | export * from './students.controller'; 3 | -------------------------------------------------------------------------------- /nestjs/src/courses/task-solutions/dto/create-task-solution.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsNotEmpty, IsUrl } from 'class-validator'; 3 | 4 | export class SaveTaskSolutionDto { 5 | @ApiProperty() 6 | @IsNotEmpty() 7 | @IsUrl() 8 | url: string; 9 | } 10 | -------------------------------------------------------------------------------- /nestjs/src/courses/task-solutions/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './task-solution.dto'; 2 | export * from './create-task-solution.dto'; 3 | -------------------------------------------------------------------------------- /nestjs/src/courses/task-solutions/index.ts: -------------------------------------------------------------------------------- 1 | export * from './task-solutions.service'; 2 | export * from './task-solutions.controller'; 3 | -------------------------------------------------------------------------------- /nestjs/src/courses/task-verifications/dto/create-task-verification.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiPropertyOptional, ApiResponse } from '@nestjs/swagger'; 2 | import { IsNumber, IsOptional } from 'class-validator'; 3 | 4 | @ApiResponse({}) 5 | export class CreateTaskVerificationDto { 6 | constructor(id?: number) { 7 | this.id = id; 8 | } 9 | 10 | @ApiPropertyOptional() 11 | @IsOptional() 12 | @IsNumber() 13 | readonly id?: number; 14 | } 15 | -------------------------------------------------------------------------------- /nestjs/src/courses/task-verifications/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './self-education.dto'; 2 | export * from './task-verifications-attempts.dto'; 3 | export * from './create-task-verification.dto'; 4 | -------------------------------------------------------------------------------- /nestjs/src/courses/tasks/dto/check-tasks-deadline.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsNumber } from 'class-validator'; 3 | 4 | export class CheckTasksDeadlineDto { 5 | @ApiProperty() 6 | @IsNumber() 7 | public deadlineInHours: number; 8 | } 9 | -------------------------------------------------------------------------------- /nestjs/src/courses/team-distribution/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './create-team-distribution.dto'; 2 | export * from './create-team.dto'; 3 | export * from './join-team.dto'; 4 | export * from './team-distribution-student.dto'; 5 | export * from './team-distribution.dto'; 6 | export * from './team.dto'; 7 | export * from './update-team-distribution.dto'; 8 | export * from './update-team-dto'; 9 | -------------------------------------------------------------------------------- /nestjs/src/courses/team-distribution/dto/join-team.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsNotEmpty, IsString } from 'class-validator'; 3 | 4 | export class JoinTeamDto { 5 | @IsString() 6 | @IsNotEmpty() 7 | @ApiProperty() 8 | public password: string; 9 | } 10 | -------------------------------------------------------------------------------- /nestjs/src/courses/team-distribution/dto/update-team-distribution.dto.ts: -------------------------------------------------------------------------------- 1 | import { CreateTeamDistributionDto } from './create-team-distribution.dto'; 2 | 3 | export class UpdateTeamDistributionDto extends CreateTeamDistributionDto {} 4 | -------------------------------------------------------------------------------- /nestjs/src/courses/team-distribution/dto/update-team-dto.ts: -------------------------------------------------------------------------------- 1 | import { CreateTeamDto } from './create-team.dto'; 2 | 3 | export class UpdateTeamDto extends CreateTeamDto {} 4 | -------------------------------------------------------------------------------- /nestjs/src/cross-check/cross-check.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | import { HttpModule } from '@nestjs/axios'; 4 | import { CourseTask } from '@entities/courseTask'; 5 | import { ConfigModule } from '../config/config.module'; 6 | import { CrossCheckService } from './cross-check.service'; 7 | 8 | @Module({ 9 | imports: [TypeOrmModule.forFeature([CourseTask]), HttpModule, ConfigModule], 10 | providers: [CrossCheckService], 11 | }) 12 | export class CrossCheckModule {} 13 | -------------------------------------------------------------------------------- /nestjs/src/disciplines/disciplines.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | import { Discipline } from '@entities/discipline'; 4 | import { DisciplinesController } from './disciplines.controller'; 5 | import { DisciplinesService } from './disciplines.service'; 6 | 7 | @Module({ 8 | imports: [TypeOrmModule.forFeature([Discipline])], 9 | controllers: [DisciplinesController], 10 | providers: [DisciplinesService], 11 | exports: [DisciplinesService], 12 | }) 13 | export class DisciplinesModule {} 14 | -------------------------------------------------------------------------------- /nestjs/src/disciplines/dto/create-discipline.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsNotEmpty, IsString } from 'class-validator'; 3 | 4 | export class CreateDisciplineDto { 5 | @IsNotEmpty() 6 | @IsString() 7 | @ApiProperty() 8 | name: string; 9 | } 10 | -------------------------------------------------------------------------------- /nestjs/src/disciplines/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './create-discipline.dto'; 2 | export * from './update-discipline.dto'; 3 | export * from './discipline.dto'; 4 | -------------------------------------------------------------------------------- /nestjs/src/disciplines/dto/update-discipline.dto.ts: -------------------------------------------------------------------------------- 1 | import { CreateDisciplineDto } from './create-discipline.dto'; 2 | 3 | export class UpdateDisciplineDto extends CreateDisciplineDto {} 4 | -------------------------------------------------------------------------------- /nestjs/src/disciplines/index.ts: -------------------------------------------------------------------------------- 1 | export * from './disciplines.module'; 2 | -------------------------------------------------------------------------------- /nestjs/src/discord-servers/discord-servers.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | import { DiscordServer } from '@entities/discordServer'; 4 | import { DiscordServersController } from './discord-servers.controller'; 5 | import { DiscordServersService } from './discord-servers.service'; 6 | 7 | @Module({ 8 | imports: [TypeOrmModule.forFeature([DiscordServer])], 9 | controllers: [DiscordServersController], 10 | providers: [DiscordServersService], 11 | }) 12 | export class DiscordServersModule {} 13 | -------------------------------------------------------------------------------- /nestjs/src/discord-servers/dto/create-discord-server.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsNotEmpty, IsString } from 'class-validator'; 3 | 4 | export class CreateDiscordServerDto { 5 | @IsNotEmpty() 6 | @IsString() 7 | @ApiProperty() 8 | name: string; 9 | 10 | @IsNotEmpty() 11 | @IsString() 12 | @ApiProperty() 13 | gratitudeUrl: string; 14 | 15 | @IsNotEmpty() 16 | @IsString() 17 | @ApiProperty() 18 | mentorsChatUrl: string; 19 | } 20 | -------------------------------------------------------------------------------- /nestjs/src/discord-servers/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './discord-server.dto'; 2 | export * from './create-discord-server.dto'; 3 | export * from './update-discord-server.dto'; 4 | -------------------------------------------------------------------------------- /nestjs/src/discord-servers/dto/update-discord-server.dto.ts: -------------------------------------------------------------------------------- 1 | import { CreateDiscordServerDto } from './create-discord-server.dto'; 2 | 3 | export class UpdateDiscordServerDto extends CreateDiscordServerDto {} 4 | -------------------------------------------------------------------------------- /nestjs/src/events/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './event.dto'; 2 | export * from './create-event.dto'; 3 | export * from './update-event.dto'; 4 | -------------------------------------------------------------------------------- /nestjs/src/events/dto/update-event.dto.ts: -------------------------------------------------------------------------------- 1 | import { CreateEventDto } from './create-event.dto'; 2 | 3 | export class UpdateEventDto extends CreateEventDto {} 4 | -------------------------------------------------------------------------------- /nestjs/src/events/events.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { EventsService } from './events.service'; 3 | import { EventsController } from './events.controller'; 4 | import { TypeOrmModule } from '@nestjs/typeorm'; 5 | import { Event } from '@entities/event'; 6 | 7 | @Module({ 8 | imports: [TypeOrmModule.forFeature([Event])], 9 | controllers: [EventsController], 10 | providers: [EventsService], 11 | }) 12 | export class EventsModule {} 13 | -------------------------------------------------------------------------------- /nestjs/src/gratitudes/dto/country.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsString } from 'class-validator'; 3 | 4 | export class CountryDto { 5 | constructor({ countryName }: { countryName: string }) { 6 | this.countryName = countryName; 7 | } 8 | 9 | @ApiProperty({ type: String }) 10 | @IsString() 11 | countryName: string; 12 | } 13 | -------------------------------------------------------------------------------- /nestjs/src/gratitudes/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './create-gratitude.dto'; 2 | export * from './gratitude.dto'; 3 | export * from './badge.dto'; 4 | export * from './heroes-radar-query.dto'; 5 | -------------------------------------------------------------------------------- /nestjs/src/gratitudes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './gratitudes.module'; 2 | -------------------------------------------------------------------------------- /nestjs/src/listeners/index.ts: -------------------------------------------------------------------------------- 1 | export { ListenersModule } from './listeners.module'; 2 | -------------------------------------------------------------------------------- /nestjs/src/listeners/listeners.module.ts: -------------------------------------------------------------------------------- 1 | import { HttpModule } from '@nestjs/axios'; 2 | import { Module } from '@nestjs/common'; 3 | import { ConfigModule } from '../config'; 4 | import { CourseListener } from './course.listener'; 5 | import { TypeOrmModule } from '@nestjs/typeorm'; 6 | import { Course } from '@entities/course'; 7 | import { Discipline } from '@entities/discipline'; 8 | 9 | @Module({ 10 | imports: [ConfigModule, HttpModule, TypeOrmModule.forFeature([Course, Discipline])], 11 | providers: [CourseListener], 12 | }) 13 | export class ListenersModule {} 14 | -------------------------------------------------------------------------------- /nestjs/src/migrations.ts: -------------------------------------------------------------------------------- 1 | export const migrations: any[] = []; 2 | -------------------------------------------------------------------------------- /nestjs/src/opportunities/dto/consent.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsBoolean } from 'class-validator'; 3 | 4 | export class ConsentDto { 5 | constructor(consent: boolean) { 6 | this.consent = consent; 7 | } 8 | 9 | @ApiProperty() 10 | @IsBoolean() 11 | public consent: boolean; 12 | } 13 | -------------------------------------------------------------------------------- /nestjs/src/opportunities/dto/give-consent-dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsBoolean, IsNumber } from 'class-validator'; 3 | 4 | export class GiveConsentDto { 5 | constructor(consent: boolean, expires: number) { 6 | this.consent = consent; 7 | this.expires = expires; 8 | } 9 | 10 | @ApiProperty() 11 | @IsBoolean() 12 | public consent: boolean; 13 | 14 | @ApiProperty() 15 | @IsNumber() 16 | public expires: number; 17 | } 18 | -------------------------------------------------------------------------------- /nestjs/src/opportunities/dto/status.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class StatusDto { 4 | constructor(expires: number) { 5 | this.expires = expires; 6 | } 7 | 8 | @ApiProperty() 9 | public expires: number; 10 | } 11 | -------------------------------------------------------------------------------- /nestjs/src/opportunities/dto/visibility.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class VisibilityDto { 4 | constructor(isHidden: boolean) { 5 | this.isHidden = isHidden; 6 | } 7 | 8 | @ApiProperty() 9 | public isHidden: boolean; 10 | } 11 | -------------------------------------------------------------------------------- /nestjs/src/profile/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './profile-course.dto'; 2 | export * from './update-user.dto'; 3 | export * from './update-profile.dto'; 4 | -------------------------------------------------------------------------------- /nestjs/src/profile/dto/profile-course.dto.ts: -------------------------------------------------------------------------------- 1 | import { Course } from '@entities/course'; 2 | import { CourseDto } from '../../courses/dto'; 3 | 4 | export class ProfileCourseDto extends CourseDto { 5 | constructor(course: Course) { 6 | super(course); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /nestjs/src/profile/dto/profile.dto.ts: -------------------------------------------------------------------------------- 1 | import { Resume } from '@entities/resume'; 2 | import { ApiProperty } from '@nestjs/swagger'; 3 | 4 | export class ProfileDto { 5 | constructor(profile: { resume: Resume | null }) { 6 | this.publicCvUrl = profile.resume?.uuid ? `/cv/${profile.resume.uuid}` : null; 7 | } 8 | 9 | @ApiProperty({ type: String, nullable: true }) 10 | public publicCvUrl: string | null; 11 | } 12 | -------------------------------------------------------------------------------- /nestjs/src/profile/index.ts: -------------------------------------------------------------------------------- 1 | export * from './profile.module'; 2 | -------------------------------------------------------------------------------- /nestjs/src/prompts/dto/create-prompt.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsNumber, IsString } from 'class-validator'; 3 | 4 | export class CreatePromptDto { 5 | @IsString() 6 | @ApiProperty() 7 | type: string; 8 | 9 | @IsString() 10 | @ApiProperty() 11 | text: string; 12 | 13 | @IsNumber() 14 | @ApiProperty() 15 | temperature: number; 16 | } 17 | -------------------------------------------------------------------------------- /nestjs/src/prompts/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './prompt.dto'; 2 | export * from './create-prompt.dto'; 3 | export * from './update-prompt.dto'; 4 | -------------------------------------------------------------------------------- /nestjs/src/prompts/dto/prompt.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { Prompt } from '@entities/prompt'; 3 | 4 | export class PromptDto { 5 | constructor(prompt: Prompt) { 6 | this.id = prompt.id; 7 | this.type = prompt.type; 8 | this.text = prompt.text; 9 | this.temperature = prompt.temperature; 10 | } 11 | 12 | @ApiProperty() 13 | id: number; 14 | 15 | @ApiProperty() 16 | type: string; 17 | 18 | @ApiProperty() 19 | text: string; 20 | 21 | @ApiProperty() 22 | temperature: number; 23 | } 24 | -------------------------------------------------------------------------------- /nestjs/src/prompts/dto/update-prompt.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsNumber, IsOptional, IsString } from 'class-validator'; 3 | 4 | export class UpdatePromptDto { 5 | @IsOptional() 6 | @IsNumber() 7 | @ApiProperty() 8 | temperature?: number; 9 | 10 | @IsOptional() 11 | @IsString() 12 | @ApiProperty() 13 | type?: string; 14 | 15 | @IsOptional() 16 | @IsString() 17 | @ApiProperty() 18 | text?: string; 19 | } 20 | -------------------------------------------------------------------------------- /nestjs/src/prompts/prompts.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | import { PromptsController } from './prompts.controller'; 4 | import { PromptsService } from './prompts.service'; 5 | import { Prompt } from '@entities/prompt'; 6 | 7 | @Module({ 8 | imports: [TypeOrmModule.forFeature([Prompt])], 9 | controllers: [PromptsController], 10 | providers: [PromptsService], 11 | }) 12 | export class PromptsModule {} 13 | -------------------------------------------------------------------------------- /nestjs/src/registry/constants.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT_PAGE_SIZE = 200; 2 | export const DEFAULT_PAGE_NUMBER = 1; 3 | -------------------------------------------------------------------------------- /nestjs/src/registry/dto/approve-mentor.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsArray } from 'class-validator'; 3 | 4 | export class ApproveMentorDto { 5 | @ApiProperty() 6 | @IsArray() 7 | preselectedCourses!: string[]; 8 | } 9 | -------------------------------------------------------------------------------- /nestjs/src/registry/dto/comment-mentor-registry.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsString } from 'class-validator'; 3 | 4 | export class CommentMentorRegistryDto { 5 | @IsString() 6 | @ApiProperty({ nullable: true, type: String }) 7 | public comment: string; 8 | } 9 | -------------------------------------------------------------------------------- /nestjs/src/registry/dto/invite-mentors.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsArray, IsBoolean, IsOptional, IsString } from 'class-validator'; 3 | 4 | export class InviteMentorsDto { 5 | @ApiProperty() 6 | @IsArray() 7 | disciplines: string[]; 8 | 9 | @ApiProperty() 10 | @IsBoolean() 11 | @IsOptional() 12 | isMentor?: boolean; 13 | 14 | @ApiProperty() 15 | @IsString() 16 | text: string; 17 | } 18 | -------------------------------------------------------------------------------- /nestjs/src/reset.d.ts: -------------------------------------------------------------------------------- 1 | import '@total-typescript/ts-reset'; 2 | -------------------------------------------------------------------------------- /nestjs/src/schedule/dto/check-schedule-changes.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsNumber, IsOptional } from 'class-validator'; 3 | 4 | export class CheckScheduleChangesDto { 5 | @ApiProperty() 6 | @IsNumber() 7 | @IsOptional() 8 | public lastHours?: number; 9 | } 10 | -------------------------------------------------------------------------------- /nestjs/src/session/session.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { SessionController } from './session.controller'; 3 | import { AuthModule } from 'src/auth/auth.module'; 4 | 5 | @Module({ 6 | imports: [AuthModule], 7 | controllers: [SessionController], 8 | }) 9 | export class SessionModule {} 10 | -------------------------------------------------------------------------------- /nestjs/src/tasks/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './create-task.dto'; 2 | export * from './update-task.dto'; 3 | export * from './task.dto'; 4 | -------------------------------------------------------------------------------- /nestjs/src/tasks/dto/update-task.dto.ts: -------------------------------------------------------------------------------- 1 | import { CreateTaskDto } from './create-task.dto'; 2 | 3 | export class UpdateTaskDto extends CreateTaskDto {} 4 | -------------------------------------------------------------------------------- /nestjs/src/tasks/tasks-criteria/dto/task-criteria.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsArray, IsNotEmpty } from 'class-validator'; 2 | import { ApiProperty } from '@nestjs/swagger'; 3 | import { CriteriaDto } from './criteria.dto'; 4 | 5 | export class TaskCriteriaDto { 6 | @IsNotEmpty() 7 | @IsArray() 8 | @ApiProperty({ type: [CriteriaDto] }) 9 | criteria: CriteriaDto[]; 10 | } 11 | -------------------------------------------------------------------------------- /nestjs/src/tasks/tasks-criteria/index.ts: -------------------------------------------------------------------------------- 1 | export { TasksCriteriaController } from './tasks-criteria.controller'; 2 | export { TasksCriteriaService } from './tasks-criteria.service'; 3 | -------------------------------------------------------------------------------- /nestjs/src/user-groups/dto/create-user-group.dto.ts: -------------------------------------------------------------------------------- 1 | import { CourseRole } from '@entities/session'; 2 | import { ApiProperty } from '@nestjs/swagger'; 3 | import { IsArray, IsNotEmpty, IsString } from 'class-validator'; 4 | 5 | export class CreateUserGroupDto { 6 | @IsNotEmpty() 7 | @IsString() 8 | @ApiProperty() 9 | name: string; 10 | 11 | @ApiProperty({ type: [Number] }) 12 | @IsArray() 13 | users: number[]; 14 | 15 | @ApiProperty({ enum: CourseRole, isArray: true }) 16 | @IsArray() 17 | roles: CourseRole[]; 18 | } 19 | -------------------------------------------------------------------------------- /nestjs/src/user-groups/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './user-group.dto'; 2 | export * from './create-user-group.dto'; 3 | export * from './update-user-group.dto'; 4 | -------------------------------------------------------------------------------- /nestjs/src/user-groups/dto/update-user-group.dto.ts: -------------------------------------------------------------------------------- 1 | import { CreateUserGroupDto } from './create-user-group.dto'; 2 | 3 | export class UpdateUserGroupDto extends CreateUserGroupDto {} 4 | -------------------------------------------------------------------------------- /nestjs/src/user-groups/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/nestjs/src/user-groups/index.ts -------------------------------------------------------------------------------- /nestjs/src/user-groups/user-groups.module.ts: -------------------------------------------------------------------------------- 1 | import { UserGroup } from '@entities/userGroup'; 2 | import { Module } from '@nestjs/common'; 3 | import { TypeOrmModule } from '@nestjs/typeorm'; 4 | import { UsersModule } from 'src/users'; 5 | import { UserGroupsController } from './user-groups.controller'; 6 | import { UserGroupsService } from './user-groups.service'; 7 | 8 | @Module({ 9 | imports: [TypeOrmModule.forFeature([UserGroup]), UsersModule], 10 | controllers: [UserGroupsController], 11 | providers: [UserGroupsService], 12 | }) 13 | export class UserGroupsModule {} 14 | -------------------------------------------------------------------------------- /nestjs/src/users-notifications/dto/update-notification-user-settings.dto.ts: -------------------------------------------------------------------------------- 1 | import { NotificationChannelId } from '@entities/notificationChannel'; 2 | import { ApiProperty } from '@nestjs/swagger'; 3 | import { IsBoolean } from 'class-validator'; 4 | 5 | export class UpdateNotificationUserSettingsDto { 6 | @ApiProperty() 7 | public notificationId: string; 8 | 9 | @ApiProperty() 10 | @IsBoolean() 11 | public enabled: boolean; 12 | 13 | @ApiProperty() 14 | public channelId: NotificationChannelId; 15 | } 16 | -------------------------------------------------------------------------------- /nestjs/src/users-notifications/index.ts: -------------------------------------------------------------------------------- 1 | export { UsersNotificationsModule } from './users-notifications.module'; 2 | export { UserNotificationsService } from './users.notifications.service'; 3 | -------------------------------------------------------------------------------- /nestjs/src/users/dto/index.ts: -------------------------------------------------------------------------------- 1 | export { UserSearchDto } from './user-search.dto'; 2 | -------------------------------------------------------------------------------- /nestjs/src/users/index.ts: -------------------------------------------------------------------------------- 1 | export * from './users.module'; 2 | -------------------------------------------------------------------------------- /nestjs/src/users/users.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { UsersService } from './users.service'; 3 | import { TypeOrmModule } from '@nestjs/typeorm'; 4 | import { User } from '@entities/user'; 5 | import { UsersController } from './users.controller'; 6 | 7 | @Module({ 8 | imports: [TypeOrmModule.forFeature([User])], 9 | providers: [UsersService], 10 | controllers: [UsersController], 11 | exports: [UsersService], 12 | }) 13 | export class UsersModule {} 14 | -------------------------------------------------------------------------------- /nestjs/test/jest-e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": ".", 4 | "testEnvironment": "node", 5 | "testRegex": ".e2e-spec.ts$", 6 | "transform": { 7 | "^.+\\.(t|j)s$": "ts-jest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /nestjs/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /server/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .turbo 3 | -------------------------------------------------------------------------------- /server/.env.example: -------------------------------------------------------------------------------- 1 | TZ=utc 2 | 3 | RSSHCOOL_PG_HOST=localhost 4 | RSSHCOOL_PG_USERNAME=rs_master 5 | RSSHCOOL_PG_PASSWORD=12345678 6 | RSSHCOOL_PG_DATABASE=rs_school 7 | 8 | RSSHCOOL_API_ADMIN_USERNAME=admin 9 | RSSHCOOL_API_ADMIN_PASSWORD=password 10 | -------------------------------------------------------------------------------- /server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:20-alpine 2 | 3 | EXPOSE 8080 4 | 5 | ENV NODE_ENV production 6 | ENV NODE_PORT 8080 7 | ENV TZ utc 8 | ENV RS_ENV production 9 | 10 | WORKDIR /app 11 | 12 | COPY server/tsconfig.json /app/server/ 13 | COPY server/package.json /app/server/ 14 | 15 | COPY package.json /app 16 | COPY package-lock.json /app 17 | 18 | RUN npm install --production --no-optional 19 | 20 | COPY server/public /app/server/public 21 | COPY server/dist /app/server/dist 22 | 23 | CMD [ "node", "/app/server/dist/server/src/index.js" ] 24 | -------------------------------------------------------------------------------- /server/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import defaultConfig from '../eslint.config.mjs'; 2 | 3 | export default defaultConfig; 4 | -------------------------------------------------------------------------------- /server/jest.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | globals: { 3 | NODE_ENV: 'test', 4 | }, 5 | preset: 'ts-jest', 6 | testEnvironment: 'node', 7 | moduleDirectories: ['node_modules', 'src'], 8 | moduleFileExtensions: ['ts', 'js', 'json'], 9 | transform: { 10 | '^.+\\.(ts|tsx)$': 'ts-jest', 11 | }, 12 | testPathIgnorePatterns: ['/node_modules/', '/dist/'], 13 | verbose: true, 14 | }; 15 | -------------------------------------------------------------------------------- /server/src/dataSource.ts: -------------------------------------------------------------------------------- 1 | import 'dotenv/config'; 2 | 3 | import { DataSource } from 'typeorm'; 4 | import { dataSourceOptions } from './dataSourceOptions'; 5 | 6 | export default new DataSource(dataSourceOptions); 7 | -------------------------------------------------------------------------------- /server/src/index.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata'; // for typeorm 2 | import { App } from './app'; 3 | 4 | const app = new App(); 5 | app 6 | .pgConnect() 7 | .then(() => app.start(true)) 8 | .then(() => app.startBackgroundJobs()) 9 | .catch(e => console.error(e)); 10 | -------------------------------------------------------------------------------- /server/src/migrations/1630341383942-TaskResult.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class TaskResult1630341383942 implements MigrationInterface { 4 | public async up(queryRunner: QueryRunner): Promise { 5 | await queryRunner.query(` 6 | UPDATE "task_result" 7 | SET "lastCheckerId" = CAST("historicalScores"->-1->>'authorId' AS INT) 8 | WHERE CAST("historicalScores"->-1->>'authorId' AS INT) > 0 9 | `); 10 | } 11 | 12 | public async down(_: QueryRunner): Promise { 13 | // do nothing 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server/src/migrations/1638302439645-CourseMigration.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class CourseMigration1638302439645 implements MigrationInterface { 4 | name = 'CourseMigration1638302439645'; 5 | 6 | public async up(queryRunner: QueryRunner): Promise { 7 | await queryRunner.query(`ALTER TABLE "course" ADD "personalMentoring" boolean NOT NULL DEFAULT true`); 8 | } 9 | 10 | public async down(queryRunner: QueryRunner): Promise { 11 | await queryRunner.query(`ALTER TABLE "course" DROP COLUMN "personalMentoring"`); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/src/migrations/1639502600339-Student.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class Student1639502600339 implements MigrationInterface { 4 | name = 'Student1639502600339'; 5 | 6 | public async up(queryRunner: QueryRunner): Promise { 7 | await queryRunner.query(`ALTER TABLE "student" ADD "mentoring" boolean DEFAULT true`); 8 | } 9 | 10 | public async down(queryRunner: QueryRunner): Promise { 11 | await queryRunner.query(`ALTER TABLE "student" DROP COLUMN "mentoring"`); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/src/migrations/1642884123347-ResumeSelectCourses.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class ResumeSelectCourses1642884123347 implements MigrationInterface { 4 | name = 'ResumeSelectCourses1642884123347'; 5 | 6 | public async up(queryRunner: QueryRunner): Promise { 7 | await queryRunner.query(`ALTER TABLE "resume" ADD "visibleCourses" integer array NOT NULL DEFAULT '{}'`); 8 | } 9 | 10 | public async down(queryRunner: QueryRunner): Promise { 11 | await queryRunner.query(`ALTER TABLE "resume" DROP COLUMN "visibleCourses"`); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/src/migrations/1643481312933-Task.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class Task1643481312933 implements MigrationInterface { 4 | name = 'Task1643481312933'; 5 | 6 | public async up(queryRunner: QueryRunner): Promise { 7 | await queryRunner.query(`ALTER TABLE "task" ADD "skills" text NOT NULL DEFAULT ''`); 8 | } 9 | 10 | public async down(queryRunner: QueryRunner): Promise { 11 | await queryRunner.query(`ALTER TABLE "task" DROP COLUMN "skills"`); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/src/migrations/1645364514538-RepositoryEvent.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class RepositoryEvent1645364514538 implements MigrationInterface { 4 | name = 'RepositoryEvent1645364514538'; 5 | 6 | public async up(queryRunner: QueryRunner): Promise { 7 | await queryRunner.query(`ALTER TABLE "repository_event" ADD "userId" integer`); 8 | } 9 | 10 | public async down(queryRunner: QueryRunner): Promise { 11 | await queryRunner.query(`ALTER TABLE "repository_event" DROP COLUMN "userId"`); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/src/migrations/1650652882300-DiscordChannel.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class DiscordChannel1650652882300 implements MigrationInterface { 4 | name = 'DiscordChannel1650652882300'; 5 | 6 | public async up(queryRunner: QueryRunner): Promise { 7 | await queryRunner.query(`INSERT INTO "notification_channel" (id) VALUES('discord')`); 8 | } 9 | 10 | public async down(queryRunner: QueryRunner): Promise { 11 | await queryRunner.query(`DELETE FROM "notification_channel" WHERE "id" = 'discord'`); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/src/migrations/1652870756742-Resume.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class Resume1652870756742 implements MigrationInterface { 4 | name = 'Resume1652870756742'; 5 | 6 | public async up(queryRunner: QueryRunner): Promise { 7 | await queryRunner.query(`ALTER TABLE "resume" ADD "updatedDate" TIMESTAMP NOT NULL DEFAULT now()`); 8 | } 9 | 10 | public async down(queryRunner: QueryRunner): Promise { 11 | await queryRunner.query(`ALTER TABLE "resume" DROP COLUMN "updatedDate"`); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/src/migrations/1664183799115-CourseEvent.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class CourseEvent1664183799115 implements MigrationInterface { 4 | name = 'CourseEvent1664183799115'; 5 | 6 | public async up(queryRunner: QueryRunner): Promise { 7 | await queryRunner.query(`ALTER TABLE "course_event" ADD "endTime" TIMESTAMP WITH TIME ZONE`); 8 | } 9 | 10 | public async down(queryRunner: QueryRunner): Promise { 11 | await queryRunner.query(`ALTER TABLE "course_event" DROP COLUMN "endTime"`); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/src/migrations/1666621080327-TaskSolutionResult.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class TaskSolutionResult1666621080327 implements MigrationInterface { 4 | name = 'TaskSolutionResult1666621080327'; 5 | 6 | public async up(queryRunner: QueryRunner): Promise { 7 | await queryRunner.query(`ALTER TABLE "task_solution_result" ADD "messages" json NOT NULL DEFAULT '[]'`); 8 | } 9 | 10 | public async down(queryRunner: QueryRunner): Promise { 11 | await queryRunner.query(`ALTER TABLE "task_solution_result" DROP COLUMN "messages"`); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/src/migrations/1671475396333-Tasks.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class Tasks1671475396333 implements MigrationInterface { 4 | name = 'Tasks1671475396333'; 5 | 6 | public async up(queryRunner: QueryRunner): Promise { 7 | await queryRunner.query(`ALTER TABLE "task" ADD "deletedDate" TIMESTAMP`); 8 | } 9 | 10 | public async down(queryRunner: QueryRunner): Promise { 11 | await queryRunner.query(`ALTER TABLE "task" DROP COLUMN "deletedDate"`); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/src/migrations/1673090827105-TaskVerification.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class TaskVerification1673090827105 implements MigrationInterface { 4 | name = 'TaskVerification1673090827105'; 5 | 6 | public async up(queryRunner: QueryRunner): Promise { 7 | await queryRunner.query(`ALTER TABLE "task_verification" ADD "answers" json NOT NULL DEFAULT '[]'`); 8 | } 9 | 10 | public async down(queryRunner: QueryRunner): Promise { 11 | await queryRunner.query(`ALTER TABLE "task_verification" DROP COLUMN "answers"`); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/src/migrations/1673692838338-User.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class User1673692838338 implements MigrationInterface { 4 | name = 'User1673692838338'; 5 | 6 | public async up(queryRunner: QueryRunner): Promise { 7 | await queryRunner.query(`ALTER TABLE "user" ADD "contactsWhatsApp" character varying`); 8 | } 9 | 10 | public async down(queryRunner: QueryRunner): Promise { 11 | await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "contactsWhatsApp"`); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/src/migrations/1675245424426-UserGroup.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class UserGroup1675245424426 implements MigrationInterface { 4 | name = 'UserGroup1675245424426'; 5 | 6 | public async up(queryRunner: QueryRunner): Promise { 7 | await queryRunner.query(`ALTER TABLE "course_user" ADD "isDementor" boolean NOT NULL DEFAULT false`); 8 | } 9 | 10 | public async down(queryRunner: QueryRunner): Promise { 11 | await queryRunner.query(`ALTER TABLE "course_user" DROP COLUMN "isDementor"`); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/src/migrations/1676139987317-User.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class User1676139987317 implements MigrationInterface { 4 | name = 'User1676139987317'; 5 | 6 | public async up(queryRunner: QueryRunner): Promise { 7 | await queryRunner.query(`ALTER TABLE "user" ADD "languages" text array NOT NULL DEFAULT '{}'`); 8 | } 9 | 10 | public async down(queryRunner: QueryRunner): Promise { 11 | await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "languages"`); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/src/migrations/1691520611773-Temperature.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class Temperature1691520611773 implements MigrationInterface { 4 | name = 'Temperature1691520611773'; 5 | 6 | public async up(queryRunner: QueryRunner): Promise { 7 | await queryRunner.query(`ALTER TABLE "prompt" ADD "temperature" integer NOT NULL`); 8 | } 9 | 10 | public async down(queryRunner: QueryRunner): Promise { 11 | await queryRunner.query(`ALTER TABLE "prompt" DROP COLUMN "temperature"`); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/src/migrations/1693930286280-CourseUsersActivist.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class CourseUsersActivist1693930286280 implements MigrationInterface { 4 | name = 'CourseUsersActivist1693930286280'; 5 | 6 | public async up(queryRunner: QueryRunner): Promise { 7 | await queryRunner.query(`ALTER TABLE "course_user" ADD "isActivist" boolean NOT NULL DEFAULT false`); 8 | } 9 | 10 | public async down(queryRunner: QueryRunner): Promise { 11 | await queryRunner.query(`ALTER TABLE "course_user" DROP COLUMN "isActivist"`); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/src/migrations/1700391857109-Obfuscation.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class Obfuscation1700391857109 implements MigrationInterface { 4 | name = 'Obfuscation1700391857109'; 5 | 6 | public async up(queryRunner: QueryRunner): Promise { 7 | await queryRunner.query(`ALTER TABLE "user" ADD "obfuscated" boolean NOT NULL DEFAULT false`); 8 | } 9 | 10 | public async down(queryRunner: QueryRunner): Promise { 11 | await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "obfuscated"`); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/src/migrations/1712137476312-Course.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class Course1712137476312 implements MigrationInterface { 4 | name = 'Course1712137476312'; 5 | 6 | public async up(queryRunner: QueryRunner): Promise { 7 | await queryRunner.query(`ALTER TABLE "course" ADD "certificateThreshold" integer DEFAULT '70'`); 8 | } 9 | 10 | public async down(queryRunner: QueryRunner): Promise { 11 | await queryRunner.query(`ALTER TABLE "course" DROP COLUMN "certificateThreshold"`); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/src/migrations/1730926720293-CourseTask.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class CourseTask1730926720293 implements MigrationInterface { 4 | name = 'CourseTask1730926720293'; 5 | 6 | public async up(queryRunner: QueryRunner): Promise { 7 | await queryRunner.query(`ALTER TABLE "course_task" ADD "studentRegistrationStartDate" TIMESTAMP WITH TIME ZONE`); 8 | } 9 | 10 | public async down(queryRunner: QueryRunner): Promise { 11 | await queryRunner.query(`ALTER TABLE "course_task" DROP COLUMN "studentRegistrationStartDate"`); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/src/migrations/1736458672717-Course.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm'; 2 | 3 | export class Course1736458672717 implements MigrationInterface { 4 | name = 'Course1736458672717'; 5 | 6 | public async up(queryRunner: QueryRunner): Promise { 7 | await queryRunner.query(`ALTER TABLE "course" ADD "wearecommunityUrl" character varying`); 8 | } 9 | 10 | public async down(queryRunner: QueryRunner): Promise { 11 | await queryRunner.query(`ALTER TABLE "course" DROP COLUMN "wearecommunityUrl"`); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/src/models/courseManager.ts: -------------------------------------------------------------------------------- 1 | import { CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'; 2 | import { Course } from './course'; 3 | import { User } from './user'; 4 | 5 | @Entity() 6 | export class CourseManager { 7 | @PrimaryGeneratedColumn() id: number; 8 | 9 | @CreateDateColumn() 10 | createdDate: number; 11 | 12 | @UpdateDateColumn() 13 | updatedDate: number; 14 | 15 | @ManyToOne(_ => Course) 16 | course: Course; 17 | 18 | @ManyToOne(_ => User) 19 | user: User; 20 | } 21 | -------------------------------------------------------------------------------- /server/src/models/data/available-languages.data.ts: -------------------------------------------------------------------------------- 1 | export enum AvailableLanguages { 2 | EN = 'EN', 3 | ZH = 'ZH', 4 | HI = 'HI', 5 | ES = 'ES', 6 | FR = 'FR', 7 | AR = 'AR', 8 | BN = 'BN', 9 | RU = 'RU', 10 | PT = 'PT', 11 | ID = 'ID', 12 | UR = 'UR', 13 | JA = 'JA', 14 | DE = 'DE', 15 | PA = 'PA', 16 | TE = 'TE', 17 | TR = 'TR', 18 | KO = 'KO', 19 | MR = 'MR', 20 | KY = 'KY', 21 | KK = 'KK', 22 | UZ = 'UZ', 23 | KA = 'KA', 24 | PL = 'PL', 25 | LT = 'LT', 26 | LV = 'LV', 27 | BE = 'BE', 28 | UK = 'UK', 29 | } 30 | -------------------------------------------------------------------------------- /server/src/models/data/index.ts: -------------------------------------------------------------------------------- 1 | export * from './language-levels.data'; 2 | export * from './available-languages.data'; 3 | -------------------------------------------------------------------------------- /server/src/models/data/language-levels.data.ts: -------------------------------------------------------------------------------- 1 | export enum LanguageLevel { 2 | Unkwown = 'unknown', 3 | A0 = 'a0', 4 | A1 = 'a1', 5 | A2 = 'a2', 6 | B1 = 'b1', 7 | B2 = 'b2', 8 | C1 = 'c1', 9 | C2 = 'c2', 10 | } 11 | -------------------------------------------------------------------------------- /server/src/models/discipline.ts: -------------------------------------------------------------------------------- 1 | import { Column, CreateDateColumn, DeleteDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'; 2 | 3 | @Entity() 4 | export class Discipline { 5 | @PrimaryGeneratedColumn({ name: 'id' }) 6 | public id: number; 7 | 8 | @CreateDateColumn({ name: 'created_date' }) 9 | public createdDate: string; 10 | 11 | @UpdateDateColumn({ name: 'updated_date' }) 12 | public updatedDate: string; 13 | 14 | @DeleteDateColumn({ name: 'deleted_date' }) 15 | public deletedDate: string; 16 | 17 | @Column({ name: 'name' }) 18 | public name: string; 19 | } 20 | -------------------------------------------------------------------------------- /server/src/models/notificationChannel.ts: -------------------------------------------------------------------------------- 1 | import { Entity, CreateDateColumn, PrimaryColumn, UpdateDateColumn } from 'typeorm'; 2 | 3 | @Entity() 4 | export class NotificationChannel { 5 | @PrimaryColumn() 6 | id: NotificationChannelId; 7 | 8 | @CreateDateColumn() 9 | createdDate: number; 10 | 11 | @UpdateDateColumn() 12 | updatedDate: number; 13 | } 14 | 15 | export type NotificationChannelId = 'telegram' | 'email' | 'discord'; 16 | -------------------------------------------------------------------------------- /server/src/models/prompt.ts: -------------------------------------------------------------------------------- 1 | import { Entity, Column, CreateDateColumn, UpdateDateColumn, PrimaryGeneratedColumn, Unique } from 'typeorm'; 2 | 3 | @Entity() 4 | @Unique(['type']) 5 | export class Prompt { 6 | @PrimaryGeneratedColumn() 7 | id: number; 8 | 9 | @CreateDateColumn() 10 | createdDate: number; 11 | 12 | @UpdateDateColumn() 13 | updatedDate: number; 14 | 15 | @Column({ type: 'varchar', length: 256 }) 16 | type: string; 17 | 18 | @Column({ type: 'float' }) 19 | temperature: number; 20 | 21 | @Column() 22 | text: string; 23 | } 24 | -------------------------------------------------------------------------------- /server/src/models/userGroup.ts: -------------------------------------------------------------------------------- 1 | import { Entity, Column, CreateDateColumn, UpdateDateColumn, PrimaryGeneratedColumn } from 'typeorm'; 2 | import { CourseRole } from './session'; 3 | 4 | @Entity() 5 | export class UserGroup { 6 | @PrimaryGeneratedColumn() id: number; 7 | 8 | @CreateDateColumn() 9 | createdDate: number; 10 | 11 | @UpdateDateColumn() 12 | updatedDate: number; 13 | 14 | @Column() 15 | name: string; 16 | 17 | @Column('int', { array: true }) 18 | users: number[]; 19 | 20 | @Column('text', { array: true }) 21 | roles: CourseRole[]; 22 | } 23 | -------------------------------------------------------------------------------- /server/src/reset.d.ts: -------------------------------------------------------------------------------- 1 | import '@total-typescript/ts-reset'; 2 | -------------------------------------------------------------------------------- /server/src/routes/checks/getBadComment.ts: -------------------------------------------------------------------------------- 1 | import { OK } from 'http-status-codes'; 2 | import { RouterContext } from '../guards'; 3 | import { ILogger } from '../../logger'; 4 | import { setResponse } from '../utils'; 5 | import { getCheckersWithoutComments } from '../../services/check.service'; 6 | 7 | export const getBadComment = (_: ILogger) => async (ctx: RouterContext) => { 8 | const { taskId } = ctx.params; 9 | 10 | const badCheckers = await getCheckersWithoutComments(Number(taskId)); 11 | 12 | setResponse(ctx, OK, badCheckers); 13 | }; 14 | -------------------------------------------------------------------------------- /server/src/routes/checks/getMaxScoreCheckers.ts: -------------------------------------------------------------------------------- 1 | import { OK } from 'http-status-codes'; 2 | import { RouterContext } from '../guards'; 3 | import { ILogger } from '../../logger'; 4 | import { setResponse } from '../utils'; 5 | import { getCheckersWithMaxScore } from '../../services/check.service'; 6 | 7 | export const getMaxScoreCheckers = (_: ILogger) => async (ctx: RouterContext) => { 8 | const { taskId } = ctx.params; 9 | 10 | const badCheckers = await getCheckersWithMaxScore(Number(taskId)); 11 | 12 | setResponse(ctx, OK, badCheckers); 13 | }; 14 | -------------------------------------------------------------------------------- /server/src/routes/course/crossCheck/index.ts: -------------------------------------------------------------------------------- 1 | export * from './createCompletion'; 2 | export * from './createDistribution'; 3 | export * from './createResult'; 4 | export * from './createSolution'; 5 | export * from './createMessage'; 6 | export * from './updateMessage'; 7 | export * from './deleteSolution'; 8 | export * from './getAssignments'; 9 | export * from './getResult'; 10 | export * from './getSolution'; 11 | export * from './getTaskDetails'; 12 | -------------------------------------------------------------------------------- /server/src/routes/course/score/index.ts: -------------------------------------------------------------------------------- 1 | export * from './createMultipleScores'; 2 | export * from './createSingleScore'; 3 | export * from './getScoreCsv'; 4 | export * from './getScoreByStudent'; 5 | export * from './recalculateScore'; 6 | -------------------------------------------------------------------------------- /server/src/routes/course/stageInterview/index.ts: -------------------------------------------------------------------------------- 1 | export { createFeedback } from './createFeedback'; 2 | export { createInterview } from './createInterview'; 3 | export { createInterviews } from './createInterviews'; 4 | export { cancelInterview } from './cancelInterview'; 5 | export { getFeedback } from './getFeedback'; 6 | export { getInterviewStudent } from './getInterviewStudent'; 7 | export { getInterviewerStudents } from './getInterviewerStudents'; 8 | export { getInterviews } from './getInterviews'; 9 | export { updateInterview } from './updateInterview'; 10 | -------------------------------------------------------------------------------- /server/src/routes/course/tasks/index.ts: -------------------------------------------------------------------------------- 1 | export { createCourseTaskDistribution } from './createCourseTaskDistribution'; 2 | export { getCourseTasksDetails } from './getCourseTasksDetails'; 3 | -------------------------------------------------------------------------------- /server/src/routes/file/index.ts: -------------------------------------------------------------------------------- 1 | import Router from '@koa/router'; 2 | import { ILogger } from '../../logger'; 3 | import { guard } from '../guards'; 4 | import { uploadFile } from './upload'; 5 | 6 | export function filesRoute(logger: ILogger) { 7 | const router = new Router({ prefix: '/file' }); 8 | 9 | router.post('/upload', guard, uploadFile(logger)); 10 | 11 | return router; 12 | } 13 | -------------------------------------------------------------------------------- /server/src/routes/repository/index.ts: -------------------------------------------------------------------------------- 1 | import Router from '@koa/router'; 2 | import { ILogger } from '../../logger'; 3 | import { basicAuthAws } from '../guards'; 4 | import { createRepositoryEvents } from './events'; 5 | 6 | export function repositoryRoute(logger: ILogger) { 7 | const router = new Router({ prefix: '/repository' }); 8 | 9 | router.post('/events', basicAuthAws, createRepositoryEvents(logger)); 10 | 11 | return router; 12 | } 13 | -------------------------------------------------------------------------------- /server/src/routes/taskVerification/index.ts: -------------------------------------------------------------------------------- 1 | import Router from '@koa/router'; 2 | import { ILogger } from '../../logger'; 3 | import { TaskVerification } from '../../models'; 4 | import { createPostRoute } from '../common'; 5 | import { adminGuard } from '../guards'; 6 | 7 | export function taskVerification(logger: ILogger) { 8 | const router = new Router({ prefix: '/task-verification' }); 9 | 10 | router.post('/', adminGuard, createPostRoute(TaskVerification, logger)); 11 | 12 | return router; 13 | } 14 | -------------------------------------------------------------------------------- /server/src/rules/index.ts: -------------------------------------------------------------------------------- 1 | export * from './name'; 2 | -------------------------------------------------------------------------------- /server/src/rules/name.ts: -------------------------------------------------------------------------------- 1 | export const getFullName = (firstName: string, lastName: string, githubId: string) => 2 | [firstName, lastName].filter(Boolean).join(' ') || githubId; 3 | -------------------------------------------------------------------------------- /server/src/rules/types.ts: -------------------------------------------------------------------------------- 1 | export type TaskOwnerCourses = { 2 | id: number; 3 | tasksIds: number[]; 4 | }; 5 | 6 | export type TaskOwnerRole = { 7 | courses: TaskOwnerCourses[]; 8 | }; 9 | -------------------------------------------------------------------------------- /server/src/services/distribution/index.ts: -------------------------------------------------------------------------------- 1 | export * from './crossCheckDistribution.service'; 2 | export * from './crossMentorDistribution.service'; 3 | -------------------------------------------------------------------------------- /server/src/services/operationResult.ts: -------------------------------------------------------------------------------- 1 | export interface OperationResult { 2 | status: 'created' | 'updated' | 'deleted' | 'skipped' | 'failed'; 3 | value: any; 4 | } 5 | -------------------------------------------------------------------------------- /server/src/services/score/index.ts: -------------------------------------------------------------------------------- 1 | export * from './score.service'; 2 | -------------------------------------------------------------------------------- /server/src/services/taskVerification.service.ts: -------------------------------------------------------------------------------- 1 | import { TaskVerification } from '../models'; 2 | import { getRepository } from 'typeorm'; 3 | 4 | export async function cancelPendingTasks() { 5 | return getRepository(TaskVerification) 6 | .createQueryBuilder() 7 | .update() 8 | .set({ status: 'cancelled' }) 9 | .where(`"updatedDate" + interval '1 hour' < now()::timestamp AND status = 'pending'`) 10 | .execute(); 11 | } 12 | -------------------------------------------------------------------------------- /server/swaggerDef.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | info: { 3 | title: 'rs.school API', 4 | version: '1.0.0', 5 | description: '', 6 | }, 7 | basePath: '/api', 8 | }; 9 | -------------------------------------------------------------------------------- /setup/cdk/App.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from 'aws-cdk-lib'; 2 | import { RsSchoolAppStack } from './Stack'; 3 | 4 | const app = new cdk.App(); 5 | 6 | const feature = app.node.tryGetContext('feature') ?? 'master'; 7 | const deployId = app.node.tryGetContext('deployId') ?? new Date().getTime().toString(); 8 | 9 | new RsSchoolAppStack(app, `rsschool-app-${feature}`, { 10 | env: { account: '511361162520', region: 'eu-central-1' }, 11 | feature, 12 | deployId, 13 | certificateArn: 'arn:aws:acm:us-east-1:511361162520:certificate/07e01035-1bb4-430c-8b82-625565f66bdb', 14 | }); 15 | -------------------------------------------------------------------------------- /setup/cdk/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx tsx App.ts", 3 | "context": { 4 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, 5 | "@aws-cdk/core:stackRelativeExports": true, 6 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 7 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true, 8 | "@aws-cdk/core:target-partitions": ["aws", "aws-cn"] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /setup/cdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "name": "cdk", 4 | "dependencies": { 5 | "aws-cdk": "2.1012.0", 6 | "aws-cdk-lib": "2.192.0", 7 | "constructs": "^10", 8 | "tsx": "^4.19.2" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /setup/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | postgres: 3 | image: postgres:15.5 4 | container_name: db 5 | restart: always 6 | #volumes: 7 | # - /tmp/postgresql:/var/lib/postgresql 8 | environment: 9 | POSTGRES_PASSWORD: 12345678 10 | POSTGRES_USER: rs_master 11 | POSTGRES_DB: rs_school 12 | PGDATA: /tmp 13 | ports: 14 | - 5432:5432 15 | -------------------------------------------------------------------------------- /setup/dump-local.sh: -------------------------------------------------------------------------------- 1 | pg_dump -h localhost --username rs_master rs_school --file backup-local.sql -------------------------------------------------------------------------------- /setup/dump.sh: -------------------------------------------------------------------------------- 1 | pg_dump -h $RSSCHOOL_AWS_RDS_HOST --username rs_master rs_school --file backup.sql 2 | -------------------------------------------------------------------------------- /setup/restore-local.sh: -------------------------------------------------------------------------------- 1 | podman exec -i db psql -U rs_master -d rs_school < ./backup-local.sql 2 | -------------------------------------------------------------------------------- /setup/restore.sh: -------------------------------------------------------------------------------- 1 | podman exec -i db psql -U rs_master -d rs_school < ./backup.sql 2 | -------------------------------------------------------------------------------- /tools/bumblebee/.env.example: -------------------------------------------------------------------------------- 1 | RS_BUMBLEBEE_TELEGRAM_TOKEN= 2 | RS_BUMBLEBEE_TELEGRAM_ADMIN_CHANNEL_ID= 3 | RS_BUMBLEBEE_TELEGRAM_CHANNEL_ID= 4 | RS_BUMBLEBEE_DISCORD_TOKEN= 5 | RS_BUMBLEBEE_DISCORD_CHANNEL_NAMES=announcements 6 | RS_BUMBLEBEE_LIMIT_INITIAL_MESSAGES_COUNT=50 7 | RS_BUMBLEBEE_PAUSE_BETWEEN_TELEGRAM_SENDS=2000 8 | -------------------------------------------------------------------------------- /tools/bumblebee/.gitignore: -------------------------------------------------------------------------------- 1 | timestamp.json 2 | -------------------------------------------------------------------------------- /tools/bumblebee/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16 2 | 3 | EXPOSE 7000 4 | 5 | ENV NODE_ENV production 6 | ENV NODE_PORT 7000 7 | ENV TZ utc 8 | 9 | WORKDIR /app 10 | 11 | RUN mkdir -p /tmp/timestamp 12 | VOLUME /tmp/timestamp 13 | 14 | COPY package.json /app 15 | COPY package-lock.json /app 16 | RUN npm install --production --no-optional 17 | 18 | COPY src /app/src 19 | 20 | CMD [ "npm", "start" ] 21 | -------------------------------------------------------------------------------- /tools/bumblebee/README.md: -------------------------------------------------------------------------------- 1 | # rs-bumblebee 2 | RS Bumblebee is a bridge bot for transfer messages from Discord to Telegram created especially for the Rolling Scopes School 3 | 4 | ## Developing 5 | These librares are using for developing: 6 | - [node-telegram-bot-api](https://github.com/yagop/node-telegram-bot-api/blob/master/doc/api.md) 7 | - [discord.js](https://discord.js.org/#/docs/main/stable/general/welcome) 8 | 9 | -------------------------------------------------------------------------------- /tools/bumblebee/src/constants/defaults.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | LIMIT_INITIAL_MESSAGES_COUNT: 50, 3 | PAUSE_BETWEEN_TELEGRAM_SENDS: 2000, 4 | }; 5 | -------------------------------------------------------------------------------- /tools/bumblebee/src/constants/discord-bridge.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | LOCALE: 'en', 3 | TIMEZONE: 'Europe/Minsk', 4 | TIME_FORMAT: 'llll', 5 | TELEGRAM_PARSE_MODE: 'Markdown', 6 | TIMESTAMP_FILE_NAME: 'timestamp.json', 7 | }; 8 | -------------------------------------------------------------------------------- /tools/bumblebee/src/discord/events/index.js: -------------------------------------------------------------------------------- 1 | const ready = require('./ready'); 2 | const message = require('./message'); 3 | const messageUpdate = require('./message-update'); 4 | const messageDelete = require('./message-delete'); 5 | 6 | module.exports = { 7 | ready, 8 | message, 9 | messageUpdate, 10 | messageDelete, 11 | }; 12 | -------------------------------------------------------------------------------- /tools/bumblebee/src/discord/index.js: -------------------------------------------------------------------------------- 1 | const { createClient, listen } = require('./discord-bridge'); 2 | 3 | module.exports = async (telegramBot) => { 4 | const client = await createClient(); 5 | 6 | listen(client, telegramBot); 7 | }; 8 | -------------------------------------------------------------------------------- /tools/bumblebee/src/index.js: -------------------------------------------------------------------------------- 1 | const dotenv = require('dotenv'); 2 | const discord = require('./discord'); 3 | const telegram = require('./telegram'); 4 | 5 | dotenv.config(); 6 | 7 | const telegramBot = telegram(); 8 | discord(telegramBot); 9 | 10 | global.console.log(`rs-bumblebee is started!`); 11 | -------------------------------------------------------------------------------- /tools/bumblebee/src/lib/common.js: -------------------------------------------------------------------------------- 1 | module.exports.catcher = handler => async (...args) => { 2 | try { 3 | const result = handler(...args); 4 | if (result instanceof Promise) { 5 | return await result; 6 | } 7 | return result; 8 | } catch (err) { 9 | return global.console.log('Error =>', err.message); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /tools/bumblebee/src/telegram/events/help.js: -------------------------------------------------------------------------------- 1 | module.exports = ({ client }) => ({ 2 | description: 'Show this message', 3 | method: ({ chat: { id } }) => { 4 | // eslint-disable-next-line global-require 5 | const events = require('./index'); 6 | 7 | const message = Object.keys(events) 8 | .map(eventName => `/${eventName} : ${events[eventName]({ client }).description}`) 9 | .join('\n'); 10 | 11 | client.sendMessage(id, message, { parse_mode: 'Markdown' }); 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /tools/bumblebee/src/telegram/events/index.js: -------------------------------------------------------------------------------- 1 | const start = require('./start'); 2 | const ping = require('./ping'); 3 | const help = require('./help'); 4 | 5 | module.exports = { 6 | start, 7 | ping, 8 | help, 9 | }; 10 | -------------------------------------------------------------------------------- /tools/bumblebee/src/telegram/events/ping.js: -------------------------------------------------------------------------------- 1 | module.exports = ({ client }) => ({ 2 | description: 'Show status if rs-bumblebee is available', 3 | method: ({ chat: { id } }) => { 4 | const messages = [ 5 | 'I\'m here!', 6 | 'Sir, yes, sir!', 7 | 'I am waiting for orders.', 8 | ]; 9 | 10 | console.log(id); 11 | const randomIndex = Math.floor(Math.random() * messages.length); 12 | 13 | client.sendMessage(id, messages[randomIndex]); 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /tools/bumblebee/src/telegram/events/start.js: -------------------------------------------------------------------------------- 1 | module.exports = ({ client }) => ({ 2 | description: 'Start rs-bumblebee', 3 | method: ({ 4 | from: { 5 | first_name: firstName, 6 | last_name: lastName = '', 7 | }, 8 | chat: { id }, 9 | }) => { 10 | const message = `Hello *${firstName} ${lastName}*! Glad to see you.\nType /help for available options.`; 11 | 12 | client.sendMessage(id, message, { parse_mode: 'Markdown' }); 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /tools/bumblebee/src/telegram/index.js: -------------------------------------------------------------------------------- 1 | const { createClient, listen } = require('./telegram-bot'); 2 | 3 | module.exports = async () => { 4 | const client = await createClient(); 5 | 6 | listen(client); 7 | 8 | return client; 9 | }; 10 | -------------------------------------------------------------------------------- /tools/sloths/.env.example: -------------------------------------------------------------------------------- 1 | VITE_CDN_URL=http://localhost:5173/cdn 2 | -------------------------------------------------------------------------------- /tools/sloths/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 2, 4 | "semi": true, 5 | "singleQuote": true, 6 | "printWidth": 120 7 | } -------------------------------------------------------------------------------- /tools/sloths/README.md: -------------------------------------------------------------------------------- 1 | # rs-sloths 2 | 3 | RS SLOTHS is a depository of sloths. View all existing stickers, download the necessary ones or chill a bit - why not? Feel a sloth! 4 | 5 | ## Stack 6 | 7 | Vue3, Pinia, Vue Router, TypeScript, Vite 8 | 9 | ## Project Setup 10 | 11 | ```sh 12 | npm install 13 | ``` 14 | 15 | ### Compile and Hot-Reload for Development 16 | 17 | ```sh 18 | npm run dev 19 | ``` 20 | 21 | ### Type-Check, Compile and Minify for Production 22 | 23 | ```sh 24 | npm run build 25 | ``` 26 | 27 | ### Lint with [ESLint](https://eslint.org/) 28 | 29 | ```sh 30 | npm run lint 31 | ``` 32 | -------------------------------------------------------------------------------- /tools/sloths/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /tools/sloths/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | RS-SLOTHS 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /tools/sloths/public/cdn/cleaned/filelist.json: -------------------------------------------------------------------------------- 1 | ["codewars.svg", "congrats.svg", "deadline.svg"] 2 | -------------------------------------------------------------------------------- /tools/sloths/public/cdn/stickers/activist/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Activist", 3 | "description": "Primus inter pares", 4 | "tags": ["activist", "hero"] 5 | } 6 | -------------------------------------------------------------------------------- /tools/sloths/public/cdn/stickers/codewars/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Codewars", 3 | "description": "Как-то летним днём\nСамурай не сдал кросс-чек!\nГрустно. 0 баллов.", 4 | "tags": ["student", "samurai", "codewars"] 5 | } 6 | -------------------------------------------------------------------------------- /tools/sloths/public/cdn/stickers/congrats/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Congrats", 3 | "description": "Bring on the Champagne!", 4 | "tags": ["congratulations", "student"] 5 | } 6 | -------------------------------------------------------------------------------- /tools/sloths/public/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/public/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /tools/sloths/public/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/public/favicon/favicon.ico -------------------------------------------------------------------------------- /tools/sloths/public/img/merch/hoodie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/public/img/merch/hoodie.png -------------------------------------------------------------------------------- /tools/sloths/public/img/merch/mug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/public/img/merch/mug.png -------------------------------------------------------------------------------- /tools/sloths/public/img/merch/thermo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/public/img/merch/thermo.png -------------------------------------------------------------------------------- /tools/sloths/public/img/merch/tshirt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/public/img/merch/tshirt.png -------------------------------------------------------------------------------- /tools/sloths/public/img/photo/ogimly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/public/img/photo/ogimly.png -------------------------------------------------------------------------------- /tools/sloths/public/img/photo/vanet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/public/img/photo/vanet.png -------------------------------------------------------------------------------- /tools/sloths/public/img/photo/wiijoy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/public/img/photo/wiijoy.png -------------------------------------------------------------------------------- /tools/sloths/public/img/preview.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tools/sloths/src/assets/backgrounds/bg-footer-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /tools/sloths/src/assets/backgrounds/bg-footer-light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /tools/sloths/src/assets/fonts/Arial-Black.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/src/assets/fonts/Arial-Black.woff -------------------------------------------------------------------------------- /tools/sloths/src/assets/fonts/Arial-Black.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/src/assets/fonts/Arial-Black.woff2 -------------------------------------------------------------------------------- /tools/sloths/src/assets/fonts/NunitoSans-Black.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/src/assets/fonts/NunitoSans-Black.woff -------------------------------------------------------------------------------- /tools/sloths/src/assets/fonts/NunitoSans-Black.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/src/assets/fonts/NunitoSans-Black.woff2 -------------------------------------------------------------------------------- /tools/sloths/src/assets/fonts/NunitoSans-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/src/assets/fonts/NunitoSans-Bold.woff -------------------------------------------------------------------------------- /tools/sloths/src/assets/fonts/NunitoSans-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/src/assets/fonts/NunitoSans-Bold.woff2 -------------------------------------------------------------------------------- /tools/sloths/src/assets/fonts/NunitoSans-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/src/assets/fonts/NunitoSans-Light.woff -------------------------------------------------------------------------------- /tools/sloths/src/assets/fonts/NunitoSans-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/src/assets/fonts/NunitoSans-Light.woff2 -------------------------------------------------------------------------------- /tools/sloths/src/assets/fonts/NunitoSans-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/src/assets/fonts/NunitoSans-Regular.woff -------------------------------------------------------------------------------- /tools/sloths/src/assets/fonts/NunitoSans-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/src/assets/fonts/NunitoSans-Regular.woff2 -------------------------------------------------------------------------------- /tools/sloths/src/assets/icons/btn/center-square-black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /tools/sloths/src/assets/icons/btn/center-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /tools/sloths/src/assets/icons/btn/check-circle-fill.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /tools/sloths/src/assets/icons/btn/check-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /tools/sloths/src/assets/icons/btn/check-square-black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /tools/sloths/src/assets/icons/btn/check-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /tools/sloths/src/assets/icons/btn/dash-square-black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /tools/sloths/src/assets/icons/btn/dash-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /tools/sloths/src/assets/icons/btn/download-black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /tools/sloths/src/assets/icons/btn/download.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /tools/sloths/src/assets/icons/btn/plus-square-black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /tools/sloths/src/assets/icons/btn/plus-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /tools/sloths/src/assets/icons/rs-school.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/src/assets/icons/rs-school.png -------------------------------------------------------------------------------- /tools/sloths/src/assets/sounds/fail.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/src/assets/sounds/fail.mp3 -------------------------------------------------------------------------------- /tools/sloths/src/assets/sounds/flip.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/src/assets/sounds/flip.mp3 -------------------------------------------------------------------------------- /tools/sloths/src/assets/sounds/ovation.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/src/assets/sounds/ovation.mp3 -------------------------------------------------------------------------------- /tools/sloths/src/assets/sounds/sad-trombone.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/src/assets/sounds/sad-trombone.mp3 -------------------------------------------------------------------------------- /tools/sloths/src/assets/sounds/slide.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/src/assets/sounds/slide.mp3 -------------------------------------------------------------------------------- /tools/sloths/src/assets/sounds/success.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/src/assets/sounds/success.mp3 -------------------------------------------------------------------------------- /tools/sloths/src/assets/sounds/ta-da.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes/rsschool-app/51294715c03ccd8e6e38b05e33859d28e86bc08e/tools/sloths/src/assets/sounds/ta-da.mp3 -------------------------------------------------------------------------------- /tools/sloths/src/assets/styles/main.css: -------------------------------------------------------------------------------- 1 | @import './fonts.css'; 2 | @import './variables.css'; 3 | 4 | @import './base.css'; 5 | -------------------------------------------------------------------------------- /tools/sloths/src/i18n.ts: -------------------------------------------------------------------------------- 1 | import { createI18n } from 'vue-i18n'; 2 | import en from './assets/locales/en.json'; 3 | 4 | const i18n = createI18n({ 5 | legacy: false, 6 | locale: 'en', 7 | fallbackLocale: 'en', 8 | messages: { 9 | en, 10 | }, 11 | globalInjection: true, 12 | }); 13 | 14 | export default i18n; 15 | -------------------------------------------------------------------------------- /tools/sloths/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import { createPinia } from 'pinia'; 3 | 4 | import Shortkey from 'vue-three-shortkey'; 5 | import i18n from './i18n'; 6 | import App from './App.vue'; 7 | import router from './router'; 8 | 9 | import 'normalize.css/normalize.css'; 10 | import './assets/styles/main.css'; 11 | 12 | const app = createApp(App); 13 | 14 | app.use(createPinia()); 15 | app.use(router); 16 | app.use(i18n); 17 | app.use(Shortkey, { prevent: ['input', 'textarea'] }); 18 | 19 | app.mount('#app'); 20 | -------------------------------------------------------------------------------- /tools/sloths/src/services/error-handler.ts: -------------------------------------------------------------------------------- 1 | import useAlertModal from '@/stores/alert-modal'; 2 | 3 | const { showAlertModal } = useAlertModal(); 4 | 5 | export const errorHandler = (error: unknown) => { 6 | showAlertModal('modal.header.error', `${error}`); 7 | }; 8 | 9 | export default errorHandler; 10 | -------------------------------------------------------------------------------- /tools/sloths/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue'; 2 | declare module 'vue-three-shortkey'; 3 | -------------------------------------------------------------------------------- /tools/sloths/src/stores/alert-modal.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | 3 | const useAlertModal = defineStore({ 4 | id: 'alertModal', 5 | 6 | state: () => ({ 7 | isAlert: false, 8 | header: '', 9 | message: '', 10 | }), 11 | 12 | actions: { 13 | showAlertModal(header: string, message: string) { 14 | this.isAlert = true; 15 | this.header = header; 16 | this.message = message; 17 | }, 18 | }, 19 | }); 20 | 21 | export default useAlertModal; 22 | -------------------------------------------------------------------------------- /tools/sloths/src/stores/audio-on.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | 3 | const useAudioOn = defineStore({ 4 | id: 'audioOn', 5 | 6 | state: () => ({ 7 | isAudioOn: false, 8 | }), 9 | }); 10 | 11 | export default useAudioOn; 12 | -------------------------------------------------------------------------------- /tools/sloths/src/stores/cleaned.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | 3 | const useCleanedStore = defineStore({ 4 | id: 'cleaned', 5 | state: () => ({ 6 | cleanedFilelist: [] as string[], 7 | originalFilelist: [] as string[], 8 | }), 9 | }); 10 | 11 | export default useCleanedStore; 12 | -------------------------------------------------------------------------------- /tools/sloths/src/stores/counter.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | 3 | const useCounterStore = defineStore({ 4 | id: 'counter', 5 | state: () => ({ 6 | counter: 0, 7 | }), 8 | getters: { 9 | doubleCount: (state) => state.counter * 2, 10 | }, 11 | actions: { 12 | increment() { 13 | this.counter += 1; 14 | }, 15 | }, 16 | }); 17 | 18 | export default useCounterStore; 19 | -------------------------------------------------------------------------------- /tools/sloths/src/stores/loader.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | 3 | const useLoader = defineStore({ 4 | id: 'loader', 5 | state: () => ({ 6 | isLoad: false, 7 | }), 8 | }); 9 | 10 | export default useLoader; 11 | -------------------------------------------------------------------------------- /tools/sloths/src/stores/search-text.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | 3 | const useSearchText = defineStore({ 4 | id: 'searchText', 5 | 6 | state: () => ({ 7 | searchText: '', 8 | }), 9 | 10 | actions: { 11 | getSearchText(): string { 12 | return this.searchText; 13 | }, 14 | 15 | setSearchText(s: string) { 16 | this.searchText = s; 17 | }, 18 | }, 19 | }); 20 | 21 | export default useSearchText; 22 | -------------------------------------------------------------------------------- /tools/sloths/src/stores/sloths.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | import type { Sloth } from '../common/types'; 3 | 4 | const useSlothsStore = defineStore({ 5 | id: 'sloths', 6 | state: () => ({ 7 | sloths: [] as Sloth[], 8 | }), 9 | }); 10 | 11 | export default useSlothsStore; 12 | -------------------------------------------------------------------------------- /tools/sloths/src/stores/sorting-list.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | 3 | const useSortingList = defineStore({ 4 | id: 'sortingList', 5 | 6 | state: () => ({ 7 | sortingList: '', 8 | }), 9 | 10 | actions: { 11 | getSortingList(): string { 12 | return this.sortingList; 13 | }, 14 | 15 | setSortingList(s: string) { 16 | this.sortingList = s; 17 | }, 18 | }, 19 | }); 20 | 21 | export default useSortingList; 22 | -------------------------------------------------------------------------------- /tools/sloths/src/stores/tag-cloud.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | 3 | const useSelectedTags = defineStore({ 4 | id: 'tagCloud', 5 | 6 | state: () => ({ 7 | selected: [] as string[], 8 | }), 9 | 10 | actions: { 11 | getSelected(): string[] { 12 | return [...this.selected]; 13 | }, 14 | 15 | setSelected(s: string[]) { 16 | this.selected = [...s]; 17 | }, 18 | }, 19 | }); 20 | 21 | export default useSelectedTags; 22 | -------------------------------------------------------------------------------- /tools/sloths/src/stores/theme.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | 3 | const useThemeProp = defineStore({ 4 | id: 'theme', 5 | state: () => ({ 6 | currTheme: '', 7 | }), 8 | }); 9 | 10 | export default useThemeProp; 11 | -------------------------------------------------------------------------------- /tools/sloths/src/utils/game-utils.ts: -------------------------------------------------------------------------------- 1 | export default function isEven(value: number) { 2 | return !(value % 2); 3 | } 4 | -------------------------------------------------------------------------------- /tools/sloths/src/utils/userTheme.ts: -------------------------------------------------------------------------------- 1 | export default function getUserTheme(): string { 2 | if (window.matchMedia('(prefers-color-scheme: dark)').matches) { 3 | return 'dark'; 4 | } 5 | return 'light'; 6 | } 7 | -------------------------------------------------------------------------------- /tools/sloths/tsconfig.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./node_modules/@vue/tsconfig/tsconfig.node.json", 3 | "include": ["vite.config.*", "vitest.config.*", "cypress.config.*"], 4 | "compilerOptions": { 5 | "composite": true, 6 | "types": ["node"], 7 | } 8 | } 9 | --------------------------------------------------------------------------------