├── .dockerignore ├── .editorconfig ├── .env ├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── CI.yml ├── .gitignore ├── .husky └── pre-commit ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── .storybook ├── main.js └── tsconfig.json ├── .vscode └── extensions.json ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── apps ├── build-kata │ ├── .eslintrc.json │ ├── jest.config.ts │ ├── project.json │ ├── src │ │ ├── app │ │ │ ├── .gitkeep │ │ │ ├── FileReader.ts │ │ │ ├── KataFactory.ts │ │ │ └── katas │ │ │ │ ├── index.ts │ │ │ │ ├── js │ │ │ │ ├── 01.ts │ │ │ │ ├── 01.txt │ │ │ │ ├── 02.ts │ │ │ │ └── 02.txt │ │ │ │ └── ts │ │ │ │ ├── 01.ts │ │ │ │ ├── 01.txt │ │ │ │ ├── 02.ts │ │ │ │ └── 02.txt │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── environments │ │ │ ├── environment.prod.ts │ │ │ └── environment.ts │ │ └── main.ts │ ├── tsconfig.app.json │ ├── tsconfig.json │ └── tsconfig.spec.json ├── web-e2e │ ├── .eslintrc.json │ ├── cypress.json │ ├── project.json │ ├── src │ │ ├── fixtures │ │ │ └── example.json │ │ ├── integration │ │ │ └── app.spec.ts │ │ └── support │ │ │ ├── app.po.ts │ │ │ ├── commands.ts │ │ │ └── index.ts │ └── tsconfig.json └── web │ ├── .eslintrc.json │ ├── @types │ ├── next-auth │ │ └── index.d.ts │ └── react-table │ │ └── index.d.ts │ ├── components │ ├── AsyncInfo.tsx │ ├── Banner.tsx │ ├── ButtonLink.tsx │ ├── ErrorMessage.tsx │ ├── Input.tsx │ ├── Label.tsx │ ├── LabeledInput.tsx │ ├── LabeledSelect.tsx │ ├── LabeledTextarea.tsx │ ├── Layout.tsx │ ├── Link.tsx │ ├── NavLink.tsx │ ├── Navigation.tsx │ ├── Required.tsx │ ├── Select.tsx │ ├── Sidebar.tsx │ ├── SignInModal.tsx │ ├── Table.tsx │ ├── analyse │ │ ├── AnalysingStep.tsx │ │ ├── FetchFileFromGithubStep.tsx │ │ └── index.ts │ ├── codeHighLighter │ │ ├── CodeHighLighter.tsx │ │ ├── InteractiveCodeHighlighter.tsx │ │ └── index.ts │ └── index.ts │ ├── container.ts │ ├── global.css │ ├── hooks │ ├── index.ts │ ├── useAnalysisReportsTable.tsx │ ├── useAuth.ts │ ├── useAuthError.ts │ ├── useInfectedLineNumbers.ts │ ├── useIsFirstRender.ts │ ├── useLanguagesQueryString.ts │ ├── useLocalStorage.ts │ ├── useLoginRedirect.ts │ └── useModal.ts │ ├── index.d.ts │ ├── jest.config.ts │ ├── next-env.d.ts │ ├── next.config.js │ ├── pages │ ├── _app.tsx │ ├── analyse.tsx │ ├── api │ │ ├── auth │ │ │ └── [...nextauth].ts │ │ ├── getAllProgrammingLanguages.ts │ │ ├── getAnalysisDetails.ts │ │ ├── getFileContentFromGithub.ts │ │ ├── getMyAnalysisReports.ts │ │ ├── startKata.ts │ │ ├── submitAnalysis.ts │ │ ├── submitKata.ts │ │ └── voteOnAnalysis.ts │ ├── index.tsx │ ├── investigate.tsx │ └── learn.tsx │ ├── prisma.ts │ ├── project.json │ ├── public │ └── .gitkeep │ ├── rpc │ ├── ensureAuthenticated.ts │ ├── index.ts │ └── withSession.ts │ ├── schemas │ ├── ContentDtoSchema.ts │ ├── LineDtoSchema.ts │ └── ProgrammingLanguageSchema.ts │ ├── stores │ └── ModalStore.ts │ ├── tsconfig.json │ ├── tsconfig.spec.json │ ├── types.ts │ └── utils │ ├── CamelCaseUtil.ts │ ├── PluralizeUtil.ts │ ├── StatusColourPicker.tsx │ └── index.ts ├── babel.config.json ├── docker-compose.test.yml ├── docker-compose.yml ├── env.d.ts ├── jest.config.ts ├── jest.preset.js ├── libs ├── application │ ├── .babelrc │ ├── .eslintrc.json │ ├── README.md │ ├── jest.config.ts │ ├── project.json │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ ├── ApplicationModule.ts │ │ │ ├── dtos │ │ │ ├── AnalysisDetailsDto.ts │ │ │ ├── ContentDto.ts │ │ │ ├── InvestigateDto.ts │ │ │ ├── LineDto.ts │ │ │ ├── ProgrammingLanguageDto.ts │ │ │ ├── UserDto.ts │ │ │ └── index.ts │ │ │ ├── exceptions │ │ │ ├── UnknownProgrammingLanguageException.ts │ │ │ └── index.ts │ │ │ ├── interfaces │ │ │ ├── IGithubApi.ts │ │ │ ├── ILogger.ts │ │ │ └── index.ts │ │ │ └── useCases │ │ │ ├── getAnalysisDetailsUseCase │ │ │ ├── GetAnalysisDetailsResponse.ts │ │ │ ├── GetAnalysisDetailsUseCase.ts │ │ │ ├── IGetAnalysisDetailsRequest.ts │ │ │ ├── NoAvailableAnalysisForUserException.ts │ │ │ └── index.ts │ │ │ ├── getFileContentFromGithubUseCase │ │ │ ├── GetFileContentFromGithubResponse.ts │ │ │ ├── GetFileContentFromGithubUseCase.ts │ │ │ ├── IGetFileContentFromGithubRequest.ts │ │ │ └── index.ts │ │ │ ├── getMyAnalysisReportsUseCase │ │ │ ├── GetMyAnalysisReportsResponse.ts │ │ │ ├── GetMyAnalysisReportsUseCase.ts │ │ │ ├── IGetMyAnalysisReportsRequest.ts │ │ │ └── index.ts │ │ │ ├── getProgrammingLanguagesUseCase │ │ │ ├── GetProgrammingLanguagesResponse.ts │ │ │ ├── GetProgrammingLanguagesUseCase.ts │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── startKataUseCase │ │ │ ├── IStartKataRequest.ts │ │ │ ├── NoAvailableKatasException.ts │ │ │ ├── StartKataResponse.ts │ │ │ ├── StartKataUseCase.ts │ │ │ └── index.ts │ │ │ ├── submitAnalysisUseCase │ │ │ ├── ISubmitAnalysisRequest.ts │ │ │ ├── SubmitAnalysisUseCase.ts │ │ │ └── index.ts │ │ │ ├── submitKataUsecase │ │ │ ├── ISubmitKataRequest.ts │ │ │ ├── SubmitKataResponse.ts │ │ │ ├── SubmitKataUseCase.ts │ │ │ ├── UnknownKataException.ts │ │ │ └── index.ts │ │ │ └── voteOnAnalysisUseCase │ │ │ ├── AnalysisDoesNotExistException.ts │ │ │ ├── IVoteOnAnalysisRequest.ts │ │ │ ├── VoteOnAnalysisUseCase.ts │ │ │ └── index.ts │ ├── tests │ │ ├── useCases │ │ │ ├── getAnalysisDetailsUseCase │ │ │ │ └── GetAnalysisDetailsUseCase.spec.ts │ │ │ ├── getFileContentFromGithubUseCase │ │ │ │ └── GetFileContentFromGithubUseCase.spec.ts │ │ │ ├── getMyAnalysisReportsUseCase │ │ │ │ └── GetMyAnalysisReportsUseCase.spec.ts │ │ │ ├── getProgrammingLangaugesUseCase │ │ │ │ └── GetProgrammingLanguagesUseCase.spec.ts │ │ │ ├── startKataUseCase │ │ │ │ └── StartKataUseCase.spec.ts │ │ │ ├── submitAnalysisUseCase │ │ │ │ └── SubmitAnalysisUseCase.spec.ts │ │ │ ├── submitKataUseCase │ │ │ │ └── SubmitKataUseCase.spec.ts │ │ │ └── voteOnAnalysisUseCase │ │ │ │ └── VoteOnAnalysisUseCase.spec.ts │ │ └── utils │ │ │ ├── AnalysisDetailsFactory.ts │ │ │ ├── AnalysisFactory.ts │ │ │ ├── AnswerFactory.ts │ │ │ ├── ContentFactory.ts │ │ │ ├── GetMyAnalysisReportsRequestFactory.ts │ │ │ ├── KataFactory.ts │ │ │ ├── ProgrammingLanguageFactory.ts │ │ │ ├── SolutionFactory.ts │ │ │ ├── SubmitAnalysisRequestFactory.ts │ │ │ ├── UserFactory.ts │ │ │ ├── VoteFactory.ts │ │ │ ├── VoteOnAnalysisRequestFactory.ts │ │ │ └── index.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ └── tsconfig.spec.json ├── components │ ├── .babelrc │ ├── .eslintrc.json │ ├── .storybook │ │ ├── main.js │ │ ├── preview.js │ │ └── tsconfig.json │ ├── README.md │ ├── jest.config.ts │ ├── project.json │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ ├── Theme.ts │ │ │ ├── buttons │ │ │ ├── Button.stories.tsx │ │ │ ├── Button.tsx │ │ │ ├── ButtonConfig.ts │ │ │ ├── IconButton.stories.tsx │ │ │ ├── IconButton.tsx │ │ │ └── IconButtonConfig.ts │ │ │ ├── dropdown │ │ │ └── Dropdown.tsx │ │ │ └── header │ │ │ ├── Header.stories.tsx │ │ │ └── Header.tsx │ ├── tsconfig.json │ ├── tsconfig.lib.json │ └── tsconfig.spec.json ├── domain │ ├── .babelrc │ ├── .eslintrc.json │ ├── README.md │ ├── jest.config.ts │ ├── project.json │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ ├── Content.ts │ │ │ ├── IPaginate.ts │ │ │ ├── IRead.ts │ │ │ ├── IWrite.ts │ │ │ ├── Line.ts │ │ │ ├── ProgrammingLanguage.ts │ │ │ ├── Smell.ts │ │ │ ├── analysis │ │ │ ├── Analysis.ts │ │ │ ├── AnalysisDetails.ts │ │ │ ├── AnalysisStatus.ts │ │ │ ├── AnalysisType.ts │ │ │ ├── CannotVoteOnAnalysisException.ts │ │ │ ├── HasAlreadyVotedException.ts │ │ │ ├── IAnalysisRepository.ts │ │ │ ├── OwnersCannotVoteOnTheirOwnAnalysisException.ts │ │ │ ├── index.ts │ │ │ └── valueObjects │ │ │ │ ├── AnalysisAuthor.ts │ │ │ │ ├── AnalysisFileDir.ts │ │ │ │ ├── AnalysisId.ts │ │ │ │ ├── AnalysisReason.ts │ │ │ │ ├── AnalysisRepositoryName.ts │ │ │ │ ├── AnalysisSha.ts │ │ │ │ └── index.ts │ │ │ ├── answer │ │ │ ├── Answer.ts │ │ │ ├── index.ts │ │ │ └── valueObjects │ │ │ │ ├── AnswerId.ts │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── kata │ │ │ ├── IKataRepository.ts │ │ │ ├── Kata.ts │ │ │ ├── index.ts │ │ │ └── valueObjects │ │ │ │ ├── KataId.ts │ │ │ │ └── index.ts │ │ │ ├── solution │ │ │ ├── Solution.ts │ │ │ ├── index.ts │ │ │ └── valueObjects │ │ │ │ ├── SolutionId.ts │ │ │ │ └── index.ts │ │ │ ├── user │ │ │ ├── IUserRepository.ts │ │ │ ├── User.ts │ │ │ ├── enums │ │ │ │ ├── UserRole.ts │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ └── valueObjects │ │ │ │ ├── UserEmail.ts │ │ │ │ ├── UserId.ts │ │ │ │ ├── UserName.ts │ │ │ │ └── index.ts │ │ │ └── vote │ │ │ ├── Vote.ts │ │ │ └── index.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ └── tsconfig.spec.json ├── infrastructure │ ├── .babelrc │ ├── .eslintrc.json │ ├── README.md │ ├── jest.config.ts │ ├── jest.integration-config.ts │ ├── project.json │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ ├── ConfigService.ts │ │ │ ├── InfraException.ts │ │ │ ├── InfrastructureModule.ts │ │ │ ├── drivers │ │ │ └── prisma │ │ │ │ ├── PrismaService.ts │ │ │ │ ├── PrismaUrlFactory.ts │ │ │ │ ├── PrismaUrlParser.spec.ts │ │ │ │ ├── PrismaUrlParser.ts │ │ │ │ ├── index.ts │ │ │ │ ├── mappers │ │ │ │ ├── AnalysisDetailsMapper.ts │ │ │ │ ├── AnalysisMapper.ts │ │ │ │ ├── AnswerMapper.ts │ │ │ │ ├── ContentMapper.ts │ │ │ │ ├── KataMapper.ts │ │ │ │ ├── SolutionMapper.ts │ │ │ │ └── UserMapper.ts │ │ │ │ ├── models │ │ │ │ ├── AnalysisModel.ts │ │ │ │ ├── AnswerModel.ts │ │ │ │ ├── ContentModel.ts │ │ │ │ ├── KataModel.ts │ │ │ │ ├── SolutionModel.ts │ │ │ │ └── UserModel.ts │ │ │ │ └── repositories │ │ │ │ ├── PrismaAnalysisRepositoryImpl.ts │ │ │ │ └── PrismaKataRepositoryImpl.ts │ │ │ ├── githubApi │ │ │ └── GithubApi.ts │ │ │ ├── index.ts │ │ │ └── logger │ │ │ ├── DevelopmentLoggerImpl.ts │ │ │ └── LoggerLogTailImpl.ts │ ├── tests │ │ ├── drivers │ │ │ └── prisma │ │ │ │ └── repositories │ │ │ │ ├── PrismaAnalysisRepositoryImpl.integration-spec.ts │ │ │ │ ├── PrismaKataRepositoryImpl.integration-spec.ts │ │ │ │ └── PrismaKataRepositoryImpl.spec.ts │ │ └── utils │ │ │ ├── AnalysisBuilder.ts │ │ │ ├── KataBuilder.ts │ │ │ └── UserBuilder.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ └── tsconfig.spec.json ├── shared │ ├── .babelrc │ ├── .eslintrc.json │ ├── README.md │ ├── jest.config.ts │ ├── project.json │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ ├── EntityId.ts │ │ │ ├── Utils.ts │ │ │ ├── ValueObject.ts │ │ │ ├── exceptions │ │ │ ├── DomainException.ts │ │ │ ├── DomainExceptionMixin.ts │ │ │ ├── InvalidArgumentException.ts │ │ │ └── index.ts │ │ │ ├── github │ │ │ ├── GithubRepositoryUrlParser.ts │ │ │ └── index.ts │ │ │ ├── guard │ │ │ ├── AgainstClause.ts │ │ │ ├── Guard.ts │ │ │ ├── GuardProps.ts │ │ │ ├── IClause.ts │ │ │ ├── IsClause.ts │ │ │ └── index.ts │ │ │ ├── interfaces │ │ │ ├── IEntity.ts │ │ │ └── IUseCase.ts │ │ │ ├── inversify │ │ │ ├── TestingFactory.ts │ │ │ └── index.ts │ │ │ └── types.ts │ ├── tests │ │ ├── ValueObject.spec.ts │ │ ├── github │ │ │ └── GithubRepositoryUrlParser.spec.ts │ │ └── guard │ │ │ ├── AgainstClause.spec.ts │ │ │ └── IsClause.spec.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ └── tsconfig.spec.json └── testing-utils │ ├── .babelrc │ ├── .eslintrc.json │ ├── README.md │ ├── jest.config.ts │ ├── project.json │ ├── src │ └── index.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ └── tsconfig.spec.json ├── nx.json ├── package.json ├── prisma ├── migrations │ ├── 20220911130421_init │ │ └── migration.sql │ └── migration_lock.toml ├── schema.prisma └── seeds.ts ├── run-tests.sh ├── setupJest.ts ├── tools ├── generators │ └── .gitkeep └── tsconfig.tools.json ├── tsconfig.base.json ├── workspace.json └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile* 2 | .env 3 | dist 4 | .github 5 | .vscode 6 | coverage 7 | .env.local 8 | yarn-error.log 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | DATABASE_URL="mysql://root:root@localhost:3306/codebarker" 3 | 4 | # Auth 5 | GITHUB_ID= 6 | GITHUB_SECRET= 7 | 8 | DISCORD_ID= 9 | DISCORD_SECRET= 10 | 11 | GOOGLE_ID= 12 | GOOGLE_SECRET= 13 | 14 | NEXTAUTH_URL=http://localhost:4200/ 15 | 16 | # Github Integration 17 | GITHUB_ACCESS_TOKEN= 18 | 19 | # Logging 20 | SOURCE_TOKEN= 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: CI/CD 2 | 3 | on: push 4 | 5 | env: 6 | NODE_ENV: test 7 | MYSQL_DATABASE: 'codebarker' 8 | MYSQL_ROOT_PASSWORD: 'root' 9 | 10 | jobs: 11 | test: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: tests 17 | run: docker-compose -f docker-compose.test.yml up --exit-code-from=test 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | yarn-error.log 34 | testem.log 35 | /typings 36 | 37 | # System Files 38 | .DS_Store 39 | Thumbs.db 40 | 41 | .todo 42 | .env.local 43 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | 5 | nx affected:lint --fix 6 | nx run-many --target=test --all 7 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v16.15.0 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Add files here to ignore them from prettier formatting 2 | 3 | /dist 4 | /coverage 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stories: [], 3 | addons: ['@storybook/addon-essentials', '@chakra-ui/storybook-addon'], 4 | features: { 5 | emotionAlias: false, 6 | }, 7 | // uncomment the property below if you want to apply some webpack config globally 8 | // webpackFinal: async (config, { configType }) => { 9 | // // Make whatever fine-grained changes you need that should apply to all storybook configs 10 | 11 | // // Return the altered config 12 | // return config; 13 | // }, 14 | }; 15 | -------------------------------------------------------------------------------- /.storybook/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.base.json", 3 | "exclude": [ 4 | "../**/*.spec.js", 5 | "../**/*.test.js", 6 | "../**/*.spec.ts", 7 | "../**/*.test.ts", 8 | "../**/*.spec.tsx", 9 | "../**/*.test.tsx", 10 | "../**/*.spec.jsx", 11 | "../**/*.test.jsx" 12 | ], 13 | "include": ["../**/*"] 14 | } 15 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "nrwl.angular-console", 4 | "esbenp.prettier-vscode", 5 | "firsttris.vscode-jest-runner", 6 | "dbaeumer.vscode-eslint" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16 as base 2 | 3 | WORKDIR /app 4 | COPY ./package.json . 5 | RUN yarn 6 | COPY . . 7 | RUN yarn prisma generate 8 | 9 | FROM base as test 10 | ENTRYPOINT ["/app/run-tests.sh"] 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Donny Roufs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | branch ?= staging 2 | 3 | pscale-up: 4 | pscale connect codebarker $(branch) --port 3309 5 | 6 | db-push: 7 | yarn prisma db push 8 | 9 | db-reset: 10 | yarn prisma migrate reset --force --skip-generate 11 | 12 | db-new: 13 | $(MAKE) db-reset 14 | $(MAKE) db-push 15 | yarn db:seed 16 | 17 | cloc: 18 | cloc --exclude-dir=node_modules,dist --exclude-lang=JavaScript,JSON,HTML . -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Codebarker 2 | 3 | **prod env** 4 | https://codebarker.net 5 | 6 | **staging env** 7 | https://staging.codebarker.net 8 | 9 | **design** 10 | https://www.figma.com/file/ie7Jw7cGfREdzJUtvsmCLk/Untitled?node-id=147%3A2 11 | 12 | --- 13 | 14 | An application that allows the user to learn about code smells through community submissions. 15 | 16 | - As a user I can view a smell and vote on what smell it is 17 | - As a user I can submit source code from an open source github application as a smell 18 | - As a user I can vote whether I agree or disagree on a potential code smell submission 19 | - As a user I can see an overview of my submissions 20 | 21 | ## Setup 22 | 23 | ### Before running 24 | 25 | - Have the correct node version (check .nvmrc) 26 | - Make sure you have docker and docker-compose installed so that you can run the internal services (e.g. docker-compose up -d) 27 | - Create a .env.local file and fill in the required secrets that can be found in the .env file 28 | 29 | ### How to run 30 | 31 | - Run docker-compose up -d 32 | - Run yarn prisma migrate dev 33 | - Run yarn db:seed 34 | - Run the app with: "yarn nx run web:serve" 35 | - Recommended to install nx extension in vscode or webstorm to easily start the app 36 | -------------------------------------------------------------------------------- /apps/build-kata/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /apps/build-kata/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'build-kata', 4 | preset: '../../jest.preset.js', 5 | globals: { 6 | 'ts-jest': { 7 | tsconfig: '/tsconfig.spec.json', 8 | }, 9 | }, 10 | testEnvironment: 'node', 11 | transform: { 12 | '^.+\\.[tj]s$': 'ts-jest', 13 | }, 14 | moduleFileExtensions: ['ts', 'js', 'html'], 15 | coverageDirectory: '../../coverage/apps/build-kata', 16 | }; 17 | -------------------------------------------------------------------------------- /apps/build-kata/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 3 | "sourceRoot": "apps/build-kata/src", 4 | "projectType": "application", 5 | "targets": { 6 | "build": { 7 | "executor": "@nrwl/node:webpack", 8 | "outputs": ["{options.outputPath}"], 9 | "options": { 10 | "outputPath": "dist/apps/build-kata", 11 | "main": "apps/build-kata/src/main.ts", 12 | "tsConfig": "apps/build-kata/tsconfig.app.json", 13 | "assets": ["apps/build-kata/src/assets"] 14 | }, 15 | "configurations": { 16 | "production": { 17 | "optimization": true, 18 | "extractLicenses": true, 19 | "inspect": false, 20 | "fileReplacements": [ 21 | { 22 | "replace": "apps/build-kata/src/environments/environment.ts", 23 | "with": "apps/build-kata/src/environments/environment.prod.ts" 24 | } 25 | ] 26 | } 27 | } 28 | }, 29 | "serve": { 30 | "executor": "@nrwl/node:node", 31 | "options": { 32 | "buildTarget": "build-kata:build" 33 | } 34 | }, 35 | "lint": { 36 | "executor": "@nrwl/linter:eslint", 37 | "outputs": ["{options.outputFile}"], 38 | "options": { 39 | "lintFilePatterns": ["apps/build-kata/**/*.ts"] 40 | } 41 | }, 42 | "test": { 43 | "executor": "@nrwl/jest:jest", 44 | "outputs": ["coverage/apps/build-kata"], 45 | "options": { 46 | "jestConfig": "apps/build-kata/jest.config.ts", 47 | "passWithNoTests": true 48 | } 49 | } 50 | }, 51 | "tags": [] 52 | } 53 | -------------------------------------------------------------------------------- /apps/build-kata/src/app/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donnyroufs/codebarker/9b717965ef84d404b15f744c9aea0fdbe97b7542/apps/build-kata/src/app/.gitkeep -------------------------------------------------------------------------------- /apps/build-kata/src/app/FileReader.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'fs'; 2 | 3 | export class FileReader { 4 | public static readSync(path: string): Buffer { 5 | return readFileSync(path); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /apps/build-kata/src/app/KataFactory.ts: -------------------------------------------------------------------------------- 1 | import { Prisma } from '@prisma/client'; 2 | import * as crypto from 'crypto'; 3 | 4 | import { 5 | Content, 6 | Line, 7 | Smell, 8 | ProgrammingLanguage as P, 9 | } from '@codebarker/domain'; 10 | 11 | export class KataFactory { 12 | public static make({ 13 | code, 14 | highlightedLines, 15 | extension, 16 | name, 17 | solutionType, 18 | }: { 19 | code: string; 20 | highlightedLines: number[]; 21 | extension: string; 22 | name: string; 23 | solutionType: Smell; 24 | }): Prisma.KataCreateInput { 25 | return { 26 | id: crypto.randomUUID(), 27 | content: { 28 | create: { 29 | id: crypto.randomUUID(), 30 | lines: this.makeCode(code, extension, name, highlightedLines), 31 | programmingLanguage: { 32 | connect: { 33 | extension_name: { 34 | extension, 35 | name, 36 | }, 37 | }, 38 | }, 39 | }, 40 | }, 41 | solution: { 42 | create: { 43 | id: crypto.randomUUID(), 44 | type: solutionType, 45 | }, 46 | }, 47 | }; 48 | } 49 | 50 | private static makeCode( 51 | code: string, 52 | extension: string, 53 | name: string, 54 | highlightedLines: number[] 55 | ): any { 56 | return Content.make({ 57 | lines: code.split('\n').map((line, i) => 58 | Line.make({ 59 | lineNumber: i, 60 | value: line, 61 | isInfected: highlightedLines.includes(i - 1), 62 | }) 63 | ), 64 | programmingLanguage: P.make({ 65 | extension, 66 | name, 67 | }), 68 | }).lines.map((line) => ({ 69 | line: line.lineNumber, 70 | content: line.value, 71 | isInfected: line.isInfected, 72 | })); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /apps/build-kata/src/app/katas/index.ts: -------------------------------------------------------------------------------- 1 | export * from './js/01'; 2 | export * from './js/02'; 3 | 4 | export * from './ts/01'; 5 | export * from './ts/02'; 6 | -------------------------------------------------------------------------------- /apps/build-kata/src/app/katas/js/01.ts: -------------------------------------------------------------------------------- 1 | import { Smell } from '@codebarker/domain'; 2 | 3 | import { KataFactory } from '../../KataFactory'; 4 | import { FileReader } from '../../FileReader'; 5 | import { cwd } from 'process'; 6 | 7 | export const LongParameterTypeJavascript01 = KataFactory.make({ 8 | name: 'javascript', 9 | extension: 'js', 10 | highlightedLines: [2, 6, 7, 8, 9, 10, 11], 11 | solutionType: Smell.LongParameterList, 12 | code: FileReader.readSync( 13 | cwd() + '/apps/build-kata/src/app/katas/js/01.txt' 14 | ).toString(), 15 | }); 16 | -------------------------------------------------------------------------------- /apps/build-kata/src/app/katas/js/01.txt: -------------------------------------------------------------------------------- 1 | import db from 'db' 2 | 3 | async function createUser(id, firstName, lastName, email, age, country) { 4 | try { 5 | await db.users.create({ 6 | data: { 7 | id, 8 | firstName, 9 | lastName, 10 | email, 11 | age, 12 | country 13 | } 14 | }) 15 | return true 16 | } catch(err) { 17 | return false 18 | } 19 | } -------------------------------------------------------------------------------- /apps/build-kata/src/app/katas/js/02.ts: -------------------------------------------------------------------------------- 1 | import { Smell } from '@codebarker/domain'; 2 | 3 | import { KataFactory } from '../../KataFactory'; 4 | import { FileReader } from '../../FileReader'; 5 | import { cwd } from 'process'; 6 | import { range } from 'lodash'; 7 | 8 | export const LongMethodJavascript02 = KataFactory.make({ 9 | name: 'javascript', 10 | extension: 'js', 11 | highlightedLines: range(92, 162), 12 | solutionType: Smell.LongMethod, 13 | code: FileReader.readSync( 14 | cwd() + '/apps/build-kata/src/app/katas/js/02.txt' 15 | ).toString(), 16 | }); 17 | -------------------------------------------------------------------------------- /apps/build-kata/src/app/katas/ts/01.ts: -------------------------------------------------------------------------------- 1 | import { Smell } from '@codebarker/domain'; 2 | 3 | import { KataFactory } from '../../KataFactory'; 4 | import { FileReader } from '../../FileReader'; 5 | import { cwd } from 'process'; 6 | 7 | export const CommentsTypescript01 = KataFactory.make({ 8 | name: 'typescript', 9 | extension: 'ts', 10 | highlightedLines: [12, 16, 38], 11 | solutionType: Smell.Comments, 12 | code: FileReader.readSync( 13 | cwd() + '/apps/build-kata/src/app/katas/ts/01.txt' 14 | ).toString(), 15 | }); 16 | -------------------------------------------------------------------------------- /apps/build-kata/src/app/katas/ts/02.ts: -------------------------------------------------------------------------------- 1 | import { Smell } from '@codebarker/domain'; 2 | 3 | import { KataFactory } from '../../KataFactory'; 4 | import { FileReader } from '../../FileReader'; 5 | import { cwd } from 'process'; 6 | 7 | export const LargeClassTypescript02 = KataFactory.make({ 8 | name: 'typescript', 9 | extension: 'ts', 10 | highlightedLines: [28, 147], 11 | solutionType: Smell.LargeClass, 12 | code: FileReader.readSync( 13 | cwd() + '/apps/build-kata/src/app/katas/ts/02.txt' 14 | ).toString(), 15 | }); 16 | -------------------------------------------------------------------------------- /apps/build-kata/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donnyroufs/codebarker/9b717965ef84d404b15f744c9aea0fdbe97b7542/apps/build-kata/src/assets/.gitkeep -------------------------------------------------------------------------------- /apps/build-kata/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | }; 4 | -------------------------------------------------------------------------------- /apps/build-kata/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: false, 3 | }; 4 | -------------------------------------------------------------------------------- /apps/build-kata/src/main.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from '@prisma/client'; 2 | import * as Assets from './app/katas'; 3 | 4 | async function main(run: boolean): Promise { 5 | if (!run) return; 6 | 7 | const prisma = new PrismaClient(); 8 | const queries: any[] = Object.values(Assets).map((item) => 9 | prisma.kata.create({ 10 | data: item, 11 | }) 12 | ); 13 | 14 | await prisma.$transaction(queries).catch(console.error); 15 | } 16 | 17 | main(true); 18 | -------------------------------------------------------------------------------- /apps/build-kata/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["node"] 7 | }, 8 | "exclude": ["jest.config.ts", "**/*.spec.ts", "**/*.test.ts"], 9 | "include": ["**/*.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /apps/build-kata/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.app.json" 8 | }, 9 | { 10 | "path": "./tsconfig.spec.json" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /apps/build-kata/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /apps/web-e2e/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["plugin:cypress/recommended", "../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["src/plugins/index.js"], 11 | "rules": { 12 | "@typescript-eslint/no-var-requires": "off", 13 | "no-undef": "off" 14 | } 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /apps/web-e2e/cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "fileServerFolder": ".", 3 | "fixturesFolder": "./src/fixtures", 4 | "integrationFolder": "./src/integration", 5 | "modifyObstructiveCode": false, 6 | "supportFile": "./src/support/index.ts", 7 | "pluginsFile": false, 8 | "video": true, 9 | "videosFolder": "../../dist/cypress/apps/web-e2e/videos", 10 | "screenshotsFolder": "../../dist/cypress/apps/web-e2e/screenshots", 11 | "chromeWebSecurity": false 12 | } 13 | -------------------------------------------------------------------------------- /apps/web-e2e/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 3 | "sourceRoot": "apps/web-e2e/src", 4 | "projectType": "application", 5 | "targets": { 6 | "e2e": { 7 | "executor": "@nrwl/cypress:cypress", 8 | "options": { 9 | "cypressConfig": "apps/web-e2e/cypress.json", 10 | "devServerTarget": "web:serve:development" 11 | }, 12 | "configurations": { 13 | "production": { 14 | "devServerTarget": "web:serve:production" 15 | } 16 | } 17 | }, 18 | "lint": { 19 | "executor": "@nrwl/linter:eslint", 20 | "outputs": ["{options.outputFile}"], 21 | "options": { 22 | "lintFilePatterns": ["apps/web-e2e/**/*.{js,ts}"] 23 | } 24 | } 25 | }, 26 | "tags": [], 27 | "implicitDependencies": ["web"] 28 | } 29 | -------------------------------------------------------------------------------- /apps/web-e2e/src/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io" 4 | } 5 | -------------------------------------------------------------------------------- /apps/web-e2e/src/integration/app.spec.ts: -------------------------------------------------------------------------------- 1 | import { getGreeting } from '../support/app.po'; 2 | 3 | describe('web', () => { 4 | beforeEach(() => cy.visit('/')); 5 | 6 | it('should display welcome message', () => { 7 | // Custom command example, see `../support/commands.ts` file 8 | cy.login('my-email@something.com', 'myPassword'); 9 | 10 | // Function helper example, see `../support/app.po.ts` file 11 | getGreeting().contains('Welcome web'); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /apps/web-e2e/src/support/app.po.ts: -------------------------------------------------------------------------------- 1 | export const getGreeting = (): Cypress.Chainable => cy.get('h1'); 2 | -------------------------------------------------------------------------------- /apps/web-e2e/src/support/commands.ts: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | 11 | // eslint-disable-next-line @typescript-eslint/no-namespace 12 | declare namespace Cypress { 13 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 14 | // eslint-disable-next-line @typescript-eslint/naming-convention 15 | interface Chainable { 16 | login(email: string, password: string): void; 17 | } 18 | } 19 | // 20 | // -- This is a parent command -- 21 | Cypress.Commands.add('login', (email, password) => { 22 | console.log('Custom command example: Login', email, password); 23 | }); 24 | // 25 | // -- This is a child command -- 26 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) 27 | // 28 | // 29 | // -- This is a dual command -- 30 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) 31 | // 32 | // 33 | // -- This will overwrite an existing command -- 34 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) 35 | -------------------------------------------------------------------------------- /apps/web-e2e/src/support/index.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands'; 18 | -------------------------------------------------------------------------------- /apps/web-e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "sourceMap": false, 5 | "outDir": "../../dist/out-tsc", 6 | "allowJs": true, 7 | "types": ["cypress", "node"] 8 | }, 9 | "include": ["src/**/*.ts", "src/**/*.js"] 10 | } 11 | -------------------------------------------------------------------------------- /apps/web/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "plugin:@nrwl/nx/react-typescript", 4 | "../../.eslintrc.json", 5 | "next", 6 | "next/core-web-vitals" 7 | ], 8 | "ignorePatterns": [ 9 | "!**/*" 10 | ], 11 | "overrides": [ 12 | { 13 | "files": [ 14 | "*.ts", 15 | "*.tsx", 16 | "*.js", 17 | "*.jsx" 18 | ], 19 | "rules": { 20 | "@next/next/no-html-link-for-pages": [ 21 | "error", 22 | "apps/web/pages" 23 | ], 24 | "react/no-unescaped-entities": "off" 25 | } 26 | }, 27 | { 28 | "files": [ 29 | "*.ts", 30 | "*.tsx" 31 | ], 32 | "rules": {} 33 | }, 34 | { 35 | "files": [ 36 | "*.js", 37 | "*.jsx" 38 | ], 39 | "rules": {} 40 | } 41 | ], 42 | "env": { 43 | "jest": true 44 | } 45 | } -------------------------------------------------------------------------------- /apps/web/@types/next-auth/index.d.ts: -------------------------------------------------------------------------------- 1 | import 'next-auth'; 2 | 3 | declare module 'next-auth' { 4 | // eslint-disable-next-line @typescript-eslint/naming-convention 5 | interface User { 6 | id: string; 7 | role: number; 8 | email: string; 9 | image: ?string; 10 | } 11 | 12 | // eslint-disable-next-line @typescript-eslint/naming-convention 13 | interface Session { 14 | user?: User; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /apps/web/components/AsyncInfo.tsx: -------------------------------------------------------------------------------- 1 | import { Heading, VStack, Text, Skeleton } from '@chakra-ui/react'; 2 | 3 | type Props = { 4 | title?: string; 5 | body?: string; 6 | isLoaded: boolean; 7 | }; 8 | 9 | export const AsyncInfo = ({ title, body, isLoaded }: Props): JSX.Element => ( 10 | 11 | 18 | 19 | {title} 20 | 21 | 22 | 23 | 31 | {body} 32 | 33 | 34 | ); 35 | -------------------------------------------------------------------------------- /apps/web/components/ButtonLink.tsx: -------------------------------------------------------------------------------- 1 | import { ChakraProps } from '@chakra-ui/react'; 2 | import { Button, ButtonVariant } from '@codebarker/components'; 3 | import NextLink from 'next/link'; 4 | 5 | type Props = React.PropsWithChildren<{ 6 | href: string; 7 | variant?: ButtonVariant; 8 | _style?: ChakraProps; 9 | }>; 10 | 11 | export const ButtonLink = ({ 12 | href, 13 | children, 14 | variant, 15 | _style, 16 | }: Props): JSX.Element => ( 17 | 18 | 19 | 27 | 28 | 29 | ); 30 | -------------------------------------------------------------------------------- /apps/web/components/ErrorMessage.tsx: -------------------------------------------------------------------------------- 1 | import { Box } from '@chakra-ui/react'; 2 | 3 | type Props = { 4 | message: string; 5 | }; 6 | 7 | export const ErrorMessage = ({ message }: Props): JSX.Element => ( 8 | {message} 9 | ); 10 | -------------------------------------------------------------------------------- /apps/web/components/Input.tsx: -------------------------------------------------------------------------------- 1 | import { ChakraProps, Input as ChakraInput, VStack } from '@chakra-ui/react'; 2 | import { HTMLInputTypeAttribute } from 'react'; 3 | import { ErrorMessage } from './ErrorMessage'; 4 | 5 | type Props = { 6 | name: string; 7 | value: string; 8 | type: HTMLInputTypeAttribute; 9 | placeholder?: string; 10 | onChange: any; 11 | _styles?: ChakraProps; 12 | errorMessage?: string; 13 | }; 14 | 15 | export const Input = ({ 16 | name, 17 | type, 18 | placeholder, 19 | value, 20 | onChange, 21 | _styles, 22 | errorMessage, 23 | }: Props): JSX.Element => { 24 | return ( 25 | 26 | 42 | {errorMessage != null && } 43 | 44 | ); 45 | }; 46 | -------------------------------------------------------------------------------- /apps/web/components/Label.tsx: -------------------------------------------------------------------------------- 1 | import { Box, BoxProps } from '@chakra-ui/react'; 2 | 3 | type Props = React.PropsWithChildren<{ 4 | htmlFor: string; 5 | _styles?: BoxProps; 6 | }>; 7 | 8 | export const Label = ({ htmlFor, children, _styles }: Props): JSX.Element => { 9 | return ( 10 | 11 | {children} 12 | 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /apps/web/components/LabeledInput.tsx: -------------------------------------------------------------------------------- 1 | import { ChakraProps } from '@chakra-ui/react'; 2 | import { HTMLInputTypeAttribute } from 'react'; 3 | 4 | import { Label } from './Label'; 5 | import { Input } from './Input'; 6 | 7 | type Props = { 8 | name: string; 9 | value: string; 10 | type: HTMLInputTypeAttribute; 11 | placeholder?: string; 12 | onChange: any; 13 | _labelStyles?: ChakraProps; 14 | _styles?: ChakraProps; 15 | labelName: string; 16 | }; 17 | 18 | export const LabeledInput = ({ 19 | name, 20 | _labelStyles, 21 | labelName, 22 | ...rest 23 | }: Props): JSX.Element => { 24 | return ( 25 | 35 | ); 36 | }; 37 | -------------------------------------------------------------------------------- /apps/web/components/LabeledSelect.tsx: -------------------------------------------------------------------------------- 1 | import { Label } from './Label'; 2 | import { Required } from './Required'; 3 | import { Select, Props as SelectProps } from './Select'; 4 | 5 | type Props = SelectProps & { 6 | labelName: string | JSX.Element; 7 | isRequired?: boolean; 8 | }; 9 | 10 | export const LabeledSelect = ({ 11 | opts, 12 | value, 13 | name, 14 | onChange, 15 | labelName, 16 | isInvalid = false, 17 | isRequired = false, 18 | isMulti = false, 19 | placeholder = 'Select...', 20 | isLoading = false, 21 | }: Props): JSX.Element => { 22 | return ( 23 |