├── .github
└── workflows
│ ├── chromatic.yaml
│ ├── dev_greenonsoftware-dev-api.yml
│ ├── e2e.yaml
│ ├── prod_greenonsoftware-api.yml
│ └── units.yaml
├── .vscode
├── cb.code-snippets
├── cmp.code-snippets
├── csb.code-snippets
├── ex.code-snippets
├── hk.code-snippets
├── pg.code-snippets
└── vfn.code-snippets
├── COMMANDS.md
├── CONTRIBUTION.md
├── FAQ.md
├── README.md
├── dotnet
└── GreenOnSoftware
│ ├── .editorconfig
│ ├── .gitattributes
│ ├── .gitignore
│ ├── GreenOnSoftware.Api
│ ├── Context
│ │ ├── Context.cs
│ │ ├── ContextAccessor.cs
│ │ ├── ContextConfig.cs
│ │ └── IdentityContext.cs
│ ├── Controllers
│ │ ├── AccountController.cs
│ │ ├── ArticlesController.cs
│ │ ├── ReviewsController.cs
│ │ ├── SnippetsController.cs
│ │ └── UsersController.cs
│ ├── Filters
│ │ └── LogAttribute.cs
│ ├── GreenOnSoftware.Api.csproj
│ ├── Middlewares
│ │ ├── ExceptionHandlingMiddleware.cs
│ │ └── LogUsernameMiddleware.cs
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── Startup
│ │ ├── DbContextConfig.cs
│ │ ├── IdentityConfig.cs
│ │ └── InvalidModelStateResponseFactory.cs
│ ├── appsettings.Development.json
│ ├── appsettings.dev.json
│ ├── appsettings.json
│ └── appsettings.prod.json
│ ├── GreenOnSoftware.Application
│ ├── Account
│ │ ├── ChangePasswordCommand
│ │ │ ├── ChangePassword.cs
│ │ │ ├── ChangePasswordHandler.cs
│ │ │ └── ChangePasswordValidator.cs
│ │ ├── ForgottenPasswordCommand
│ │ │ ├── ForgottenPassword.cs
│ │ │ ├── ForgottenPasswordHandler.cs
│ │ │ └── ForgottenPasswordValidator.cs
│ │ ├── GetAvatarNameQuery
│ │ │ ├── GetAvatarName.cs
│ │ │ └── GetAvatarNameHandler.cs
│ │ ├── RegisterCommand
│ │ │ ├── RegistarHandler.cs
│ │ │ ├── Register.cs
│ │ │ └── RegisterValidator.cs
│ │ ├── ResetPasswordCommand
│ │ │ ├── ResetPassword.cs
│ │ │ ├── ResetPasswordHandler.cs
│ │ │ └── ResetPasswordValidator.cs
│ │ ├── SetAvatarNameCommand
│ │ │ ├── SetAvatarName.cs
│ │ │ ├── SetAvatarNameHandler.cs
│ │ │ └── SetAvatarNameValidator.cs
│ │ ├── SignInCommand
│ │ │ ├── SignIn.cs
│ │ │ └── SignInHandler.cs
│ │ └── SignOutCommand
│ │ │ ├── SignOut.cs
│ │ │ └── SignOutHandler.cs
│ ├── Articles
│ │ ├── AcceptArticleCommand
│ │ │ ├── AcceptArticle.cs
│ │ │ └── AcceptArticleHandler.cs
│ │ ├── AddArticleCommand
│ │ │ ├── AddArticle.cs
│ │ │ ├── AddArticleHandler.cs
│ │ │ └── AddArticleValidator.cs
│ │ ├── DeleteArticleCommand
│ │ │ ├── DeleteArticle.cs
│ │ │ └── DeleteArticleHandler.cs
│ │ ├── GetArticleByIdQuery
│ │ │ ├── ArticleDto.cs
│ │ │ ├── CommentDto.cs
│ │ │ ├── GetArticleById.cs
│ │ │ └── GetArticleByIdHandler.cs
│ │ ├── GetArticlesQuery
│ │ │ ├── ArticleLookupDto.cs
│ │ │ ├── GetArticles.cs
│ │ │ ├── GetArticlesHandler.cs
│ │ │ └── GetArticlesValidator.cs
│ │ ├── RejectArticleCommand
│ │ │ ├── RejectArticle.cs
│ │ │ └── RejectArticleHandler.cs
│ │ ├── SendForApprovalCommand
│ │ │ ├── SendForApproval.cs
│ │ │ └── SendForApprovalHandler.cs
│ │ └── UpdateArticleCommand
│ │ │ ├── UpdateArticle.cs
│ │ │ ├── UpdateArticleHandler.cs
│ │ │ └── UpdateArticleValidator.cs
│ ├── DependencyInjection.cs
│ ├── GreenOnSoftware.Application.csproj
│ ├── Ratings
│ │ ├── AddArticleRateCommand
│ │ │ ├── AddArticleRate.cs
│ │ │ ├── AddArticleRateHandler.cs
│ │ │ └── AddArticleRateValidator.cs
│ │ ├── GetArticleRatingQuery
│ │ │ ├── GetArticleRatings.cs
│ │ │ ├── GetArticleRatingsHandler.cs
│ │ │ └── RatingsDto.cs
│ │ ├── RemoveArticleRateCommand
│ │ │ ├── RemoveArticleRate.cs
│ │ │ └── RemoveArticleRateHandler.cs
│ │ └── UpdateArticleRate
│ │ │ ├── UpdateArticleRate.cs
│ │ │ ├── UpdateArticleRateHandler.cs
│ │ │ └── UpdateArticleRateValidator.cs
│ ├── Reviews
│ │ ├── AddReviewCommand
│ │ │ ├── AddReview.cs
│ │ │ ├── AddReviewHandler.cs
│ │ │ └── AddReviewValidator.cs
│ │ ├── DeleteReviewCommand
│ │ │ ├── DeleteReview.cs
│ │ │ └── DeleteReviewHandler.cs
│ │ ├── GetAllReviewerReviewsQuery
│ │ │ ├── GetAllReviewerReviews.cs
│ │ │ └── GetAllReviewerReviewsHandler.cs
│ │ ├── GetReviewByIdQuery
│ │ │ ├── GetReviews.cs
│ │ │ └── GetReviewsHandler.cs
│ │ ├── GetReviewsQuery
│ │ │ ├── GetReviews.cs
│ │ │ └── GetReviewsHandler.cs
│ │ └── UpdateReviewCommand
│ │ │ ├── UpdateReview.cs
│ │ │ ├── UpdateReviewHandler.cs
│ │ │ └── UpdateReviewValidator.cs
│ ├── Snippets
│ │ ├── AddSnippetCommand
│ │ │ ├── AddSnippet.cs
│ │ │ ├── AddSnippetHandler.cs
│ │ │ └── AddSnippetValidator.cs
│ │ └── GetSnippetByIdQuery
│ │ │ ├── GetSnippetById.cs
│ │ │ ├── GetSnippetByIdHandler.cs
│ │ │ ├── GetSnippetByIdValidator.cs
│ │ │ └── SnippetDto.cs
│ ├── Users
│ │ ├── AddToRoleCommand
│ │ │ ├── AddToRole.cs
│ │ │ ├── AddToRoleHandler.cs
│ │ │ └── AddToRoleValidator.cs
│ │ ├── GetUsersQuery
│ │ │ ├── GetUsers.cs
│ │ │ ├── GetUsersHandler.cs
│ │ │ ├── GetUsersValidator.cs
│ │ │ └── UserLookupDto.cs
│ │ └── RemoveFromRoleCommand
│ │ │ ├── RemoveFromRole.cs
│ │ │ ├── RemoveFromRoleHandler.cs
│ │ │ └── RemoveFromRoleValidator.cs
│ ├── _Configuration
│ │ └── ApplicationConfiguration.cs
│ ├── _Dtos
│ │ ├── ICurrentUserReviewer.cs
│ │ ├── ReviewDto.cs
│ │ ├── ReviewLookupDto.cs
│ │ └── UserReviewLookupDto.cs
│ ├── _Mapper
│ │ ├── ArticlesMapperProfile.cs
│ │ ├── MappingActions
│ │ │ └── ReviewDtoAction.cs
│ │ ├── SnippetsMapperProfile.cs
│ │ └── UsersMapperProfile.cs
│ └── _Services
│ │ ├── EmailService.cs
│ │ ├── Interfaces
│ │ ├── IBlobStorageService.cs
│ │ ├── IEmailSenderService.cs
│ │ ├── IEmailService.cs
│ │ ├── IRatingsSessionService.cs
│ │ └── IThumbnailService.cs
│ │ ├── RatingsSessionService.cs
│ │ └── ThumbnailService.cs
│ ├── GreenOnSoftware.Commons
│ ├── CQRS
│ │ └── ISearchQuery.cs
│ ├── Clock
│ │ ├── Clock.cs
│ │ └── IClock.cs
│ ├── Consts
│ │ └── Role.cs
│ ├── Context
│ │ ├── IContext.cs
│ │ ├── IContextAccessor.cs
│ │ └── IIdentityContext.cs
│ ├── Dtos
│ │ ├── ErrorMessage.cs
│ │ ├── PagedResult.cs
│ │ └── Result.cs
│ ├── Extensions
│ │ ├── EnumExtensions.cs
│ │ ├── IdentityResultExtensions.cs
│ │ ├── QueryableExtensions.cs
│ │ └── StringExtensions.cs
│ ├── GreenOnSoftware.Commons.csproj
│ └── Resources
│ │ ├── ErrorMessages.Designer.cs
│ │ └── ErrorMessages.resx
│ ├── GreenOnSoftware.Core
│ ├── Enums
│ │ ├── SnippetFrameAnimationType.cs
│ │ └── Status.cs
│ ├── GreenOnSoftware.Core.csproj
│ ├── Identity
│ │ ├── User.cs
│ │ └── UserRole.cs
│ └── Models
│ │ ├── Article.cs
│ │ ├── Comment.cs
│ │ ├── Ratings
│ │ ├── AnnonymousArticleRate.cs
│ │ └── UserArticleRate.cs
│ │ ├── Reviews
│ │ └── Review.cs
│ │ ├── Snippets
│ │ ├── Snippet.cs
│ │ └── SnippetFrame.cs
│ │ └── _Entity.cs
│ ├── GreenOnSoftware.DataAccess
│ ├── Configuration
│ │ ├── AnnonymousRateConfiguration.cs
│ │ ├── ArticleConfiguration.cs
│ │ ├── CommentConfiguration.cs
│ │ ├── ReviewConfiguration.cs
│ │ ├── SnippetConfiguration.cs
│ │ ├── UserConfiguration.cs
│ │ ├── UserRateConfiguration.cs
│ │ └── UserRoleConfiguration.cs
│ ├── Converters
│ │ └── SnippetFramesValueConverter.cs
│ ├── GreenOnSoftware.DataAccess.csproj
│ ├── GreenOnSoftwareDbContext.cs
│ └── Migrations
│ │ ├── 20221104151505_Init.Designer.cs
│ │ ├── 20221104151505_Init.cs
│ │ ├── 20230531122339_AddSnippetsCreator.Designer.cs
│ │ ├── 20230531122339_AddSnippetsCreator.cs
│ │ ├── 20230603075206_SetSnippetGifUrlOptional.Designer.cs
│ │ ├── 20230603075206_SetSnippetGifUrlOptional.cs
│ │ └── GreenOnSoftwareDbContextModelSnapshot.cs
│ ├── GreenOnSoftware.Infrastructure
│ ├── BlobStorage
│ │ ├── BlobStorageConfiguration.cs
│ │ └── BlobStorageService.cs
│ ├── DependencyInjection.cs
│ ├── GreenOnSoftware.Infrastructure.csproj
│ ├── Security
│ │ ├── GreenOnSoftwareDatabaseInitializer.cs
│ │ └── SecurityConfiguration.cs
│ └── SendGrid
│ │ ├── EmailSenderService.cs
│ │ └── SendGridConfiguration.cs
│ └── GreenOnSoftware.sln
└── system
├── .editorconfig
├── .eslintignore
├── .eslintrc.json
├── .gitignore
├── .husky
└── pre-commit
├── .prettierignore
├── .prettierrc
├── .vscode
└── extensions.json
├── README.md
├── apps
├── .gitkeep
├── blog-creator-e2e
│ ├── .eslintrc.json
│ ├── cypress.config.ts
│ ├── project.json
│ ├── src
│ │ ├── e2e
│ │ │ └── app.cy.ts
│ │ ├── fixtures
│ │ │ └── example.json
│ │ └── support
│ │ │ ├── app.po.ts
│ │ │ ├── commands.ts
│ │ │ └── e2e.ts
│ └── tsconfig.json
├── blog-creator
│ ├── .babelrc
│ ├── .eslintrc.json
│ ├── jest.config.ts
│ ├── project.json
│ ├── src
│ │ ├── app
│ │ │ ├── app.spec.tsx
│ │ │ └── app.tsx
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ ├── environments
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── main.tsx
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.spec.json
│ └── webpack.config.js
├── blog-e2e
│ ├── .eslintrc.json
│ ├── cypress.config.ts
│ ├── project.json
│ ├── src
│ │ ├── e2e
│ │ │ └── app.cy.ts
│ │ ├── fixtures
│ │ │ └── example.json
│ │ └── support
│ │ │ ├── app.po.ts
│ │ │ ├── commands.ts
│ │ │ └── e2e.ts
│ └── tsconfig.json
├── blog
│ ├── .eslintrc.json
│ ├── components
│ │ ├── PageWrapper.tsx
│ │ ├── index.tsx
│ │ ├── link
│ │ │ ├── defs.ts
│ │ │ ├── index.ts
│ │ │ └── link.tsx
│ │ └── main-layout
│ │ │ ├── defs.ts
│ │ │ ├── index.ts
│ │ │ └── main-layout.tsx
│ ├── consts
│ │ └── index.ts
│ ├── content
│ │ ├── en
│ │ │ └── articles
│ │ │ │ └── use-ref-hook.mdx
│ │ └── pl
│ │ │ └── articles
│ │ │ └── use-ref-hook.mdx
│ ├── dk
│ │ ├── index.ts
│ │ ├── lang.ts
│ │ ├── tests
│ │ │ ├── lang.test.ts
│ │ │ └── use-lang.test.ts
│ │ └── use-lang.ts
│ ├── index.d.ts
│ ├── jest.config.ts
│ ├── models
│ │ └── index.ts
│ ├── next-env.d.ts
│ ├── next.config.js
│ ├── pages
│ │ ├── _app.tsx
│ │ ├── _document.tsx
│ │ ├── en
│ │ │ ├── articles
│ │ │ │ ├── [id].tsx
│ │ │ │ └── index.tsx
│ │ │ └── index.tsx
│ │ ├── index.tsx
│ │ └── pl
│ │ │ ├── articles
│ │ │ ├── [id].tsx
│ │ │ └── index.tsx
│ │ │ └── index.tsx
│ ├── project.json
│ ├── public
│ │ ├── .gitkeep
│ │ └── fonts
│ │ │ ├── LexendBold.ttf
│ │ │ ├── LexendLight.ttf
│ │ │ ├── LexendMedium.ttf
│ │ │ └── LexendRegular.ttf
│ ├── tsconfig.json
│ ├── tsconfig.spec.json
│ ├── utils
│ │ ├── index.test.ts
│ │ └── index.ts
│ └── views
│ │ └── home
│ │ ├── defs.ts
│ │ ├── home.view.tsx
│ │ └── index.ts
├── jamjam-e2e
│ ├── .eslintrc.json
│ ├── cypress.config.ts
│ ├── project.json
│ ├── src
│ │ ├── e2e
│ │ │ └── guitar-notes-teacher.cy.ts
│ │ ├── fixtures
│ │ │ └── example.json
│ │ └── support
│ │ │ ├── app.po.ts
│ │ │ ├── commands.ts
│ │ │ └── e2e.ts
│ └── tsconfig.json
├── jamjam
│ ├── .eslintrc.json
│ ├── components
│ │ ├── guitar-fretboard
│ │ │ ├── consts.ts
│ │ │ ├── defs.ts
│ │ │ ├── guitar-fretboard-strings.tsx
│ │ │ ├── guitar-fretboard.tsx
│ │ │ ├── index.ts
│ │ │ ├── note-button.tsx
│ │ │ └── tests
│ │ │ │ ├── __snapshots__
│ │ │ │ └── guitar-fretboard-strings.test.tsx.snap
│ │ │ │ ├── consts.test.ts
│ │ │ │ ├── guitar-fretboard-strings.test.tsx
│ │ │ │ ├── guitar-fretboard.test.tsx
│ │ │ │ └── note-button.test.tsx
│ │ └── index.ts
│ ├── domain
│ │ ├── index.ts
│ │ └── music-theory
│ │ │ ├── consts.ts
│ │ │ ├── core.ts
│ │ │ ├── defs.ts
│ │ │ ├── index.ts
│ │ │ └── tests
│ │ │ ├── consts.test.ts
│ │ │ └── core.test.ts
│ ├── features
│ │ └── guitar-notes-teacher
│ │ │ ├── __snapshots__
│ │ │ └── guitar-notes-teacher.view.test.tsx.snap
│ │ │ ├── components
│ │ │ ├── index.ts
│ │ │ └── links
│ │ │ │ ├── __snapshots__
│ │ │ │ └── links.test.tsx.snap
│ │ │ │ ├── index.ts
│ │ │ │ ├── links.test.tsx
│ │ │ │ └── links.tsx
│ │ │ ├── guitar-notes-teacher.actions.test.ts
│ │ │ ├── guitar-notes-teacher.actions.ts
│ │ │ ├── guitar-notes-teacher.defs.ts
│ │ │ ├── guitar-notes-teacher.feature.tsx
│ │ │ ├── guitar-notes-teacher.selectors.ts
│ │ │ ├── guitar-notes-teacher.view.test.tsx
│ │ │ ├── guitar-notes-teacher.view.tsx
│ │ │ ├── index.ts
│ │ │ ├── use-guitar-notes-teacher.facade.test.ts
│ │ │ └── use-guitar-notes-teacher.facade.ts
│ ├── index.d.ts
│ ├── jest.config.ts
│ ├── next-env.d.ts
│ ├── next.config.js
│ ├── pages
│ │ ├── _app.tsx
│ │ ├── _document.tsx
│ │ └── index.tsx
│ ├── project.json
│ ├── public
│ │ ├── .gitkeep
│ │ └── fonts
│ │ │ ├── LexendBold.ttf
│ │ │ ├── LexendLight.ttf
│ │ │ ├── LexendMedium.ttf
│ │ │ └── LexendRegular.ttf
│ ├── specs
│ │ └── index.spec.tsx
│ ├── tsconfig.json
│ ├── tsconfig.spec.json
│ └── utils
│ │ ├── index.test.ts
│ │ └── index.ts
├── sparkle-flicks-e2e
│ ├── .eslintrc.json
│ ├── cypress.config.ts
│ ├── project.json
│ ├── src
│ │ ├── e2e
│ │ │ └── app.cy.ts
│ │ ├── fixtures
│ │ │ └── example.json
│ │ └── support
│ │ │ ├── commands.ts
│ │ │ └── e2e.ts
│ └── tsconfig.json
└── sparkle-flicks
│ ├── .eslintrc.json
│ ├── index.d.ts
│ ├── jest.config.ts
│ ├── next-env.d.ts
│ ├── next.config.js
│ ├── pages
│ ├── _app.tsx
│ ├── _document.tsx
│ └── index.tsx
│ ├── project.json
│ ├── public
│ ├── .gitkeep
│ └── fonts
│ │ ├── LexendBold.ttf
│ │ ├── LexendLight.ttf
│ │ ├── LexendMedium.ttf
│ │ └── LexendRegular.ttf
│ ├── specs
│ └── index.spec.tsx
│ ├── tsconfig.json
│ └── tsconfig.spec.json
├── assets
└── fonts
│ ├── LexendBold.ttf
│ ├── LexendLight.ttf
│ ├── LexendMedium.ttf
│ └── LexendRegular.ttf
├── babel.config.json
├── build-storybook.log
├── jest.config.ts
├── jest.preset.js
├── libs
├── .gitkeep
├── blog-api
│ ├── .eslintrc.json
│ ├── README.md
│ ├── jest.config.ts
│ ├── package.json
│ ├── project.json
│ ├── src
│ │ ├── index.ts
│ │ ├── lib
│ │ │ ├── account.ts
│ │ │ ├── articles.ts
│ │ │ ├── index.ts
│ │ │ └── instance.ts
│ │ └── models
│ │ │ ├── account.ts
│ │ │ ├── articles.ts
│ │ │ ├── general.ts
│ │ │ └── index.ts
│ ├── tsconfig.json
│ ├── tsconfig.lib.json
│ └── tsconfig.spec.json
├── figa-hooks
│ ├── .eslintrc.json
│ ├── README.md
│ ├── jest.config.ts
│ ├── package.json
│ ├── project.json
│ ├── src
│ │ ├── index.ts
│ │ └── lib
│ │ │ ├── use-click-outside
│ │ │ ├── defs.ts
│ │ │ ├── index.ts
│ │ │ ├── use-click-outside.test.tsx
│ │ │ └── use-click-outside.ts
│ │ │ ├── use-client-effect
│ │ │ ├── index.ts
│ │ │ ├── use-client-effect.test.tsx
│ │ │ └── use-client-effect.ts
│ │ │ ├── use-client-layout-effect
│ │ │ ├── index.ts
│ │ │ ├── use-client-layout-effect.test.tsx
│ │ │ └── use-client-layout-effect.tsx
│ │ │ ├── use-intersection-observer
│ │ │ ├── defs.ts
│ │ │ ├── index.ts
│ │ │ ├── use-intersection-observer.test.tsx
│ │ │ └── use-intersection-observer.ts
│ │ │ ├── use-portal
│ │ │ ├── defs.ts
│ │ │ ├── index.ts
│ │ │ └── use-portal.tsx
│ │ │ ├── use-scroll-y
│ │ │ ├── defs.ts
│ │ │ ├── index.ts
│ │ │ ├── use-scroll-y.test.tsx
│ │ │ └── use-scroll-y.ts
│ │ │ ├── use-stepper
│ │ │ ├── defs.ts
│ │ │ ├── index.ts
│ │ │ ├── use-stepper.test.tsx
│ │ │ └── use-stepper.tsx
│ │ │ └── use-toggle
│ │ │ ├── defs.ts
│ │ │ ├── index.ts
│ │ │ └── use-toggle.tsx
│ ├── tsconfig.json
│ ├── tsconfig.lib.json
│ ├── tsconfig.spec.json
│ └── vite.config.ts
├── figa-ui
│ ├── .eslintrc.json
│ ├── .storybook
│ │ ├── main.js
│ │ ├── preview.tsx
│ │ └── tsconfig.json
│ ├── README.md
│ ├── jest.config.ts
│ ├── package.json
│ ├── project.json
│ ├── src
│ │ ├── index.ts
│ │ └── lib
│ │ │ ├── article-layout
│ │ │ ├── __snapshots__
│ │ │ │ └── article-layout.test.tsx.snap
│ │ │ ├── article-layout.stories.tsx
│ │ │ ├── article-layout.test.tsx
│ │ │ ├── article-layout.tsx
│ │ │ ├── defs.ts
│ │ │ └── index.ts
│ │ │ ├── blockquote
│ │ │ ├── __snapshots__
│ │ │ │ └── blockquote.test.tsx.snap
│ │ │ ├── blockquote.stories.tsx
│ │ │ ├── blockquote.test.tsx
│ │ │ ├── blockquote.tsx
│ │ │ ├── defs.ts
│ │ │ └── index.ts
│ │ │ ├── box
│ │ │ ├── __snapshots__
│ │ │ │ └── box.test.tsx.snap
│ │ │ ├── box.stories.tsx
│ │ │ ├── box.test.tsx
│ │ │ ├── box.tsx
│ │ │ ├── defs.ts
│ │ │ └── index.ts
│ │ │ ├── button
│ │ │ ├── __snapshots__
│ │ │ │ └── button.test.tsx.snap
│ │ │ ├── button.stories.tsx
│ │ │ ├── button.test.tsx
│ │ │ ├── button.tsx
│ │ │ ├── consts.ts
│ │ │ ├── defs.ts
│ │ │ └── index.ts
│ │ │ ├── code-block
│ │ │ ├── __snapshots__
│ │ │ │ └── code-block.test.tsx.snap
│ │ │ ├── code-block.stories.tsx
│ │ │ ├── code-block.test.tsx
│ │ │ ├── code-block.tsx
│ │ │ ├── defs.ts
│ │ │ └── index.ts
│ │ │ ├── code
│ │ │ ├── code.stories.tsx
│ │ │ ├── code.test.tsx
│ │ │ ├── code.tsx
│ │ │ ├── consts.ts
│ │ │ ├── defs.ts
│ │ │ ├── index.ts
│ │ │ ├── setup.test.ts
│ │ │ └── setup.ts
│ │ │ ├── emoji-picker
│ │ │ ├── consts.ts
│ │ │ ├── defs.ts
│ │ │ ├── emoji-picker.stories.tsx
│ │ │ ├── emoji-picker.test.tsx
│ │ │ ├── emoji-picker.tsx
│ │ │ └── index.ts
│ │ │ ├── font
│ │ │ ├── consts.ts
│ │ │ ├── defs.ts
│ │ │ ├── font.stories.tsx
│ │ │ ├── font.tsx
│ │ │ └── index.ts
│ │ │ ├── footer
│ │ │ ├── __snapshots__
│ │ │ │ └── footer.test.tsx.snap
│ │ │ ├── defs.ts
│ │ │ ├── footer.stories.tsx
│ │ │ ├── footer.test.tsx
│ │ │ ├── footer.tsx
│ │ │ └── index.ts
│ │ │ ├── icon
│ │ │ ├── __snapshots__
│ │ │ │ └── icon.test.tsx.snap
│ │ │ ├── defs.ts
│ │ │ ├── icon.stories.tsx
│ │ │ ├── icon.test.tsx
│ │ │ ├── icon.tsx
│ │ │ └── index.ts
│ │ │ ├── input
│ │ │ ├── __snapshots__
│ │ │ │ └── input.test.tsx.snap
│ │ │ ├── defs.ts
│ │ │ ├── index.ts
│ │ │ ├── input.stories.tsx
│ │ │ ├── input.test.tsx
│ │ │ └── input.tsx
│ │ │ ├── layout
│ │ │ ├── __snapshots__
│ │ │ │ └── layout.test.tsx.snap
│ │ │ ├── defs.ts
│ │ │ ├── index.ts
│ │ │ ├── layout.stories.tsx
│ │ │ ├── layout.test.tsx
│ │ │ └── layout.tsx
│ │ │ ├── left-bar
│ │ │ ├── __snapshots__
│ │ │ │ └── left-bar.test.tsx.snap
│ │ │ ├── defs.ts
│ │ │ ├── index.ts
│ │ │ ├── left-bar.stories.tsx
│ │ │ ├── left-bar.test.tsx
│ │ │ └── left-bar.tsx
│ │ │ ├── link
│ │ │ ├── __snapshots__
│ │ │ │ └── link.test.tsx.snap
│ │ │ ├── defs.ts
│ │ │ ├── index.ts
│ │ │ ├── link.stories.tsx
│ │ │ ├── link.test.tsx
│ │ │ └── link.tsx
│ │ │ ├── list
│ │ │ ├── __snapshots__
│ │ │ │ └── list.test.tsx.snap
│ │ │ ├── defs.ts
│ │ │ ├── index.ts
│ │ │ ├── list.stories.tsx
│ │ │ ├── list.test.tsx
│ │ │ └── list.tsx
│ │ │ ├── logo-graphic
│ │ │ ├── __snapshots__
│ │ │ │ └── logo-graphic.test.tsx.snap
│ │ │ ├── defs.ts
│ │ │ ├── index.ts
│ │ │ ├── logo-graphic.stories.tsx
│ │ │ ├── logo-graphic.test.tsx
│ │ │ └── logo-graphic.tsx
│ │ │ ├── logo
│ │ │ ├── __snapshots__
│ │ │ │ └── logo.test.tsx.snap
│ │ │ ├── defs.ts
│ │ │ ├── index.ts
│ │ │ ├── logo.stories.tsx
│ │ │ ├── logo.test.tsx
│ │ │ └── logo.tsx
│ │ │ ├── modal
│ │ │ ├── defs.ts
│ │ │ ├── index.ts
│ │ │ ├── modal.stories.tsx
│ │ │ └── modal.tsx
│ │ │ ├── navigation
│ │ │ ├── __snapshots__
│ │ │ │ └── navigation.test.tsx.snap
│ │ │ ├── defs.ts
│ │ │ ├── index.ts
│ │ │ ├── navigation.stories.tsx
│ │ │ ├── navigation.test.tsx
│ │ │ └── navigation.tsx
│ │ │ ├── popover
│ │ │ ├── __snapshots__
│ │ │ │ └── popover.test.tsx.snap
│ │ │ ├── defs.ts
│ │ │ ├── index.ts
│ │ │ ├── popover.stories.tsx
│ │ │ ├── popover.test.tsx
│ │ │ └── popover.tsx
│ │ │ ├── progress-circle
│ │ │ ├── __snapshots__
│ │ │ │ └── progress-circle.test.tsx.snap
│ │ │ ├── defs.ts
│ │ │ ├── index.ts
│ │ │ ├── progress-circle.stories.tsx
│ │ │ ├── progress-circle.test.tsx
│ │ │ └── progress-circle.tsx
│ │ │ ├── sandbox
│ │ │ ├── defs.ts
│ │ │ ├── index.ts
│ │ │ └── sandbox.tsx
│ │ │ ├── select
│ │ │ ├── __snapshots__
│ │ │ │ └── select.test.tsx.snap
│ │ │ ├── defs.ts
│ │ │ ├── index.ts
│ │ │ ├── select.stories.tsx
│ │ │ ├── select.test.tsx
│ │ │ └── select.tsx
│ │ │ ├── shared
│ │ │ ├── defs.ts
│ │ │ ├── generators.ts
│ │ │ └── index.ts
│ │ │ └── theme-provider
│ │ │ ├── defs.ts
│ │ │ ├── global-style.ts
│ │ │ ├── index.ts
│ │ │ ├── theme-provider.tsx
│ │ │ ├── themes.ts
│ │ │ ├── viewport.test.ts
│ │ │ └── viewport.ts
│ ├── tsconfig.json
│ ├── tsconfig.lib.json
│ ├── tsconfig.spec.json
│ └── vite.config.ts
└── utils
│ ├── .eslintrc.json
│ ├── README.md
│ ├── jest.config.ts
│ ├── package.json
│ ├── project.json
│ ├── src
│ ├── index.ts
│ └── lib
│ │ ├── is-client.ts
│ │ ├── is-server.ts
│ │ ├── storage.ts
│ │ └── tests
│ │ ├── is-client.test.ts
│ │ ├── is-server.test.ts
│ │ └── storage.test.ts
│ ├── tsconfig.json
│ ├── tsconfig.lib.json
│ └── tsconfig.spec.json
├── nx.json
├── package-lock.json
├── package.json
├── tools
├── generators
│ └── .gitkeep
└── tsconfig.tools.json
└── tsconfig.base.json
/.github/workflows/chromatic.yaml:
--------------------------------------------------------------------------------
1 | name: Chromatic
2 |
3 | on:
4 | pull_request:
5 | branches: ["develop", "main"]
6 |
7 | jobs:
8 | chromatic-deployment:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Checkout repository
12 | uses: actions/checkout@v2
13 | with:
14 | fetch-depth: 0
15 | - name: Install dependencies
16 | run: |
17 | cd system
18 | npm install --legacy-peer-deps
19 | - name: Build storybook
20 | run: |
21 | cd system
22 | npm run build-storybook
23 | - name: Build storybook
24 | run: |
25 | cd system/dist/storybook/figa-ui
26 | npm run chromatic --project-token=${{ secrets.FIGA_UI_CHROMATIC_TOKEN }}
--------------------------------------------------------------------------------
/.github/workflows/e2e.yaml:
--------------------------------------------------------------------------------
1 | name: Tests e2e
2 |
3 | on:
4 | pull_request:
5 | branches: ["develop", "main"]
6 |
7 | jobs:
8 | run-tests:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Checkout repository
12 | uses: actions/checkout@v2
13 | with:
14 | fetch-depth: 0
15 | - name: Install dependencies
16 | run: |
17 | cd system
18 | npm install --legacy-peer-deps
19 | - name: Cypress e2e tests
20 | run: |
21 | cd system
22 | npm run e2e:checkAll
--------------------------------------------------------------------------------
/.github/workflows/units.yaml:
--------------------------------------------------------------------------------
1 | name: Tests
2 |
3 | on:
4 | pull_request:
5 | branches: ["develop", "main"]
6 |
7 | jobs:
8 | run-tests:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Checkout repository
12 | uses: actions/checkout@v2
13 | with:
14 | fetch-depth: 0
15 | - name: Install dependencies
16 | run: |
17 | cd system
18 | npm install --legacy-peer-deps
19 | - name: Jest run test
20 | run: |
21 | cd system
22 | npm run test:checkAll
--------------------------------------------------------------------------------
/.vscode/cb.code-snippets:
--------------------------------------------------------------------------------
1 | {
2 | "ct": {
3 | "prefix": "ct",
4 | "body": [
5 | "import { render, screen } from '@testing-library/react';",
6 | "import { ${1:example} } from './${2:example}';",
7 | "",
8 | "describe('${1:example} can be used when', () => {",
9 | " it('[FRAGILE] assigns classes', () => {",
10 | " const { container, asFragment } = render(",
11 | " <${1:example} className=\"my-class\">",
12 | " <>>",
13 | " ${1:example}>",
14 | " );",
15 | "",
16 | " const component = container.querySelector('.${2:example}');",
17 | "",
18 | " expect(component?.className).toContain('${2:example} my-class');",
19 | " expect(asFragment()).toMatchSnapshot();",
20 | " });",
21 | "});",
22 | ""
23 | ],
24 | "description": ""
25 | }
26 | }
--------------------------------------------------------------------------------
/.vscode/cmp.code-snippets:
--------------------------------------------------------------------------------
1 | {
2 | "cmp": {
3 | "prefix": "cmp",
4 | "body": [
5 | "import type { ${1:example}Props } from './defs';",
6 | "",
7 | "interface ${1:example}Props {",
8 | " ",
9 | "}",
10 | "",
11 | "export type { ${1:example}Props };",
12 | "",
13 | "const ${1:example}= ({ }: ${1:example}Props) => {",
14 | " return <${2:example}>${2:example}>",
15 | "};",
16 | "",
17 | "export { ${1:example}};",
18 | ""
19 | ],
20 | "description": ""
21 | }
22 | }
--------------------------------------------------------------------------------
/.vscode/csb.code-snippets:
--------------------------------------------------------------------------------
1 | {
2 | "csb": {
3 | "prefix": "csb",
4 | "body": [
5 | "import type { Story, Meta } from '@storybook/react';",
6 | "",
7 | "import { ${1:example} } from './${2:example}';",
8 | "",
9 | "export default {",
10 | " component: ${1:example},",
11 | " title: '${1:example}',",
12 | "} as Meta;",
13 | "",
14 | "const Template: Story = () => {",
15 | " return (",
16 | "
",
17 | " ",
18 | "
",
19 | " );",
20 | "};",
21 | "",
22 | "export const Default = Template.bind({});",
23 | "Default.args = {};",
24 | ""
25 | ],
26 | "description": ""
27 | }
28 | }
--------------------------------------------------------------------------------
/.vscode/ex.code-snippets:
--------------------------------------------------------------------------------
1 | {
2 | "ex": {
3 | "prefix": "ex",
4 | "body": [
5 | "export * from './${1:example}';",
6 | "export * from './defs';",
7 | ""
8 | ],
9 | "description": ""
10 | }
11 | }
--------------------------------------------------------------------------------
/.vscode/hk.code-snippets:
--------------------------------------------------------------------------------
1 | {
2 | "hk": {
3 | "prefix": "hk",
4 | "body": [
5 | "import { useState } from 'react';",
6 | "",
7 | "interface ${3:example}{",
8 | "",
9 | "}",
10 | "",
11 | "interface ${4:example}{",
12 | "",
13 | "}",
14 | "",
15 | "type Use${1:example} = (payload: ${3:example}) => ${4:example};",
16 | "",
17 | "export type { ${3:example}, ${4:example}}",
18 | "",
19 | "const use${1:example} = (${2:example}: ${3:example}): ${4:example} => {",
20 | " const [value, setValue] = useState()",
21 | "",
22 | " const handleSm = () => {",
23 | "",
24 | " }",
25 | " ",
26 | " return {",
27 | "",
28 | " }",
29 | "}",
30 | "",
31 | "export { use${1:example} }"
32 | ],
33 | "description": ""
34 | }
35 | }
--------------------------------------------------------------------------------
/.vscode/pg.code-snippets:
--------------------------------------------------------------------------------
1 | {
2 | "pg": {
3 | "prefix": "pg",
4 | "body": [
5 | "import { Font } from '@system/figa-ui';",
6 | "",
7 | "const ${1:example}= () => {",
8 | " return England;",
9 | "};",
10 | "",
11 | "export default ${1:example};",
12 | ""
13 | ],
14 | "description": ""
15 | }
16 | }
--------------------------------------------------------------------------------
/.vscode/vfn.code-snippets:
--------------------------------------------------------------------------------
1 | {
2 | "vfn": {
3 | "prefix": "vfn",
4 | "body": [
5 | "const ${1:example} = (${2:example}: ${3:example}): void => {",
6 | "",
7 | "}"
8 | ],
9 | "description": ""
10 | }
11 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 |
3 | # CS8618: Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
4 | dotnet_diagnostic.CS8618.severity = none
5 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Api/Context/Context.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Context;
2 |
3 | namespace GreenOnSoftware.Api.Context;
4 |
5 | public class Context : IContext
6 | {
7 | public IIdentityContext Identity { get; }
8 |
9 | public Context(HttpContext httpContext)
10 | {
11 | Identity = new IdentityContext(httpContext);
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Api/Context/ContextAccessor.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Context;
2 |
3 | namespace GreenOnSoftware.Api.Context;
4 |
5 | public class ContextAccessor : IContextAccessor
6 | {
7 | private static readonly AsyncLocal Holder = new();
8 |
9 | public IContext? Context
10 | {
11 | get => Holder.Value?.Context;
12 | set
13 | {
14 | var holder = Holder.Value;
15 | if (holder != null)
16 | {
17 | holder.Context = null;
18 | }
19 |
20 | if (value != null)
21 | {
22 | Holder.Value = new ContextHolder { Context = value };
23 | }
24 | }
25 | }
26 |
27 | private class ContextHolder
28 | {
29 | public IContext? Context { get; set; }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Api/Context/ContextConfig.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Context;
2 |
3 | namespace GreenOnSoftware.Api.Context;
4 |
5 | public static class ContextConfig
6 | {
7 | public static IServiceCollection AddIdentityContext(this IServiceCollection services)
8 | {
9 | services.AddSingleton();
10 | services.AddScoped(s => s.GetRequiredService().Context!);
11 |
12 | return services;
13 | }
14 |
15 | public static IApplicationBuilder UseIdentityContext(this IApplicationBuilder app)
16 | {
17 | app.Use((ctx, next) => {
18 | ctx.RequestServices.GetRequiredService().Context = new Context(ctx);
19 |
20 | return next();
21 | });
22 |
23 | return app;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Api/Filters/LogAttribute.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc.Filters;
2 | using Serilog;
3 |
4 | namespace GreenOnSoftware.Api.Filters;
5 |
6 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
7 | public class LogAttribute : ActionFilterAttribute
8 | {
9 | public override void OnActionExecuting(ActionExecutingContext context)
10 | {
11 | Log.Information($"Started processing request {context.HttpContext.Request.Path}");
12 | }
13 |
14 | public override void OnActionExecuted(ActionExecutedContext context)
15 | {
16 | string message;
17 |
18 | if (context.HttpContext.Response.StatusCode >= StatusCodes.Status400BadRequest)
19 | {
20 | message = "Ended processing request with errors.";
21 | }
22 | else
23 | {
24 | message = "Ended processing request with success.";
25 | }
26 | Log.Information(message);
27 | }
28 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Api/Middlewares/LogUsernameMiddleware.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Context;
2 |
3 | namespace GreenOnSoftware.Api.Middlewares;
4 |
5 | public class LogUsernameMiddleware
6 | {
7 | private readonly RequestDelegate next;
8 |
9 | public LogUsernameMiddleware(RequestDelegate next)
10 | {
11 | this.next = next;
12 | }
13 |
14 | public async Task Invoke(HttpContext context)
15 | {
16 | LogContext.PushProperty("Username", context.User.Identity?.Name);
17 |
18 | await next(context);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Api/Startup/InvalidModelStateResponseFactory.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using Microsoft.AspNetCore.Mvc;
3 |
4 | namespace GreenOnSoftware.Api.Startup;
5 |
6 | public static class InvalidModelStateResponseFactory
7 | {
8 | public static BadRequestObjectResult CustomErrorResponse(ActionContext actionContext)
9 | {
10 | var errors = actionContext.ModelState
11 | .Where(modelError => modelError.Value?.Errors.Count > 0)
12 | .ToDictionary(x => x.Key, x => x.Value?.Errors.FirstOrDefault()?.ErrorMessage ?? string.Empty);
13 |
14 | return new BadRequestObjectResult(new Result(errors));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Api/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | "GreenOnSoftware": "Server=.;Database=GreenOnSoftware;Trusted_Connection=True;MultipleActiveResultSets=true"
4 | },
5 |
6 | "Application": {
7 | "ResetPasswordUrl": ""
8 | },
9 |
10 | "Security": {
11 | "AdminNames": "--secret--",
12 | "AdminPassword": "--secret--",
13 | "ResetPasswordUrl": ""
14 | },
15 |
16 | "SendGrid": {
17 | "SenderEmailAddress": "greenonsoftware2022@gmail.com",
18 | "SenderName": "GreenOnSoftware - local",
19 | "ApiKey": "--secret--"
20 | },
21 | "BlobStorage": {
22 | "ConnectionString": "--secret--",
23 | "Container": "dev"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Api/appsettings.dev.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | "GreenOnSoftware": "--secret--"
4 | },
5 |
6 | "Application": {
7 | "ResetPasswordUrl": ""
8 | },
9 |
10 | "Security": {
11 | "AdminNames": "--secret--",
12 | "AdminPassword": "--secret--",
13 | "ResetPasswordUrl": ""
14 | },
15 |
16 | "SendGrid": {
17 | "SenderEmailAddress": "greenonsoftware2022@gmail.com",
18 | "SenderName": "GreenOnSoftware - dev",
19 | "ApiKey": "--secret--"
20 | },
21 |
22 | "BlobStorage": {
23 | "ConnectionString": "--secret--",
24 | "Container": "dev"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Api/appsettings.prod.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | "GreenOnSoftware": "--secret--"
4 | },
5 |
6 | "Application": {
7 | "ResetPasswordUrl": ""
8 | },
9 |
10 | "Security": {
11 | "AdminNames": "--secret--",
12 | "AdminPassword": "--secret--",
13 | "ResetPasswordUrl": ""
14 | },
15 |
16 | "SendGrid": {
17 | "SenderEmailAddress": "greenonsoftware2022@gmail.com",
18 | "SenderName": "GreenOnSoftware",
19 | "ApiKey": "--secret--"
20 | },
21 |
22 | "BlobStorage": {
23 | "ConnectionString": "--secret--",
24 | "Container": "prod"
25 | },
26 |
27 | "CorsOriginsUrls": [
28 | "https://greenonsoftware.com"
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Account/ChangePasswordCommand/ChangePassword.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using MediatR;
3 |
4 | namespace GreenOnSoftware.Application.Account.ChangePasswordCommand;
5 |
6 | public record ChangePassword(string CurrentPassword, string NewPassword, string ConfirmNewPassword) : IRequest;
7 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Account/ChangePasswordCommand/ChangePasswordValidator.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 | using GreenOnSoftware.Application.Account.ChangePasswordCommand;
3 |
4 | namespace GreenOnSoftware.Application.Account.ChangePasswordCommand;
5 |
6 | internal class ChangePasswordValidator : AbstractValidator
7 | {
8 | public ChangePasswordValidator()
9 | {
10 |
11 | RuleFor(x => x.CurrentPassword)
12 | .NotEmpty()
13 | .MaximumLength(100);
14 |
15 | RuleFor(x => x.NewPassword)
16 | .NotEmpty()
17 | .MaximumLength(100);
18 |
19 | RuleFor(x => x.ConfirmNewPassword)
20 | .NotEmpty()
21 | .MaximumLength(100)
22 | .Matches(x => x.NewPassword);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Account/ForgottenPasswordCommand/ForgottenPassword.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using MediatR;
3 |
4 | namespace GreenOnSoftware.Application.Account.ForgottenPasswordCommand;
5 |
6 | public record ForgottenPassword(string Login) : IRequest;
7 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Account/ForgottenPasswordCommand/ForgottenPasswordValidator.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 | using GreenOnSoftware.Application.Account.ForgottenPasswordCommand;
3 |
4 | namespace GreenOnSoftware.Application.Account.ForgottenPasswordCommand;
5 |
6 | internal class ForgottenPasswordValidator: AbstractValidator
7 | {
8 | public ForgottenPasswordValidator()
9 | {
10 | RuleFor(x => x.Login)
11 | .NotEmpty();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Account/GetAvatarNameQuery/GetAvatarName.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using MediatR;
3 |
4 | namespace GreenOnSoftware.Application.Account.GetAvatarNameQuery;
5 |
6 | public record GetAvatarName() : IRequest>;
7 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Account/GetAvatarNameQuery/GetAvatarNameHandler.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using MediatR;
3 | using GreenOnSoftware.Commons.Context;
4 | using GreenOnSoftware.Application.Account.GetAvatarNameQuery;
5 |
6 | namespace GreenOnSoftware.Application.Account.GetAvatarNameQuery;
7 |
8 | internal class GetAvatarNameHandler : IRequestHandler>
9 | {
10 | private readonly IContext _context;
11 |
12 | public GetAvatarNameHandler(IContext context)
13 | {
14 | _context = context;
15 | }
16 |
17 | public Task> Handle(GetAvatarName query, CancellationToken cancellationToken)
18 | {
19 | var result = new Result();
20 |
21 | result.SetData(_context.Identity.AvatarName);
22 |
23 | return Task.FromResult(result);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Account/RegisterCommand/Register.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using MediatR;
3 |
4 | namespace GreenOnSoftware.Application.Account.RegisterCommand;
5 |
6 | public record Register(string Login, string Email, string Password, string ConfirmPassword) : IRequest;
7 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Account/RegisterCommand/RegisterValidator.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 | using GreenOnSoftware.Application.Account.RegisterCommand;
3 |
4 | namespace GreenOnSoftware.Application.Account.RegisterCommand;
5 |
6 | internal class RegisterValidator : AbstractValidator
7 | {
8 | public RegisterValidator()
9 | {
10 | RuleFor(x => x.Login)
11 | .NotEmpty()
12 | .MaximumLength(100);
13 |
14 | RuleFor(x => x.Email)
15 | .NotEmpty()
16 | .EmailAddress();
17 |
18 | RuleFor(x => x.Password)
19 | .NotEmpty()
20 | .MaximumLength(100);
21 |
22 | RuleFor(x => x.ConfirmPassword)
23 | .NotEmpty()
24 | .MaximumLength(100)
25 | .Matches(x => x.Password);
26 | }
27 | }
28 |
29 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Account/ResetPasswordCommand/ResetPassword.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using MediatR;
3 |
4 | namespace GreenOnSoftware.Application.Account.ResetPasswordCommand;
5 | public record ResetPassword(string NewPassword, string ConfirmNewPassword, string UserId, string Token) : IRequest;
6 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Account/ResetPasswordCommand/ResetPasswordValidator.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 | using GreenOnSoftware.Application.Account.ResetPasswordCommand;
3 |
4 | namespace GreenOnSoftware.Application.Account.ResetPasswordCommand;
5 |
6 | internal class ResetPasswordValidator : AbstractValidator
7 | {
8 | public ResetPasswordValidator()
9 | {
10 | RuleFor(x => x.UserId)
11 | .NotEmpty()
12 | .MaximumLength(36);
13 |
14 | RuleFor(x => x.Token)
15 | .NotEmpty()
16 | .MaximumLength(400);
17 |
18 | RuleFor(x => x.NewPassword)
19 | .NotEmpty()
20 | .MaximumLength(100);
21 |
22 | RuleFor(x => x.ConfirmNewPassword)
23 | .NotEmpty()
24 | .MaximumLength(100)
25 | .Matches(x => x.NewPassword);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Account/SetAvatarNameCommand/SetAvatarName.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using MediatR;
3 |
4 | namespace GreenOnSoftware.Application.Ratings.SetAvatarNameCommand;
5 |
6 | public record SetAvatarName(string AvatarName) : IRequest;
7 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Account/SetAvatarNameCommand/SetAvatarNameHandler.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Application.Services.Interfaces;
2 | using GreenOnSoftware.Commons.Dtos;
3 | using MediatR;
4 |
5 | namespace GreenOnSoftware.Application.Ratings.SetAvatarNameCommand;
6 |
7 | internal class SetAvatarNameHandler : IRequestHandler
8 | {
9 | private readonly IRatingsSessionService _ratingsSessionService;
10 |
11 | public SetAvatarNameHandler(IRatingsSessionService ratingsSessionService)
12 | {
13 | _ratingsSessionService = ratingsSessionService;
14 | }
15 |
16 | public Task Handle(SetAvatarName command, CancellationToken cancellationToken)
17 | {
18 | var result = new Result();
19 |
20 | _ratingsSessionService.SetAvatarName(command.AvatarName);
21 |
22 | return Task.FromResult(result);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Account/SetAvatarNameCommand/SetAvatarNameValidator.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 |
3 | namespace GreenOnSoftware.Application.Ratings.SetAvatarNameCommand;
4 |
5 | internal class SetAvatarNameValidator : AbstractValidator
6 | {
7 | public SetAvatarNameValidator()
8 | {
9 | RuleFor(x => x.AvatarName)
10 | .NotEmpty()
11 | .MinimumLength(2)
12 | .MaximumLength(30);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Account/SignInCommand/SignIn.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using MediatR;
3 |
4 | namespace GreenOnSoftware.Application.Account.SignInCommand;
5 |
6 | public record SignIn(string Login, string Password) : IRequest;
7 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Account/SignOutCommand/SignOut.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using MediatR;
3 |
4 | namespace GreenOnSoftware.Application.Account.SignOutCommand;
5 |
6 | public record SignOut() : IRequest;
7 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Account/SignOutCommand/SignOutHandler.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Identity;
2 | using MediatR;
3 | using GreenOnSoftware.Commons.Dtos;
4 | using GreenOnSoftware.Application.Account.SignOutCommand;
5 | using GreenOnSoftware.Core.Identity;
6 |
7 | namespace GreenOnSoftware.Application.Account.SignOutCommand;
8 |
9 | internal class SignOutHandler : IRequestHandler
10 | {
11 | private readonly SignInManager _signInManager;
12 |
13 | public SignOutHandler(SignInManager signInManager)
14 | {
15 | _signInManager = signInManager;
16 | }
17 |
18 | public async Task Handle(SignOut command, CancellationToken cancellationToken)
19 | {
20 | var response = new Result();
21 | await _signInManager.SignOutAsync();
22 |
23 | return response;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Articles/AcceptArticleCommand/AcceptArticle.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using MediatR;
3 |
4 | namespace GreenOnSoftware.Application.Articles.AcceptArticleCommand;
5 |
6 | public record AcceptArticle(Guid Id) : IRequest;
7 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Articles/AddArticleCommand/AddArticle.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using MediatR;
3 | using Microsoft.AspNetCore.Http;
4 |
5 | namespace GreenOnSoftware.Application.Articles.AddArticleCommand;
6 |
7 | public record AddArticle(string Title, string? Description, string Content, IFormFile? Thumbnail, string Url) : IRequest>;
8 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Articles/AddArticleCommand/AddArticleValidator.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 |
3 | namespace GreenOnSoftware.Application.Articles.AddArticleCommand;
4 |
5 | public class AddArticleValidator : AbstractValidator
6 | {
7 | public AddArticleValidator()
8 | {
9 | RuleFor(x => x.Title).NotEmpty();
10 | RuleFor(x => x.Content).NotEmpty();
11 |
12 | RuleFor(x => x.Title)
13 | .MaximumLength(200);
14 | RuleFor(x => x.Description)
15 | .MaximumLength(500);
16 | RuleFor(x => x.Url)
17 | .NotEmpty()
18 | .MaximumLength(300);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Articles/DeleteArticleCommand/DeleteArticle.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using MediatR;
3 |
4 | namespace GreenOnSoftware.Application.Articles.DeleteArticleCommand;
5 |
6 | public record DeleteArticle(Guid Id) : IRequest;
7 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Articles/GetArticleByIdQuery/ArticleDto.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Application.Articles.GetArticleByIdQuery;
2 |
3 | public class ArticleDto
4 | {
5 | public Guid Id { get; set; }
6 | public string Title { get; set; }
7 | public string? Description { get; set; }
8 | public string Content { get; set; }
9 | public string AuthorEmail { get; set; }
10 | public string AuthorName { get; set; }
11 | public string? ThumbnailUrl { get; set; }
12 | public string Status { get; set; }
13 | public string Url { get; set; }
14 |
15 | public DateTime CreatedDate { get; set; }
16 | public DateTime ModifiedDate { get; set; }
17 | }
18 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Articles/GetArticleByIdQuery/CommentDto.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Application.Articles.GetArticleByIdQuery;
2 |
3 | public class CommentDto
4 | {
5 | public Guid Id { get; set; }
6 | public DateTime CreatedDate { get; set; }
7 | public DateTime ModifiedDate { get; set; }
8 | public string Content { get; set; }
9 | public string Username { get; set; }
10 | }
11 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Articles/GetArticleByIdQuery/GetArticleById.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using MediatR;
3 |
4 | namespace GreenOnSoftware.Application.Articles.GetArticleByIdQuery;
5 |
6 | public record GetArticleById(Guid Id) : IRequest>;
7 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Articles/GetArticlesQuery/ArticleLookupDto.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Application.Articles.GetArticlesQuery;
2 |
3 | public class ArticleLookupDto
4 | {
5 | public Guid Id { get; set; }
6 | public string Title { get; set; }
7 | public string? Description { get; set; }
8 | public string AuthorEmail { get; set; }
9 | public string AuthorName { get; set; }
10 | public string? ThumbnailUrl { get; set; }
11 | public string Status { get; set; }
12 | public string Url { get; set; }
13 | }
14 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Articles/GetArticlesQuery/GetArticles.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.CQRS;
2 |
3 | namespace GreenOnSoftware.Application.Articles.GetArticlesQuery;
4 |
5 | public record GetArticles(string[]? Status, string? Search, int? ItemsPerPage, int? CurrentPage)
6 | : ISearchQuery;
7 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Articles/RejectArticleCommand/RejectArticle.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using MediatR;
3 |
4 | namespace GreenOnSoftware.Application.Articles.RejectArticleCommand;
5 |
6 | public record RejectArticle(Guid Id) : IRequest;
7 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Articles/SendForApprovalCommand/SendForApproval.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using MediatR;
3 |
4 | namespace GreenOnSoftware.Application.Articles.SendForApprovalCommand;
5 |
6 | public record SendForApproval(Guid Id) : IRequest;
7 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Articles/UpdateArticleCommand/UpdateArticle.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Application.Reviews.AddReviewCommand;
2 | using GreenOnSoftware.Commons.Dtos;
3 | using GreenOnSoftware.Core.Models;
4 | using MediatR;
5 | using Microsoft.AspNetCore.Http;
6 |
7 | namespace GreenOnSoftware.Application.Articles.UpdateArticleCommand;
8 |
9 | public record UpdateArticle(string Title, string? Description, string Content, IFormFile? Thumbnail, string Url) : IRequest
10 | {
11 | internal Guid Id { get; private set; }
12 |
13 | public UpdateArticle BindId(Guid id)
14 | {
15 | Id = id;
16 |
17 | return this;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Articles/UpdateArticleCommand/UpdateArticleValidator.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 |
3 | namespace GreenOnSoftware.Application.Articles.UpdateArticleCommand;
4 |
5 | public class UpdateArticleValidator : AbstractValidator
6 | {
7 | public UpdateArticleValidator()
8 | {
9 | RuleFor(x => x.Title).NotEmpty();
10 | RuleFor(x => x.Content).NotEmpty();
11 |
12 | RuleFor(x => x.Title)
13 | .MaximumLength(200);
14 | RuleFor(x => x.Description)
15 | .MaximumLength(500);
16 | RuleFor(x => x.Url)
17 | .MaximumLength(300);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/DependencyInjection.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Application.Services;
2 | using GreenOnSoftware.Application.Services.Interfaces;
3 | using Microsoft.Extensions.DependencyInjection;
4 |
5 | namespace GreenOnSoftware.Application;
6 |
7 | public static class DependencyInjection
8 | {
9 | public static IServiceCollection AddApplication(this IServiceCollection services)
10 | {
11 | services.AddTransient();
12 | services.AddTransient();
13 | services.AddTransient();
14 |
15 | return services;
16 | }
17 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Ratings/AddArticleRateCommand/AddArticleRate.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using MediatR;
3 |
4 | namespace GreenOnSoftware.Application.Ratings.AddArticleRateCommand;
5 |
6 | public record AddArticleRate(int Value) : IRequest
7 | {
8 | internal Guid ArticleId { get; private set; }
9 |
10 | public AddArticleRate BindArticleId(Guid articleId)
11 | {
12 | ArticleId = articleId;
13 |
14 | return this;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Ratings/AddArticleRateCommand/AddArticleRateValidator.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 | using GreenOnSoftware.Application.Reviews.AddReviewCommand;
3 |
4 | namespace GreenOnSoftware.Application.Ratings.AddArticleRateCommand;
5 |
6 | public class AddArticleRateValidator : AbstractValidator
7 | {
8 | public AddArticleRateValidator()
9 | {
10 | RuleFor(x => x.ArticleId)
11 | .NotEmpty();
12 |
13 | RuleFor(x => x.Value)
14 | .InclusiveBetween(1, 5);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Ratings/GetArticleRatingQuery/GetArticleRatings.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Clock;
2 | using GreenOnSoftware.Commons.Dtos;
3 | using MediatR;
4 |
5 | namespace GreenOnSoftware.Application.Ratings.GetArticleRatingQuery;
6 |
7 | public record GetArticleRatings(Guid Id) : IRequest>;
8 |
9 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Ratings/GetArticleRatingQuery/RatingsDto.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Application.Ratings.GetArticleRatingQuery;
2 |
3 | public class RatingsDto
4 | {
5 | public RatingsDto(IEnumerable> averageRatingByAvatars, double averageRating, string? currentUserRateAvatar, int? currentUserRateValue)
6 | {
7 | AverageRatingByAvatars = averageRatingByAvatars;
8 | AverageRating = averageRating;
9 | CurrentUserRateAvatar = currentUserRateAvatar;
10 | CurrentUserRateValue = currentUserRateValue;
11 | }
12 |
13 | public IEnumerable> AverageRatingByAvatars { get; }
14 | public double AverageRating { get; }
15 | public string? CurrentUserRateAvatar { get; }
16 | public int? CurrentUserRateValue { get; }
17 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Ratings/RemoveArticleRateCommand/RemoveArticleRate.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using MediatR;
3 |
4 | namespace GreenOnSoftware.Application.Ratings.RemoveArticleRateCommand;
5 |
6 | public record RemoveArticleRate(Guid ArticleId) : IRequest;
7 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Ratings/UpdateArticleRate/UpdateArticleRate.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using MediatR;
3 |
4 | namespace GreenOnSoftware.Application.Ratings.UpdateArticleRate;
5 |
6 | public record UpdateArticleRate(int Value) : IRequest
7 | {
8 | internal Guid ArticleId { get; private set; }
9 |
10 | public UpdateArticleRate BindArticleId(Guid articleId)
11 | {
12 | ArticleId = articleId;
13 |
14 | return this;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Ratings/UpdateArticleRate/UpdateArticleRateValidator.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 |
3 | namespace GreenOnSoftware.Application.Ratings.UpdateArticleRate;
4 |
5 | public class UpdateArticleRateValidator : AbstractValidator
6 | {
7 | public UpdateArticleRateValidator()
8 | {
9 | RuleFor(x => x.ArticleId)
10 | .NotEmpty();
11 |
12 | RuleFor(x => x.Value)
13 | .InclusiveBetween(1, 5);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Reviews/AddReviewCommand/AddReview.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using MediatR;
3 |
4 | namespace GreenOnSoftware.Application.Reviews.AddReviewCommand;
5 |
6 | public record AddReview(string Content) : IRequest
7 | {
8 | internal Guid ArticleId { get; private set; }
9 |
10 | public AddReview BindArticleId(Guid articleId)
11 | {
12 | ArticleId = articleId;
13 |
14 | return this;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Reviews/AddReviewCommand/AddReviewValidator.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 |
3 | namespace GreenOnSoftware.Application.Reviews.AddReviewCommand;
4 |
5 | public class AddReviewValidator : AbstractValidator
6 | {
7 | public AddReviewValidator()
8 | {
9 | RuleFor(x => x.Content)
10 | .NotEmpty()
11 | .MaximumLength(4000);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Reviews/DeleteReviewCommand/DeleteReview.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using MediatR;
3 |
4 | namespace GreenOnSoftware.Application.Reviews.DeleteReviewCommand;
5 |
6 | public record DeleteReview(Guid ArticleId, Guid ReviewId) : IRequest;
7 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Reviews/GetAllReviewerReviewsQuery/GetAllReviewerReviews.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Application.Dtos;
2 | using GreenOnSoftware.Commons.Dtos;
3 | using MediatR;
4 |
5 | namespace GreenOnSoftware.Application.Reviews.GetAllReviewerReviewsQuery;
6 |
7 | public record GetAllReviewerReviews() : IRequest>>;
8 |
9 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Reviews/GetReviewByIdQuery/GetReviews.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Application.Dtos;
2 | using GreenOnSoftware.Commons.Dtos;
3 | using MediatR;
4 |
5 | namespace GreenOnSoftware.Application.Reviews.GetReviewByIdQuery;
6 |
7 | public record GetReviewById(Guid ArticleId, Guid ReviewId) : IRequest>;
8 |
9 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Reviews/GetReviewsQuery/GetReviews.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Application.Dtos;
2 | using GreenOnSoftware.Commons.Dtos;
3 | using MediatR;
4 |
5 | namespace GreenOnSoftware.Application.Reviews.GetReviewsQuery;
6 |
7 | public record GetReviews(Guid ArticleId) : IRequest>>;
8 |
9 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Reviews/UpdateReviewCommand/UpdateReview.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using GreenOnSoftware.Core.Models;
3 | using MediatR;
4 |
5 | namespace GreenOnSoftware.Application.Reviews.UpdateReviewCommand;
6 |
7 | public record UpdateReview(string Content) : IRequest
8 | {
9 | internal Guid ArticleId { get; private set; }
10 | internal Guid ReviewId { get; private set; }
11 |
12 | public UpdateReview BindIds(Guid articleId, Guid reviewId)
13 | {
14 | ArticleId = articleId;
15 | ReviewId = reviewId;
16 |
17 | return this;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Reviews/UpdateReviewCommand/UpdateReviewValidator.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 |
3 | namespace GreenOnSoftware.Application.Reviews.UpdateReviewCommand;
4 |
5 | public class UpdateReviewValidator : AbstractValidator
6 | {
7 | public UpdateReviewValidator()
8 | {
9 | RuleFor(x => x.Content)
10 | .NotEmpty()
11 | .MaximumLength(4000);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Snippets/AddSnippetCommand/AddSnippet.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Application.Articles.UpdateArticleCommand;
2 | using GreenOnSoftware.Commons.Dtos;
3 | using MediatR;
4 |
5 | namespace GreenOnSoftware.Application.Snippets.AddSnippetCommand;
6 |
7 | public record AddSnippet(string Name, string Description, IList Frames, string? GifUrl): IRequest
8 | {
9 | public record SnippetFrame(string Code, string? Name, string? Description, SnippetFrame.SnippetFrameAnimation Animation)
10 | {
11 | public record SnippetFrameAnimation(string Type, int DisplayTime)
12 | {
13 |
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Snippets/GetSnippetByIdQuery/GetSnippetById.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using MediatR;
3 |
4 | namespace GreenOnSoftware.Application.Snippets.GetSnippetByIdQuery;
5 |
6 | public record GetSnippetById(Guid Id): IRequest
7 | {
8 |
9 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Snippets/GetSnippetByIdQuery/GetSnippetByIdValidator.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 |
3 | namespace GreenOnSoftware.Application.Snippets.GetSnippetByIdQuery;
4 |
5 | public sealed class GetSnippetByIdValidator: AbstractValidator
6 | {
7 | public GetSnippetByIdValidator()
8 | {
9 | RuleFor(x => x.Id)
10 | .NotEmpty();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Snippets/GetSnippetByIdQuery/SnippetDto.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Application.Snippets.GetSnippetByIdQuery;
2 |
3 | public class SnippetDto
4 | {
5 | public Guid Id { get; set; }
6 |
7 | public string Name { get; set; }
8 |
9 | public string Description { get; set; }
10 |
11 | public IList Frames { get; set; }
12 |
13 | public string GifUrl { get; private set; }
14 |
15 | public class SnippetFrameDto
16 | {
17 | public string Code { get; set; }
18 | public string? Name { get; set; }
19 | public string? Description { get; set; }
20 |
21 | public SnippetFrameAnimationDto Animation { get; set; }
22 |
23 | }
24 |
25 | public class SnippetFrameAnimationDto
26 | {
27 | public string Type { get; set; }
28 | public int DisplayTime { get; set; }
29 | }
30 | }
31 |
32 |
33 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Users/AddToRoleCommand/AddToRole.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using MediatR;
3 |
4 | namespace GreenOnSoftware.Application.Users.AddToRoleCommand;
5 |
6 | public record AddToRole(string Role) : IRequest
7 | {
8 | internal Guid UserId { get; private set; }
9 |
10 | public AddToRole BindId(Guid userId)
11 | {
12 | UserId = userId;
13 |
14 | return this;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Users/AddToRoleCommand/AddToRoleValidator.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 | using GreenOnSoftware.Commons.Consts;
3 |
4 | namespace GreenOnSoftware.Application.Users.AddToRoleCommand;
5 |
6 | public class AddToRoleValidator : AbstractValidator
7 | {
8 | public AddToRoleValidator()
9 | {
10 | RuleFor(x => x.Role)
11 | .Must(x => Role.All.Contains(x))
12 | .WithMessage("Role must have valid value");
13 |
14 | RuleFor(x => x.Role)
15 | .Must(x => x.ToLower() != Role.Admin.ToLower())
16 | .WithMessage("Administrators cannot be change");
17 | }
18 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Users/GetUsersQuery/GetUsers.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.CQRS;
2 |
3 | namespace GreenOnSoftware.Application.Users.GetUsersQuery;
4 |
5 | public record GetUsers(string[]? Roles, string? Search, int? ItemsPerPage, int? CurrentPage)
6 | : ISearchQuery;
7 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Users/GetUsersQuery/GetUsersValidator.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 | using GreenOnSoftware.Commons.Consts;
3 | using Microsoft.EntityFrameworkCore;
4 |
5 | namespace GreenOnSoftware.Application.Users.GetUsersQuery;
6 |
7 | public class GetUsersValidator : AbstractValidator
8 | {
9 | public GetUsersValidator()
10 | {
11 | RuleFor(x => x.Roles)
12 | .Must(x => x is null || x.All(s => Role.All.Contains(s)))
13 | .WithMessage("Roles must have valid values");
14 |
15 | RuleFor(x => x.Search)
16 | .MaximumLength(100);
17 |
18 | RuleFor(x => x.ItemsPerPage)
19 | .LessThanOrEqualTo(50);
20 |
21 | RuleFor(x => x.CurrentPage)
22 | .LessThanOrEqualTo(1000);
23 | }
24 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Users/GetUsersQuery/UserLookupDto.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Application.Users.GetUsersQuery;
2 |
3 | public sealed class UserLookupDto
4 | {
5 | public Guid Id { get; set; }
6 | public string Name { get; set; }
7 | public string Email { get; set; }
8 | public string[] Roles { get; set; }
9 | }
10 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Users/RemoveFromRoleCommand/RemoveFromRole.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using MediatR;
3 |
4 | namespace GreenOnSoftware.Application.Users.RemoveFromRoleCommand;
5 |
6 | public record RemoveFromRole(string Role) : IRequest
7 | {
8 | internal Guid UserId { get; private set; }
9 |
10 | public RemoveFromRole BindId(Guid userId)
11 | {
12 | UserId = userId;
13 |
14 | return this;
15 | }
16 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/Users/RemoveFromRoleCommand/RemoveFromRoleValidator.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 | using GreenOnSoftware.Application.Users.AddToRoleCommand;
3 | using GreenOnSoftware.Commons.Consts;
4 |
5 | namespace GreenOnSoftware.Application.Users.RemoveFromRoleCommand;
6 |
7 | public class RemoveFromRoleValidator : AbstractValidator
8 | {
9 | public RemoveFromRoleValidator()
10 | {
11 | RuleFor(x => x.Role)
12 | .Must(x => Role.All.Contains(x))
13 | .WithMessage("Role must have valid value");
14 |
15 | RuleFor(x => x.Role)
16 | .Must(x => x.ToLower() != Role.Admin.ToLower())
17 | .WithMessage("Administrators cannot be change");
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/_Configuration/ApplicationConfiguration.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Application.Configuration;
2 |
3 | public class ApplicationConfiguration
4 | {
5 | public string ResetPasswordUrl { get; set; }
6 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/_Dtos/ICurrentUserReviewer.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Application.Dtos;
2 |
3 | public interface ICurrentUserReviewer
4 | {
5 | bool IsCurrentUserReviewer { get; set; }
6 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/_Dtos/ReviewDto.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Application.Dtos;
2 |
3 | public class ReviewDto : ICurrentUserReviewer
4 | {
5 | public Guid Id { get; set; }
6 | public DateTime CreatedDate { get; set; }
7 | public DateTime ModifiedDate { get; set; }
8 | public string? ReviewerName { get; set; }
9 | public string Content { get; set; }
10 | public bool IsCurrentUserReviewer { get; set; }
11 | public string? ArticleAuthorName { get; set; }
12 | public string ArticleTitle { get; set; }
13 | }
14 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/_Dtos/ReviewLookupDto.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Application.Dtos;
2 |
3 | public class ReviewLookupDto: ICurrentUserReviewer
4 | {
5 | public Guid Id { get; set; }
6 | public Guid ArticleId { get; set; }
7 | public DateTime CreatedDate { get; set; }
8 | public DateTime ModifiedDate { get; set; }
9 | public string? ReviewerName { get; set; }
10 | public bool IsCurrentUserReviewer { get; set; }
11 | }
12 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/_Dtos/UserReviewLookupDto.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Application.Dtos;
2 |
3 | public class UserReviewLookupDto
4 | {
5 | public Guid Id { get; set; }
6 | public Guid ArticleId { get; set; }
7 | public DateTime CreatedDate { get; set; }
8 | public DateTime ModifiedDate { get; set; }
9 | public string? ArticleAuthorName { get; set; }
10 | public string ArticleTitle { get; set; }
11 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/_Mapper/MappingActions/ReviewDtoAction.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using GreenOnSoftware.Application.Dtos;
3 | using GreenOnSoftware.Commons.Context;
4 | using GreenOnSoftware.Core.Models.Reviews;
5 |
6 | namespace GreenOnSoftware.Application.Mapper.MappingActions;
7 |
8 | internal sealed class CurrentUserReviewerAction : IMappingAction
9 | {
10 | private readonly IContext _context;
11 |
12 | public CurrentUserReviewerAction(IContext context)
13 | {
14 | _context = context;
15 | }
16 |
17 | public void Process(Review source, ICurrentUserReviewer destination, ResolutionContext context)
18 | {
19 | destination.IsCurrentUserReviewer = source.ReviewerId == _context.Identity.Id;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/_Mapper/SnippetsMapperProfile.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using GreenOnSoftware.Application.Snippets.GetSnippetByIdQuery;
3 | using GreenOnSoftware.Core.Models.Snippets;
4 |
5 | namespace GreenOnSoftware.Application.Mapper;
6 |
7 | public class SnippetsMapperProfile : Profile
8 | {
9 | public SnippetsMapperProfile()
10 | {
11 | CreateMap();
12 | CreateMap()
13 | .ForMember(x => x.Animation, opt => opt.MapFrom(mappingFunction: (src, dst) => new SnippetDto.SnippetFrameAnimationDto {
14 | DisplayTime = src.AnimationDisplayTime,
15 | Type = src.AnimationType.ToString()
16 | }));
17 | }
18 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/_Mapper/UsersMapperProfile.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using GreenOnSoftware.Application.Users.GetUsersQuery;
3 | using GreenOnSoftware.Core.Identity;
4 |
5 | namespace GreenOnSoftware.Application.Mapper;
6 |
7 | public class UsersMapperProfile : Profile
8 | {
9 | public UsersMapperProfile()
10 | {
11 | CreateProjection()
12 | .ForMember(dest => dest.Name, o => o.MapFrom(src => src.UserName))
13 | .ForMember(dest => dest.Roles, o => o.MapFrom(src => src.Roles.Select(x=>x.Role.Name).ToArray()));
14 | }
15 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/_Services/Interfaces/IBlobStorageService.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 |
3 | namespace GreenOnSoftware.Application.Services.Interfaces;
4 |
5 | public interface IBlobStorageService
6 | {
7 | Task RemovePictureFromStorageAsync(string path);
8 | Task> UploadPictureAsync(MemoryStream pictureFileStream, string fileExtension);
9 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/_Services/Interfaces/IEmailSenderService.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 |
3 | namespace GreenOnSoftware.Application.Services.Interfaces;
4 |
5 | public interface IEmailSenderService
6 | {
7 | Task SendEmailAsync(string recipient, string subject, string textContent);
8 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/_Services/Interfaces/IEmailService.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 |
3 | namespace GreenOnSoftware.Application.Services.Interfaces;
4 |
5 | public interface IEmailService
6 | {
7 | string GetResetPasswordUrl(string token, Guid userId);
8 | Task SendResetPasswordEmailAsync(string email, string username, Guid userId, string token);
9 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/_Services/Interfaces/IRatingsSessionService.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Application.Services.Interfaces;
2 |
3 | public interface IRatingsSessionService
4 | {
5 | void SetAvatarName(string avatarName);
6 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/_Services/Interfaces/IThumbnailService.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using Microsoft.AspNetCore.Http;
3 |
4 | namespace GreenOnSoftware.Application.Services.Interfaces;
5 |
6 | public interface IThumbnailService
7 | {
8 | Task> UploadPicture(IFormFile pictureFile);
9 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Application/_Services/RatingsSessionService.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Application.Services.Interfaces;
2 | using Microsoft.AspNetCore.Http;
3 |
4 | namespace GreenOnSoftware.Application.Services;
5 |
6 | public class RatingsSessionService : IRatingsSessionService
7 | {
8 | private const string AvatarNameKey = "avatarName";
9 |
10 | private readonly IHttpContextAccessor _httpContextAccessor;
11 |
12 | public RatingsSessionService(IHttpContextAccessor httpContextAccessor)
13 | {
14 | _httpContextAccessor = httpContextAccessor;
15 | }
16 |
17 | public void SetAvatarName(string avatarName)
18 | {
19 | _httpContextAccessor.HttpContext!.Session.SetString(AvatarNameKey, avatarName);
20 | }
21 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Commons/CQRS/ISearchQuery.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Commons.Dtos;
2 | using MediatR;
3 |
4 | namespace GreenOnSoftware.Commons.CQRS;
5 |
6 | public interface ISearchQuery: IRequest>
7 | {
8 | string? Search { get; init; }
9 | int? ItemsPerPage { get; init; }
10 | int? CurrentPage { get; init; }
11 | }
12 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Commons/Clock/Clock.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Commons.Clock;
2 |
3 | public class Clock : IClock
4 | {
5 | public DateTime UtcNow => DateTime.UtcNow;
6 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Commons/Clock/IClock.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Commons.Clock
2 | {
3 | public interface IClock
4 | {
5 | DateTime UtcNow { get; }
6 | }
7 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Commons/Consts/Role.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Commons.Consts;
2 |
3 | public static class Role
4 | {
5 | public readonly static string[] All = new[] { Admin, ContentEditor, GeneralUser };
6 |
7 | public const string Admin = "Admin";
8 | public const string ContentEditor = "ContentEditor";
9 | public const string GeneralUser = "GeneralUser";
10 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Commons/Context/IContext.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Commons.Context;
2 |
3 | public interface IContext
4 | {
5 | IIdentityContext Identity { get; }
6 | }
7 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Commons/Context/IContextAccessor.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Commons.Context;
2 |
3 | public interface IContextAccessor
4 | {
5 | IContext? Context { get; set; }
6 | }
7 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Commons/Context/IIdentityContext.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Commons.Context;
2 |
3 | public interface IIdentityContext
4 | {
5 | Guid Id { get; }
6 | string? Name { get; }
7 | bool IsAdmin { get; }
8 | bool IsContentEditor { get; }
9 | bool IsGeneralUser { get; }
10 | bool IsAuthenticated { get; }
11 | string? AvatarName { get; }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Commons/Dtos/ErrorMessage.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Commons.Dtos;
2 |
3 | public class ErrorMessage
4 | {
5 | public ErrorMessage(string key, string message)
6 | {
7 | Key = key;
8 | Message = message;
9 | }
10 |
11 | public string Key { get; set; }
12 | public string Message { get; set; }
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Commons/Dtos/PagedResult.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Commons.Dtos;
2 |
3 | public class PagedResult : Result>
4 | {
5 | public int ItemsPerPage { get; private set; }
6 | public int TotalPages { get; private set; }
7 | public int CurrentPage { get; private set; }
8 | public int CurrentPageItemsNumber { get; private set; }
9 |
10 | public void SetData(IEnumerable items, int itemsPerPage, int currentPage, int currentPageItemsNumber, int totalPages)
11 | {
12 | SetData(items);
13 | ItemsPerPage = itemsPerPage;
14 | CurrentPage = currentPage;
15 | TotalPages = totalPages;
16 | CurrentPageItemsNumber = currentPageItemsNumber;
17 | }
18 | }
19 |
20 |
21 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Commons/Extensions/EnumExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 |
3 | namespace GreenOnSoftware.Commons.Extensions;
4 |
5 | public static class EnumExtensions
6 | {
7 | public static string Description(Enum item)
8 | {
9 | var enumValue = item.ToString();
10 | var field = item.GetType().GetField(enumValue);
11 | var attribute = ((DescriptionAttribute[])field!.GetCustomAttributes(typeof(DescriptionAttribute), false)).FirstOrDefault();
12 |
13 | return attribute?.Description ?? enumValue;
14 | }
15 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Commons/Extensions/IdentityResultExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Identity;
2 |
3 | namespace GreenOnSoftware.Commons.Extensions;
4 |
5 | public static class IdentityResultExtensions
6 | {
7 | public static IEnumerable GetErrors(this IdentityResult identityResult)
8 | {
9 | return identityResult.Errors.Select(x => x.Description);
10 | }
11 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Commons/Extensions/QueryableExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Linq.Expressions;
2 |
3 | namespace GreenOnSoftware.Commons.Extensions;
4 |
5 | public static class QueryableExtensions
6 | {
7 | public static IQueryable Where(this IQueryable entities, Func condition, Expression> predicate)
8 | where T: class
9 | {
10 | return condition() ? entities.Where(predicate) : entities;
11 | }
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Commons/Extensions/StringExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Commons.Extensions;
2 |
3 | public static class StringExtensions
4 | {
5 | public static T ToEnum(this string description) where T : Enum
6 | {
7 | return (T)Enum.Parse(typeof(T), description, ignoreCase: true);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Core/Enums/SnippetFrameAnimationType.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Core.Enums;
2 |
3 | public enum SnippetFrameAnimationType
4 | {
5 | SlideRight,
6 | SlideLeft,
7 | Opacity
8 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Core/Enums/Status.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Core.Enums;
2 |
3 | public enum Status
4 | {
5 | Draft = 0,
6 | WaitingForApproval = 1,
7 | NeedWork = 2,
8 | Accepted = 3
9 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Core/GreenOnSoftware.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Core/Identity/User.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Identity;
2 |
3 | namespace GreenOnSoftware.Core.Identity;
4 |
5 | public class User : IdentityUser
6 | {
7 | public virtual ICollection Roles { get; set; }
8 | public virtual ICollection> Tokens { get; set; }
9 | }
10 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Core/Identity/UserRole.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Identity;
2 |
3 | namespace GreenOnSoftware.Core.Identity;
4 |
5 | public class UserRole : IdentityUserRole
6 | {
7 | public virtual IdentityRole Role { get; set; }
8 | }
9 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Core/Models/Snippets/Snippet.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 |
3 | namespace GreenOnSoftware.Core.Models.Snippets;
4 |
5 | public class Snippet : Entity
6 | {
7 | private Snippet()
8 | {
9 |
10 | }
11 |
12 | public Snippet(string name, string description, IList frames, string? gifUrl)
13 | {
14 | Id = Guid.NewGuid();
15 | Name = name;
16 | Description = description;
17 | Frames = frames;
18 | GifUrl = gifUrl;
19 | }
20 |
21 | public string Name { get; private set; }
22 |
23 | public string Description { get; private set; }
24 |
25 | public IList Frames { get; private set; }
26 |
27 | public string? GifUrl { get; private set; }
28 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Core/Models/Snippets/SnippetFrame.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Core.Enums;
2 | using Newtonsoft.Json;
3 |
4 | namespace GreenOnSoftware.Core.Models.Snippets;
5 |
6 | public class SnippetFrame
7 | {
8 | private SnippetFrame()
9 | {
10 |
11 | }
12 |
13 | public SnippetFrame(string code, string? name, string? description, SnippetFrameAnimationType animationType, int animationDisplayTime)
14 | {
15 | Code = code;
16 | Name = name;
17 | Description = description;
18 | AnimationType = animationType;
19 | AnimationDisplayTime = animationDisplayTime;
20 | }
21 |
22 | public string Code { get; private set; }
23 | public string? Name { get; private set; }
24 | public string? Description { get; private set; }
25 | public SnippetFrameAnimationType AnimationType { get; private set; }
26 | public int AnimationDisplayTime { get; private set; }
27 | }
28 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Core/Models/_Entity.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Identity;
2 |
3 | namespace GreenOnSoftware.Core.Models;
4 |
5 | public abstract class Entity
6 | {
7 | public Guid Id { get; protected set; }
8 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.DataAccess/Configuration/CommentConfiguration.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Core.Models;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
4 |
5 | namespace GreenOnSoftware.DataAccess.Configuration;
6 |
7 | public class CommentConfiguration : IEntityTypeConfiguration
8 | {
9 | public void Configure(EntityTypeBuilder builder)
10 | {
11 | builder.HasKey(x => x.Id);
12 |
13 | builder.Property(x => x.Content)
14 | .IsRequired();
15 |
16 | builder.HasOne(x => x.Author)
17 | .WithMany()
18 | .HasForeignKey(x => x.AuthorId)
19 | .OnDelete(DeleteBehavior.SetNull);
20 |
21 | builder.HasOne(x => x.Article)
22 | .WithMany(x=>x.Comments)
23 | .HasForeignKey(x => x.ArticleId)
24 | .OnDelete(DeleteBehavior.SetNull);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.DataAccess/Configuration/UserConfiguration.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Core.Identity;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
4 |
5 | namespace GreenOnSoftware.DataAccess.Configuration;
6 |
7 | public class UserConfiguration : IEntityTypeConfiguration
8 | {
9 | public void Configure(EntityTypeBuilder builder)
10 | {
11 | builder
12 | .HasMany(e => e.Roles)
13 | .WithOne()
14 | .HasForeignKey(e => e.UserId)
15 | .IsRequired()
16 | .OnDelete(DeleteBehavior.Cascade);
17 |
18 | builder
19 | .HasMany(x => x.Tokens)
20 | .WithOne()
21 | .HasForeignKey(x => x.UserId)
22 | .IsRequired()
23 | .OnDelete(DeleteBehavior.Cascade);
24 |
25 | builder
26 | .ToTable("Users");
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.DataAccess/Configuration/UserRoleConfiguration.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Core.Identity;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
4 |
5 | namespace GreenOnSoftware.DataAccess.Configuration;
6 |
7 | public class UserRoleConfiguration : IEntityTypeConfiguration
8 | {
9 | public void Configure(EntityTypeBuilder builder)
10 | {
11 | builder
12 | .HasOne(e => e.Role)
13 | .WithMany()
14 | .HasForeignKey(e => e.RoleId)
15 | .IsRequired()
16 | .OnDelete(DeleteBehavior.Cascade);
17 |
18 | builder
19 | .ToTable("UserRoles");
20 | }
21 |
22 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.DataAccess/Converters/SnippetFramesValueConverter.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Core.Models.Snippets;
2 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
3 | using Newtonsoft.Json;
4 |
5 | namespace GreenOnSoftware.DataAccess.Converters;
6 |
7 | internal class SnippetFramesValueConverter : ValueConverter, string>
8 | {
9 | private static JsonSerializerSettings _jsonSerializerSettings = new JsonSerializerSettings {
10 | TypeNameHandling = TypeNameHandling.Auto,
11 | NullValueHandling = NullValueHandling.Ignore,
12 | };
13 |
14 | public SnippetFramesValueConverter()
15 | : base(frames => JsonConvert.SerializeObject(frames, _jsonSerializerSettings),
16 | json => JsonConvert.DeserializeObject>(json, _jsonSerializerSettings) ?? new List(), null)
17 | {
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Infrastructure/BlobStorage/BlobStorageConfiguration.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Infrastructure.BlobStorage;
2 |
3 | public class BlobStorageConfiguration
4 | {
5 | public string Container { get; set; }
6 | public string ConnectionString { get; set; }
7 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Infrastructure/DependencyInjection.cs:
--------------------------------------------------------------------------------
1 | using GreenOnSoftware.Application.Services.Interfaces;
2 | using GreenOnSoftware.Infrastructure.BlobStorage;
3 | using GreenOnSoftware.Infrastructure.SendGrid;
4 | using Microsoft.Extensions.DependencyInjection;
5 |
6 | namespace GreenOnSoftware.Infrastructure;
7 |
8 | public static class DependencyInjection
9 | {
10 | public static IServiceCollection AddInfrastructure(this IServiceCollection services)
11 | {
12 | services.AddTransient();
13 | services.AddTransient();
14 |
15 | return services;
16 | }
17 | }
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Infrastructure/GreenOnSoftware.Infrastructure.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Infrastructure/Security/SecurityConfiguration.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Infrastructure.Security;
2 |
3 | public class SecurityConfiguration
4 | {
5 | public string AdminNames { get; set; }
6 | public string AdminPassword { get; set; }
7 | }
8 |
--------------------------------------------------------------------------------
/dotnet/GreenOnSoftware/GreenOnSoftware.Infrastructure/SendGrid/SendGridConfiguration.cs:
--------------------------------------------------------------------------------
1 | namespace GreenOnSoftware.Infrastructure.SendGrid;
2 |
3 | public class SendGridConfiguration
4 | {
5 | public string SenderName { get; set; }
6 | public string SenderEmailAddress { get; set; }
7 | public string ApiKey { get; set; }
8 | }
9 |
--------------------------------------------------------------------------------
/system/.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 |
--------------------------------------------------------------------------------
/system/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/system/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "ignorePatterns": ["**/*"],
4 | "plugins": ["@nrwl/nx"],
5 | "overrides": [
6 | {
7 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
8 | "rules": {
9 | "@nrwl/nx/enforce-module-boundaries": [
10 | "error",
11 | {
12 | "enforceBuildableLibDependency": true,
13 | "allow": [],
14 | "depConstraints": [
15 | {
16 | "sourceTag": "*",
17 | "onlyDependOnLibsWithTags": ["*"]
18 | }
19 | ]
20 | }
21 | ]
22 | }
23 | },
24 | {
25 | "files": ["*.ts", "*.tsx"],
26 | "extends": ["plugin:@nrwl/nx/typescript"],
27 | "rules": {}
28 | },
29 | {
30 | "files": ["*.js", "*.jsx"],
31 | "extends": ["plugin:@nrwl/nx/javascript"],
32 | "rules": {}
33 | }
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/system/.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 | # Next.js
42 | .next
43 |
44 | # Environment
45 | *.env
--------------------------------------------------------------------------------
/system/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | . "$(dirname -- "$0")/_/husky.sh"
4 |
5 | cd system
6 |
7 | echo "Runnig linting and fix..."
8 |
9 | npm run test:checkAll
10 | npm run e2e:checkAll
11 | npm run lint:fixAll
12 | npm run format:fixAll
13 | git add .
14 |
--------------------------------------------------------------------------------
/system/.prettierignore:
--------------------------------------------------------------------------------
1 | # Add files here to ignore them from prettier formatting
2 |
3 | /dist
4 | /coverage
5 |
--------------------------------------------------------------------------------
/system/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true
3 | }
4 |
--------------------------------------------------------------------------------
/system/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "esbenp.prettier-vscode",
4 | "nrwl.angular-console",
5 | "firsttris.vscode-jest-runner",
6 | "dbaeumer.vscode-eslint"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/system/README.md:
--------------------------------------------------------------------------------
1 | # System
2 |
3 |
4 |
5 | ✨ **This workspace has been generated by [Nx, a Smart, fast and extensible build system.](https://nx.dev)** ✨
6 |
7 | ## Development server
8 |
9 | Run `nx serve blog` for a dev server. Navigate to http://localhost:4200/. The app will automatically reload if you change any of the source files.
10 |
11 | ## Understand this workspace
12 |
13 | Run `nx graph` to see a diagram of the dependencies of the projects.
14 |
15 | ## Remote caching
16 |
17 | Run `npx nx connect-to-nx-cloud` to enable [remote caching](https://nx.app) and make CI faster.
18 |
19 | ## Further help
20 |
21 | Visit the [Nx Documentation](https://nx.dev) to learn more.
22 |
--------------------------------------------------------------------------------
/system/apps/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polubis/Dream-stack-for-React-dev/ecd07fa7b038fbd76351bb2d24c1f191995ab614/system/apps/.gitkeep
--------------------------------------------------------------------------------
/system/apps/blog-creator-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 | }
11 |
--------------------------------------------------------------------------------
/system/apps/blog-creator-e2e/cypress.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'cypress';
2 | import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
3 |
4 | export default defineConfig({
5 | e2e: nxE2EPreset(__dirname),
6 | });
7 |
--------------------------------------------------------------------------------
/system/apps/blog-creator-e2e/src/e2e/app.cy.ts:
--------------------------------------------------------------------------------
1 | describe('blog-creator', () => {
2 | beforeEach(() => cy.visit('/'));
3 |
4 | it('should display welcome message', () => {
5 | cy.get('h1').contains('Headline1');
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/system/apps/blog-creator-e2e/src/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io"
4 | }
5 |
--------------------------------------------------------------------------------
/system/apps/blog-creator-e2e/src/support/app.po.ts:
--------------------------------------------------------------------------------
1 | export const getGreeting = () => cy.get('h1');
2 |
--------------------------------------------------------------------------------
/system/apps/blog-creator-e2e/src/support/e2e.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 |
--------------------------------------------------------------------------------
/system/apps/blog-creator-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", "cypress.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/system/apps/blog-creator/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@nrwl/react/babel",
5 | {
6 | "runtime": "automatic"
7 | }
8 | ]
9 | ],
10 | "plugins": [["styled-components", { "pure": true, "ssr": true }]]
11 | }
12 |
--------------------------------------------------------------------------------
/system/apps/blog-creator/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["plugin:@nrwl/nx/react", "../../.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 |
--------------------------------------------------------------------------------
/system/apps/blog-creator/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'blog-creator',
4 | preset: '../../jest.preset.js',
5 | transform: {
6 | '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest',
7 | '^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nrwl/react/babel'] }],
8 | },
9 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
10 | coverageDirectory: '../../coverage/apps/blog-creator',
11 | };
12 |
--------------------------------------------------------------------------------
/system/apps/blog-creator/src/app/app.spec.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 |
3 | import { BrowserRouter } from 'react-router-dom';
4 |
5 | import { App } from './app';
6 |
7 | describe('App', () => {
8 | it('should render successfully', () => {
9 | const { baseElement } = render(
10 |
11 |
12 |
13 | );
14 | expect(baseElement).toBeTruthy();
15 | });
16 |
17 | it('should have a greeting as the title', () => {
18 | const { getByText } = render(
19 |
20 |
21 |
22 | );
23 | expect(getByText(/Headline1/gi)).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/system/apps/blog-creator/src/app/app.tsx:
--------------------------------------------------------------------------------
1 | import { Font } from '@system/figa-ui';
2 |
3 | const App = () => {
4 | return Headline1;
5 | };
6 |
7 | export { App };
8 |
--------------------------------------------------------------------------------
/system/apps/blog-creator/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polubis/Dream-stack-for-React-dev/ecd07fa7b038fbd76351bb2d24c1f191995ab614/system/apps/blog-creator/src/assets/.gitkeep
--------------------------------------------------------------------------------
/system/apps/blog-creator/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true,
3 | };
4 |
--------------------------------------------------------------------------------
/system/apps/blog-creator/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // When building for production, this file is replaced with `environment.prod.ts`.
3 |
4 | export const environment = {
5 | production: false,
6 | };
7 |
--------------------------------------------------------------------------------
/system/apps/blog-creator/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polubis/Dream-stack-for-React-dev/ecd07fa7b038fbd76351bb2d24c1f191995ab614/system/apps/blog-creator/src/favicon.ico
--------------------------------------------------------------------------------
/system/apps/blog-creator/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BlogCreator
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/system/apps/blog-creator/src/main.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import * as ReactDOM from 'react-dom/client';
3 | import { BrowserRouter } from 'react-router-dom';
4 |
5 | import { App } from './app/app';
6 | import { Sandbox, ThemeProvider } from '@system/figa-ui';
7 |
8 | const root = ReactDOM.createRoot(
9 | document.getElementById('root') as HTMLElement
10 | );
11 |
12 | root.render(
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | );
23 |
--------------------------------------------------------------------------------
/system/apps/blog-creator/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "types": ["node"]
6 | },
7 | "files": [
8 | "../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
9 | "../../node_modules/@nrwl/react/typings/image.d.ts"
10 | ],
11 | "exclude": [
12 | "jest.config.ts",
13 | "src/**/*.spec.ts",
14 | "src/**/*.test.ts",
15 | "src/**/*.spec.tsx",
16 | "src/**/*.test.tsx",
17 | "src/**/*.spec.js",
18 | "src/**/*.test.js",
19 | "src/**/*.spec.jsx",
20 | "src/**/*.test.jsx"
21 | ],
22 | "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"]
23 | }
24 |
--------------------------------------------------------------------------------
/system/apps/blog-creator/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsx": "react-jsx",
4 | "allowJs": false,
5 | "esModuleInterop": false,
6 | "allowSyntheticDefaultImports": true,
7 | "strict": true
8 | },
9 | "files": [],
10 | "include": [],
11 | "references": [
12 | {
13 | "path": "./tsconfig.app.json"
14 | },
15 | {
16 | "path": "./tsconfig.spec.json"
17 | }
18 | ],
19 | "extends": "../../tsconfig.base.json"
20 | }
21 |
--------------------------------------------------------------------------------
/system/apps/blog-creator/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": [
9 | "jest.config.ts",
10 | "src/**/*.test.ts",
11 | "src/**/*.spec.ts",
12 | "src/**/*.test.tsx",
13 | "src/**/*.spec.tsx",
14 | "src/**/*.test.js",
15 | "src/**/*.spec.js",
16 | "src/**/*.test.jsx",
17 | "src/**/*.spec.jsx",
18 | "src/**/*.d.ts"
19 | ],
20 | "files": [
21 | "../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
22 | "../../node_modules/@nrwl/react/typings/image.d.ts"
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/system/apps/blog-creator/webpack.config.js:
--------------------------------------------------------------------------------
1 | const { composePlugins, withNx } = require('@nrwl/webpack');
2 | const { withReact } = require('@nrwl/react');
3 | const CopyWebpackPlugin = require('copy-webpack-plugin');
4 | const path = require('path');
5 |
6 | const copyFonts = (config) => {
7 | config.plugins.push(
8 | new CopyWebpackPlugin({
9 | patterns: [
10 | {
11 | from: path.resolve(__dirname, '../../assets/fonts'),
12 | to: 'fonts',
13 | },
14 | ],
15 | })
16 | );
17 | };
18 |
19 | // Nx plugins for webpack.
20 | module.exports = composePlugins(withNx(), withReact(), (config) => {
21 | // Update the webpack config as needed here.
22 | // e.g. `config.plugins.push(new MyPlugin())`
23 | copyFonts(config);
24 |
25 | return config;
26 | });
27 |
--------------------------------------------------------------------------------
/system/apps/blog-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 |
--------------------------------------------------------------------------------
/system/apps/blog-e2e/cypress.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'cypress';
2 | import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
3 |
4 | export default defineConfig({
5 | e2e: nxE2EPreset(__dirname),
6 | });
7 |
--------------------------------------------------------------------------------
/system/apps/blog-e2e/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "blog-e2e",
3 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "apps/blog-e2e/src",
5 | "projectType": "application",
6 | "targets": {
7 | "e2e": {
8 | "executor": "@nrwl/cypress:cypress",
9 | "options": {
10 | "cypressConfig": "apps/blog-e2e/cypress.config.ts",
11 | "devServerTarget": "blog:serve:development",
12 | "testingType": "e2e"
13 | },
14 | "configurations": {
15 | "production": {
16 | "devServerTarget": "blog:serve:production"
17 | }
18 | }
19 | },
20 | "lint": {
21 | "executor": "@nrwl/linter:eslint",
22 | "outputs": ["{options.outputFile}"],
23 | "options": {
24 | "lintFilePatterns": ["apps/blog-e2e/**/*.{js,ts}"]
25 | }
26 | }
27 | },
28 | "tags": [],
29 | "implicitDependencies": ["blog"]
30 | }
31 |
--------------------------------------------------------------------------------
/system/apps/blog-e2e/src/e2e/app.cy.ts:
--------------------------------------------------------------------------------
1 | describe('blog', () => {
2 | beforeEach(() => {
3 | cy.visit('/');
4 | });
5 |
6 | it('should display welcome message', () => {
7 | cy.get('h5').contains('articles');
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/system/apps/blog-e2e/src/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io"
4 | }
5 |
--------------------------------------------------------------------------------
/system/apps/blog-e2e/src/support/app.po.ts:
--------------------------------------------------------------------------------
1 | export const getGreeting = () => cy.get('h1');
2 |
--------------------------------------------------------------------------------
/system/apps/blog-e2e/src/support/e2e.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 |
--------------------------------------------------------------------------------
/system/apps/blog-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", "cypress.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/system/apps/blog/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "plugin:@nrwl/nx/react-typescript",
4 | "next",
5 | "next/core-web-vitals",
6 | "../../.eslintrc.json"
7 | ],
8 | "ignorePatterns": ["!**/*", ".next/**/*"],
9 | "overrides": [
10 | {
11 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
12 | "rules": {
13 | "@next/next/no-html-link-for-pages": ["error", "apps/blog/pages"]
14 | }
15 | },
16 | {
17 | "files": ["*.ts", "*.tsx"],
18 | "rules": {}
19 | },
20 | {
21 | "files": ["*.js", "*.jsx"],
22 | "rules": {}
23 | }
24 | ],
25 | "rules": {
26 | "@next/next/no-html-link-for-pages": "off"
27 | },
28 | "env": {
29 | "jest": true
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/system/apps/blog/components/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './PageWrapper';
2 | export * from './main-layout';
3 | export * from './link';
4 |
--------------------------------------------------------------------------------
/system/apps/blog/components/link/defs.ts:
--------------------------------------------------------------------------------
1 | import type { ReactNode } from 'react';
2 |
3 | interface LinkProps {
4 | href: string;
5 | children: ReactNode;
6 | }
7 |
8 | export type { LinkProps };
9 |
--------------------------------------------------------------------------------
/system/apps/blog/components/link/index.ts:
--------------------------------------------------------------------------------
1 | export * from './defs';
2 | export * from './link';
3 |
--------------------------------------------------------------------------------
/system/apps/blog/components/link/link.tsx:
--------------------------------------------------------------------------------
1 | import NextLink from 'next/link';
2 | import type { LinkProps } from './defs';
3 |
4 | const Link = ({ href, children }: LinkProps) => {
5 | return {children};
6 | };
7 |
8 | export { Link };
9 |
--------------------------------------------------------------------------------
/system/apps/blog/components/main-layout/defs.ts:
--------------------------------------------------------------------------------
1 | import type { ReactNode } from 'react';
2 |
3 | interface MainLayoutProps {
4 | children: ReactNode;
5 | }
6 |
7 | export type { MainLayoutProps };
8 |
--------------------------------------------------------------------------------
/system/apps/blog/components/main-layout/index.ts:
--------------------------------------------------------------------------------
1 | export * from './main-layout';
2 | export * from './defs';
3 |
--------------------------------------------------------------------------------
/system/apps/blog/consts/index.ts:
--------------------------------------------------------------------------------
1 | const GREEN_ON_SOFTWARE_COMPANY = 'https://greenonsoftware.com/';
2 | const GREEN_ON_SOFTWARE_LINKEDIN =
3 | 'https://www.linkedin.com/company/greenon-software/';
4 | const GREEN_ON_SOFTWARE_AUTHOR =
5 | 'https://www.linkedin.com/in/adrian-po%C5%82ubi%C5%84ski-281ab2172/';
6 | const GREEN_ON_SOFTWARE_DISCORD = 'https://discord.gg/PxXQayT3x3';
7 |
8 | export {
9 | GREEN_ON_SOFTWARE_COMPANY,
10 | GREEN_ON_SOFTWARE_AUTHOR,
11 | GREEN_ON_SOFTWARE_LINKEDIN,
12 | GREEN_ON_SOFTWARE_DISCORD,
13 | };
14 |
--------------------------------------------------------------------------------
/system/apps/blog/content/pl/articles/use-ref-hook.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: 'Siema'
3 | layout: 'post'
4 | ---
5 |
6 | Czesc
7 |
8 | - Raz
9 | - Dwa
10 | - Trzy
11 |
12 |
--------------------------------------------------------------------------------
/system/apps/blog/dk/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lang';
2 | export * from './use-lang';
3 |
--------------------------------------------------------------------------------
/system/apps/blog/dk/lang.ts:
--------------------------------------------------------------------------------
1 | import type { Lang } from '../models';
2 |
3 | const isLang = (param: string): param is Lang => {
4 | return param === ('en' as Lang) || param === ('pl' as Lang);
5 | };
6 |
7 | const getLang = (pathname: string): Lang => {
8 | const lang = pathname.split('/')[1];
9 |
10 | return isLang(lang) ? lang : 'en';
11 | };
12 |
13 | export { isLang, getLang };
14 |
--------------------------------------------------------------------------------
/system/apps/blog/dk/tests/lang.test.ts:
--------------------------------------------------------------------------------
1 | import type { Lang } from '../../models';
2 | import { getLang } from '../lang';
3 |
4 | describe('Supported language is determined when: ', () => {
5 | it('returns default english language if not detected in pathname', () => {
6 | expect(getLang('/')).toBe('en' as Lang);
7 | });
8 |
9 | it('takes language from pathname', () => {
10 | expect(getLang('/pl/articles/')).toBe('pl' as Lang);
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/system/apps/blog/dk/tests/use-lang.test.ts:
--------------------------------------------------------------------------------
1 | import { renderHook } from '@testing-library/react';
2 | import { useLang } from '../use-lang';
3 | import type { Lang } from '../../models';
4 | import { getLang } from '../lang';
5 | import { useRouter } from 'next/router';
6 |
7 | jest.mock('../lang', () => ({
8 | getLang: jest.fn(),
9 | }));
10 | jest.mock('next/router', () => ({
11 | useRouter: jest.fn(),
12 | }));
13 |
14 | describe('Language is detected when: ', () => {
15 | it('takes language from next routing system pathname', () => {
16 | (getLang as jest.Mock).mockReturnValueOnce('en' as Lang);
17 | (useRouter as jest.Mock).mockReturnValueOnce({
18 | pathname: '/en/something/',
19 | });
20 |
21 | const { result } = renderHook(() => useLang());
22 |
23 | expect(result.current).toBe('en' as Lang);
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/system/apps/blog/dk/use-lang.ts:
--------------------------------------------------------------------------------
1 | import { useRouter } from 'next/router';
2 | import { useMemo } from 'react';
3 | import { getLang } from './lang';
4 |
5 | const useLang = () => {
6 | const router = useRouter();
7 |
8 | return useMemo(() => getLang(router.pathname), [router]);
9 | };
10 |
11 | export { useLang };
12 |
--------------------------------------------------------------------------------
/system/apps/blog/index.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-explicit-any */
2 | declare module '*.svg' {
3 | const content: any;
4 | export const ReactComponent: any;
5 | export default content;
6 | }
7 |
--------------------------------------------------------------------------------
/system/apps/blog/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | export default {
4 | displayName: 'blog',
5 | preset: '../../jest.preset.js',
6 | transform: {
7 | '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest',
8 | '^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nrwl/next/babel'] }],
9 | },
10 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
11 | coverageDirectory: '../../coverage/apps/blog',
12 | };
13 |
--------------------------------------------------------------------------------
/system/apps/blog/models/index.ts:
--------------------------------------------------------------------------------
1 | import type { MDXRemoteSerializeResult } from 'next-mdx-remote';
2 |
3 | interface ArticlePageProps {
4 | id: string;
5 | source: MDXRemoteSerializeResult;
6 | }
7 |
8 | interface ArticlePageParams {
9 | params: {
10 | id: string;
11 | };
12 | }
13 |
14 | type Lang = 'pl' | 'en';
15 |
16 | export type { ArticlePageParams, ArticlePageProps, Lang };
17 |
--------------------------------------------------------------------------------
/system/apps/blog/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 |
--------------------------------------------------------------------------------
/system/apps/blog/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import { ThemeProvider } from '@system/figa-ui';
2 | import { AppProps } from 'next/app';
3 | import Head from 'next/head';
4 |
5 | const App = ({ Component, pageProps }: AppProps) => {
6 | return (
7 | <>
8 |
9 | Welcome to blog!
10 |
11 |
12 |
13 |
14 | >
15 | );
16 | };
17 |
18 | export default App;
19 |
--------------------------------------------------------------------------------
/system/apps/blog/pages/en/articles/index.tsx:
--------------------------------------------------------------------------------
1 | import { Font } from '@system/figa-ui';
2 |
3 | const ArticlesPage = () => {
4 | return ArticlesPage;
5 | };
6 |
7 | export default ArticlesPage;
8 |
--------------------------------------------------------------------------------
/system/apps/blog/pages/en/index.tsx:
--------------------------------------------------------------------------------
1 | import { Font } from '@system/figa-ui';
2 |
3 | const Index = () => {
4 | return England;
5 | };
6 |
7 | export default Index;
8 |
--------------------------------------------------------------------------------
/system/apps/blog/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import type { GetStaticProps } from 'next';
2 | import { HomeView, type HomeViewProps } from '../views/home';
3 | import { getArticles } from '@system/blog-api';
4 |
5 | export const getStaticProps: GetStaticProps = async () => {
6 | const response = await getArticles();
7 |
8 | return {
9 | props: {
10 | articles: response.data,
11 | },
12 | };
13 | };
14 |
15 | const HomePage = ({ articles }: HomeViewProps) => {
16 | return ;
17 | };
18 |
19 | export default HomePage;
20 |
--------------------------------------------------------------------------------
/system/apps/blog/pages/pl/articles/index.tsx:
--------------------------------------------------------------------------------
1 | import { Font } from '@system/figa-ui';
2 |
3 | const ArticlesPage = () => {
4 | return ArticlesPage;
5 | };
6 |
7 | export default ArticlesPage;
8 |
--------------------------------------------------------------------------------
/system/apps/blog/pages/pl/index.tsx:
--------------------------------------------------------------------------------
1 | import { Font } from '@system/figa-ui';
2 |
3 | const Index = () => {
4 | return Polska;
5 | };
6 |
7 | export default Index;
8 |
--------------------------------------------------------------------------------
/system/apps/blog/public/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polubis/Dream-stack-for-React-dev/ecd07fa7b038fbd76351bb2d24c1f191995ab614/system/apps/blog/public/.gitkeep
--------------------------------------------------------------------------------
/system/apps/blog/public/fonts/LexendBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polubis/Dream-stack-for-React-dev/ecd07fa7b038fbd76351bb2d24c1f191995ab614/system/apps/blog/public/fonts/LexendBold.ttf
--------------------------------------------------------------------------------
/system/apps/blog/public/fonts/LexendLight.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polubis/Dream-stack-for-React-dev/ecd07fa7b038fbd76351bb2d24c1f191995ab614/system/apps/blog/public/fonts/LexendLight.ttf
--------------------------------------------------------------------------------
/system/apps/blog/public/fonts/LexendMedium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polubis/Dream-stack-for-React-dev/ecd07fa7b038fbd76351bb2d24c1f191995ab614/system/apps/blog/public/fonts/LexendMedium.ttf
--------------------------------------------------------------------------------
/system/apps/blog/public/fonts/LexendRegular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polubis/Dream-stack-for-React-dev/ecd07fa7b038fbd76351bb2d24c1f191995ab614/system/apps/blog/public/fonts/LexendRegular.ttf
--------------------------------------------------------------------------------
/system/apps/blog/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "jsx": "preserve",
5 | "allowJs": true,
6 | "esModuleInterop": true,
7 | "allowSyntheticDefaultImports": true,
8 | "strict": false,
9 | "forceConsistentCasingInFileNames": true,
10 | "noEmit": true,
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "incremental": true,
14 | "types": ["jest", "node"]
15 | },
16 | "include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "next-env.d.ts"],
17 | "exclude": [
18 | "node_modules",
19 | "jest.config.ts",
20 | "src/**/*.spec.ts",
21 | "src/**/*.test.ts"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/system/apps/blog/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"],
7 | "jsx": "react"
8 | },
9 | "include": [
10 | "jest.config.ts",
11 | "src/**/*.test.ts",
12 | "src/**/*.spec.ts",
13 | "src/**/*.test.tsx",
14 | "src/**/*.spec.tsx",
15 | "src/**/*.test.js",
16 | "src/**/*.spec.js",
17 | "src/**/*.test.jsx",
18 | "src/**/*.spec.jsx",
19 | "src/**/*.d.ts"
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/system/apps/blog/utils/index.ts:
--------------------------------------------------------------------------------
1 | import type { Lang } from '../models';
2 |
3 | import { readdir } from 'fs';
4 | import path from 'path';
5 |
6 | export const createArticlePath = (lang: Lang, ...rest: string[]): string =>
7 | path.join(
8 | process.cwd(),
9 | 'apps',
10 | 'blog',
11 | 'content',
12 | lang,
13 | 'articles',
14 | ...rest
15 | );
16 |
17 | export const getArticlesIds = (lang: Lang): Promise => {
18 | return new Promise((resolve, reject) => {
19 | const dirPath = createArticlePath(lang);
20 |
21 | readdir(dirPath, { withFileTypes: true }, (err, files) => {
22 | if (err) {
23 | reject(err);
24 | }
25 |
26 | resolve(
27 | files
28 | .filter((file) => file.isFile())
29 | .map((file) => file.name.replace('.mdx', ''))
30 | );
31 | });
32 | });
33 | };
34 |
--------------------------------------------------------------------------------
/system/apps/blog/views/home/defs.ts:
--------------------------------------------------------------------------------
1 | import type { ArticleDto } from '@system/blog-api';
2 |
3 | interface HomeViewProps {
4 | articles: ArticleDto[];
5 | }
6 |
7 | export type { HomeViewProps };
8 |
--------------------------------------------------------------------------------
/system/apps/blog/views/home/index.ts:
--------------------------------------------------------------------------------
1 | export * from './defs';
2 | export * from './home.view';
3 |
--------------------------------------------------------------------------------
/system/apps/jamjam-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 | }
11 |
--------------------------------------------------------------------------------
/system/apps/jamjam-e2e/cypress.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'cypress';
2 | import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
3 |
4 | export default defineConfig({
5 | e2e: nxE2EPreset(__dirname),
6 | });
7 |
--------------------------------------------------------------------------------
/system/apps/jamjam-e2e/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jamjam-e2e",
3 | "$schema": "../../node_modules/nx/schemas/project-schema.json",
4 | "sourceRoot": "apps/jamjam-e2e/src",
5 | "projectType": "application",
6 | "targets": {
7 | "e2e": {
8 | "executor": "@nrwl/cypress:cypress",
9 | "options": {
10 | "cypressConfig": "apps/jamjam-e2e/cypress.config.ts",
11 | "devServerTarget": "jamjam:serve:development",
12 | "testingType": "e2e"
13 | },
14 | "configurations": {
15 | "production": {
16 | "devServerTarget": "jamjam:serve:production"
17 | }
18 | }
19 | },
20 | "lint": {
21 | "executor": "@nrwl/linter:eslint",
22 | "outputs": ["{options.outputFile}"],
23 | "options": {
24 | "lintFilePatterns": ["apps/jamjam-e2e/**/*.{js,ts}"]
25 | }
26 | }
27 | },
28 | "tags": [],
29 | "implicitDependencies": ["jamjam"]
30 | }
31 |
--------------------------------------------------------------------------------
/system/apps/jamjam-e2e/src/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io"
4 | }
5 |
--------------------------------------------------------------------------------
/system/apps/jamjam-e2e/src/support/app.po.ts:
--------------------------------------------------------------------------------
1 | export const getGreeting = () => cy.get('h1');
2 |
--------------------------------------------------------------------------------
/system/apps/jamjam-e2e/src/support/e2e.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 |
--------------------------------------------------------------------------------
/system/apps/jamjam-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", "cypress.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/system/apps/jamjam/components/guitar-fretboard/consts.ts:
--------------------------------------------------------------------------------
1 | import type { GuitarFret } from '../../domain';
2 |
3 | const NOTES_COLORS = [
4 | '#f08989',
5 | '#cc9d72',
6 | '#a4cc72',
7 | '#72cc76',
8 | '#72ccbc',
9 | '#72b1cc',
10 | '#729bcc',
11 | '#9a72cc',
12 | '#728bcc',
13 | '#7f72cc',
14 | '#c572cc',
15 | '#cc72a8',
16 | ] as const;
17 |
18 | const DEFAULT_FRETS_MARKERS: GuitarFret[] = [
19 | 3, 5, 7, 9, 12, 15, 17, 19, 21, 24,
20 | ];
21 |
22 | export { NOTES_COLORS, DEFAULT_FRETS_MARKERS };
23 |
--------------------------------------------------------------------------------
/system/apps/jamjam/components/guitar-fretboard/defs.ts:
--------------------------------------------------------------------------------
1 | import type { MouseEventHandler, ReactElement } from 'react';
2 |
3 | import type {
4 | Guitar,
5 | GuitarFret,
6 | GuitarString,
7 | Note,
8 | NoteId,
9 | NoteNotation,
10 | NoteOctave,
11 | } from '../../domain';
12 |
13 | interface NoteButtonProps {
14 | noteOctave: NoteOctave;
15 | noteId: NoteId;
16 | notation: NoteNotation;
17 | onClick: MouseEventHandler;
18 | }
19 |
20 | interface GuitarFretboardProps {
21 | className?: string;
22 | guitar: Guitar;
23 | fretsMarkers?: GuitarFret[];
24 | notation: NoteNotation;
25 | NoteComponent?: (props: NoteButtonProps) => ReactElement;
26 | onNoteClick?: (note: Note) => void;
27 | }
28 |
29 | interface GuitarFretboardStringsProps {
30 | strings: GuitarString[];
31 | }
32 |
33 | export type {
34 | GuitarFretboardProps,
35 | NoteButtonProps,
36 | GuitarFretboardStringsProps,
37 | };
38 |
--------------------------------------------------------------------------------
/system/apps/jamjam/components/guitar-fretboard/guitar-fretboard-strings.tsx:
--------------------------------------------------------------------------------
1 | import type { GuitarFretboardStringsProps } from './defs';
2 |
3 | const GuitarFretboardStrings = ({ strings }: GuitarFretboardStringsProps) => {
4 | return (
5 |
6 | {strings.map(({ number }, idx) => (
7 |
13 | ))}
14 |
15 | );
16 | };
17 |
18 | export { GuitarFretboardStrings };
19 |
--------------------------------------------------------------------------------
/system/apps/jamjam/components/guitar-fretboard/index.ts:
--------------------------------------------------------------------------------
1 | export * from './guitar-fretboard';
2 | export * from './note-button';
3 | export * from './defs';
4 |
--------------------------------------------------------------------------------
/system/apps/jamjam/components/guitar-fretboard/tests/consts.test.ts:
--------------------------------------------------------------------------------
1 | import { NOTE_IDS } from '../../../domain';
2 | import { NOTES_COLORS, DEFAULT_FRETS_MARKERS } from '../consts';
3 |
4 | describe('creates default setup when', () => {
5 | it('number of colors are equal to number of notes', () => {
6 | expect(NOTES_COLORS.length).toBe(NOTE_IDS.length);
7 | });
8 |
9 | it('markers are equal to default guitar markers positions', () => {
10 | expect(DEFAULT_FRETS_MARKERS).toEqual([3, 5, 7, 9, 12, 15, 17, 19, 21, 24]);
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/system/apps/jamjam/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from './guitar-fretboard';
2 |
--------------------------------------------------------------------------------
/system/apps/jamjam/domain/index.ts:
--------------------------------------------------------------------------------
1 | export * from './music-theory';
2 |
--------------------------------------------------------------------------------
/system/apps/jamjam/domain/music-theory/index.ts:
--------------------------------------------------------------------------------
1 | export * from './defs';
2 | export * from './consts';
3 | export * from './core';
4 |
--------------------------------------------------------------------------------
/system/apps/jamjam/features/guitar-notes-teacher/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from './links';
2 |
--------------------------------------------------------------------------------
/system/apps/jamjam/features/guitar-notes-teacher/components/links/index.ts:
--------------------------------------------------------------------------------
1 | export * from './links';
2 |
--------------------------------------------------------------------------------
/system/apps/jamjam/features/guitar-notes-teacher/guitar-notes-teacher.feature.tsx:
--------------------------------------------------------------------------------
1 | import { useGuitarNotesTeacherFacade } from './use-guitar-notes-teacher.facade';
2 | import { GuitarNotesTeacherView } from './guitar-notes-teacher.view';
3 |
4 | const GuitarNotesTeacherFeature = () => {
5 | const machine = useGuitarNotesTeacherFacade();
6 |
7 | return ;
8 | };
9 |
10 | export { GuitarNotesTeacherFeature };
11 |
--------------------------------------------------------------------------------
/system/apps/jamjam/features/guitar-notes-teacher/guitar-notes-teacher.selectors.ts:
--------------------------------------------------------------------------------
1 | import { getNoteSymbol } from '../../domain';
2 |
3 | import type { PlayingState, StartedState } from './guitar-notes-teacher.defs';
4 |
5 | const getCurrentQuestion = (
6 | state: S
7 | ) => {
8 | const {
9 | answers: { length },
10 | } = state;
11 | const noteId = state.questions.at(state.answers.length);
12 |
13 | if (noteId === undefined) {
14 | throw Error(
15 | `Cannot find current question for ${noteId} and answer length ${length}`
16 | );
17 | }
18 |
19 | return getNoteSymbol(noteId, state.settings.notation);
20 | };
21 |
22 | export { getCurrentQuestion };
23 |
--------------------------------------------------------------------------------
/system/apps/jamjam/features/guitar-notes-teacher/index.ts:
--------------------------------------------------------------------------------
1 | export * from './guitar-notes-teacher.feature';
2 |
--------------------------------------------------------------------------------
/system/apps/jamjam/index.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-explicit-any */
2 | declare module '*.svg' {
3 | const content: any;
4 | export const ReactComponent: any;
5 | export default content;
6 | }
7 |
--------------------------------------------------------------------------------
/system/apps/jamjam/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'jamjam',
4 | preset: '../../jest.preset.js',
5 | transform: {
6 | '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest',
7 | '^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nrwl/next/babel'] }],
8 | },
9 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
10 | coverageDirectory: '../../coverage/apps/jamjam',
11 | };
12 |
--------------------------------------------------------------------------------
/system/apps/jamjam/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 |
--------------------------------------------------------------------------------
/system/apps/jamjam/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import { ThemeProvider } from '@system/figa-ui';
2 | import type { AppProps } from 'next/app';
3 | import Head from 'next/head';
4 |
5 | function CustomApp({ Component, pageProps }: AppProps) {
6 | return (
7 | <>
8 |
9 | Welcome to jamjam!
10 |
11 |
12 |
13 |
14 | >
15 | );
16 | }
17 |
18 | export default CustomApp;
19 |
--------------------------------------------------------------------------------
/system/apps/jamjam/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import { GuitarNotesTeacherFeature } from '../features/guitar-notes-teacher';
2 |
3 | const Index = () => {
4 | return ;
5 | };
6 |
7 | export default Index;
8 |
--------------------------------------------------------------------------------
/system/apps/jamjam/public/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polubis/Dream-stack-for-React-dev/ecd07fa7b038fbd76351bb2d24c1f191995ab614/system/apps/jamjam/public/.gitkeep
--------------------------------------------------------------------------------
/system/apps/jamjam/public/fonts/LexendBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polubis/Dream-stack-for-React-dev/ecd07fa7b038fbd76351bb2d24c1f191995ab614/system/apps/jamjam/public/fonts/LexendBold.ttf
--------------------------------------------------------------------------------
/system/apps/jamjam/public/fonts/LexendLight.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polubis/Dream-stack-for-React-dev/ecd07fa7b038fbd76351bb2d24c1f191995ab614/system/apps/jamjam/public/fonts/LexendLight.ttf
--------------------------------------------------------------------------------
/system/apps/jamjam/public/fonts/LexendMedium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polubis/Dream-stack-for-React-dev/ecd07fa7b038fbd76351bb2d24c1f191995ab614/system/apps/jamjam/public/fonts/LexendMedium.ttf
--------------------------------------------------------------------------------
/system/apps/jamjam/public/fonts/LexendRegular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polubis/Dream-stack-for-React-dev/ecd07fa7b038fbd76351bb2d24c1f191995ab614/system/apps/jamjam/public/fonts/LexendRegular.ttf
--------------------------------------------------------------------------------
/system/apps/jamjam/specs/index.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from '@testing-library/react';
3 |
4 | import Index from '../pages/index';
5 |
6 | describe('Index', () => {
7 | it('should render successfully', () => {
8 | const { baseElement } = render();
9 | expect(baseElement).toBeTruthy();
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/system/apps/jamjam/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"],
7 | "jsx": "react"
8 | },
9 | "include": [
10 | "jest.config.ts",
11 | "src/**/*.test.ts",
12 | "src/**/*.spec.ts",
13 | "src/**/*.test.tsx",
14 | "src/**/*.spec.tsx",
15 | "src/**/*.test.js",
16 | "src/**/*.spec.js",
17 | "src/**/*.test.jsx",
18 | "src/**/*.spec.jsx",
19 | "src/**/*.d.ts"
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/system/apps/jamjam/utils/index.test.ts:
--------------------------------------------------------------------------------
1 | import { getLast } from '.';
2 |
3 | describe('is able to return last element when', () => {
4 | it('for arrays returns first from right', () => {
5 | expect(getLast([1, 2, 3])).toBe(3);
6 | });
7 |
8 | it('returns undefined for empty or not array types', () => {
9 | expect(getLast([])).toBe(undefined);
10 | });
11 |
12 | it('original array is not changed', () => {
13 | const arr = [1, 2, 3];
14 |
15 | getLast(arr);
16 |
17 | expect(arr.length).toBe(arr.length);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/system/apps/jamjam/utils/index.ts:
--------------------------------------------------------------------------------
1 | const getLast = (items: T[]): T | undefined =>
2 | (Array.isArray(items) ? items : []).at(-1);
3 |
4 | export { getLast };
5 |
--------------------------------------------------------------------------------
/system/apps/sparkle-flicks-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 | }
11 |
--------------------------------------------------------------------------------
/system/apps/sparkle-flicks-e2e/cypress.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'cypress';
2 | import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
3 |
4 | export default defineConfig({
5 | e2e: nxE2EPreset(__dirname),
6 | });
7 |
--------------------------------------------------------------------------------
/system/apps/sparkle-flicks-e2e/src/e2e/app.cy.ts:
--------------------------------------------------------------------------------
1 | describe('home page', () => {
2 | beforeEach(() => {
3 | cy.visit('/');
4 | });
5 |
6 | it('should display welcome message', () => {
7 | cy.get('h5').contains('Sparkle flicks');
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/system/apps/sparkle-flicks-e2e/src/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io"
4 | }
5 |
--------------------------------------------------------------------------------
/system/apps/sparkle-flicks-e2e/src/support/e2e.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 |
--------------------------------------------------------------------------------
/system/apps/sparkle-flicks-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", "cypress.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/system/apps/sparkle-flicks/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "plugin:@nrwl/nx/react-typescript",
4 | "next",
5 | "next/core-web-vitals",
6 | "../../.eslintrc.json"
7 | ],
8 | "ignorePatterns": ["!**/*", ".next/**/*"],
9 | "overrides": [
10 | {
11 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
12 | "rules": {
13 | "@next/next/no-html-link-for-pages": [
14 | "error",
15 | "apps/sparkle-flicks/pages"
16 | ]
17 | }
18 | },
19 | {
20 | "files": ["*.ts", "*.tsx"],
21 | "rules": {}
22 | },
23 | {
24 | "files": ["*.js", "*.jsx"],
25 | "rules": {}
26 | }
27 | ],
28 | "rules": {
29 | "@next/next/no-html-link-for-pages": "off"
30 | },
31 | "env": {
32 | "jest": true
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/system/apps/sparkle-flicks/index.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-explicit-any */
2 | declare module '*.svg' {
3 | const content: any;
4 | export const ReactComponent: any;
5 | export default content;
6 | }
7 |
--------------------------------------------------------------------------------
/system/apps/sparkle-flicks/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'sparkle-flicks',
4 | preset: '../../jest.preset.js',
5 | transform: {
6 | '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest',
7 | '^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nrwl/next/babel'] }],
8 | },
9 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
10 | coverageDirectory: '../../coverage/apps/sparkle-flicks',
11 | };
12 |
--------------------------------------------------------------------------------
/system/apps/sparkle-flicks/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 |
--------------------------------------------------------------------------------
/system/apps/sparkle-flicks/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import { ThemeProvider } from '@system/figa-ui';
2 | import type { AppProps } from 'next/app';
3 | import Head from 'next/head';
4 |
5 | function CustomApp({ Component, pageProps }: AppProps) {
6 | return (
7 | <>
8 |
9 | Welcome to sparkle-flicks!
10 |
11 |
12 |
13 |
14 |
15 |
16 | >
17 | );
18 | }
19 |
20 | export default CustomApp;
21 |
--------------------------------------------------------------------------------
/system/apps/sparkle-flicks/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import { Font } from '@system/figa-ui';
2 |
3 | const IndexPage = () => {
4 | return Sparkle flicks;
5 | };
6 |
7 | export default IndexPage;
8 |
--------------------------------------------------------------------------------
/system/apps/sparkle-flicks/public/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polubis/Dream-stack-for-React-dev/ecd07fa7b038fbd76351bb2d24c1f191995ab614/system/apps/sparkle-flicks/public/.gitkeep
--------------------------------------------------------------------------------
/system/apps/sparkle-flicks/public/fonts/LexendBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polubis/Dream-stack-for-React-dev/ecd07fa7b038fbd76351bb2d24c1f191995ab614/system/apps/sparkle-flicks/public/fonts/LexendBold.ttf
--------------------------------------------------------------------------------
/system/apps/sparkle-flicks/public/fonts/LexendLight.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polubis/Dream-stack-for-React-dev/ecd07fa7b038fbd76351bb2d24c1f191995ab614/system/apps/sparkle-flicks/public/fonts/LexendLight.ttf
--------------------------------------------------------------------------------
/system/apps/sparkle-flicks/public/fonts/LexendMedium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polubis/Dream-stack-for-React-dev/ecd07fa7b038fbd76351bb2d24c1f191995ab614/system/apps/sparkle-flicks/public/fonts/LexendMedium.ttf
--------------------------------------------------------------------------------
/system/apps/sparkle-flicks/public/fonts/LexendRegular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polubis/Dream-stack-for-React-dev/ecd07fa7b038fbd76351bb2d24c1f191995ab614/system/apps/sparkle-flicks/public/fonts/LexendRegular.ttf
--------------------------------------------------------------------------------
/system/apps/sparkle-flicks/specs/index.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from '@testing-library/react';
3 |
4 | import Index from '../pages/index';
5 |
6 | describe('Index', () => {
7 | it('should render successfully', () => {
8 | const { baseElement } = render();
9 | expect(baseElement).toBeTruthy();
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/system/apps/sparkle-flicks/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"],
7 | "jsx": "react"
8 | },
9 | "include": [
10 | "jest.config.ts",
11 | "src/**/*.test.ts",
12 | "src/**/*.spec.ts",
13 | "src/**/*.test.tsx",
14 | "src/**/*.spec.tsx",
15 | "src/**/*.test.js",
16 | "src/**/*.spec.js",
17 | "src/**/*.test.jsx",
18 | "src/**/*.spec.jsx",
19 | "src/**/*.d.ts"
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/system/assets/fonts/LexendBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polubis/Dream-stack-for-React-dev/ecd07fa7b038fbd76351bb2d24c1f191995ab614/system/assets/fonts/LexendBold.ttf
--------------------------------------------------------------------------------
/system/assets/fonts/LexendLight.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polubis/Dream-stack-for-React-dev/ecd07fa7b038fbd76351bb2d24c1f191995ab614/system/assets/fonts/LexendLight.ttf
--------------------------------------------------------------------------------
/system/assets/fonts/LexendMedium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polubis/Dream-stack-for-React-dev/ecd07fa7b038fbd76351bb2d24c1f191995ab614/system/assets/fonts/LexendMedium.ttf
--------------------------------------------------------------------------------
/system/assets/fonts/LexendRegular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polubis/Dream-stack-for-React-dev/ecd07fa7b038fbd76351bb2d24c1f191995ab614/system/assets/fonts/LexendRegular.ttf
--------------------------------------------------------------------------------
/system/babel.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "babelrcRoots": ["*"]
3 | }
4 |
--------------------------------------------------------------------------------
/system/jest.config.ts:
--------------------------------------------------------------------------------
1 | import { getJestProjects } from '@nrwl/jest';
2 |
3 | export default {
4 | projects: getJestProjects(),
5 | };
6 |
--------------------------------------------------------------------------------
/system/jest.preset.js:
--------------------------------------------------------------------------------
1 | const nxPreset = require('@nrwl/jest/preset').default;
2 |
3 | module.exports = { ...nxPreset };
4 |
--------------------------------------------------------------------------------
/system/libs/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polubis/Dream-stack-for-React-dev/ecd07fa7b038fbd76351bb2d24c1f191995ab614/system/libs/.gitkeep
--------------------------------------------------------------------------------
/system/libs/blog-api/.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 |
--------------------------------------------------------------------------------
/system/libs/blog-api/README.md:
--------------------------------------------------------------------------------
1 | # blog-api
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Building
6 |
7 | Run `nx build blog-api` to build the library.
8 |
9 | ## Running unit tests
10 |
11 | Run `nx test blog-api` to execute the unit tests via [Jest](https://jestjs.io).
12 |
--------------------------------------------------------------------------------
/system/libs/blog-api/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'blog-api',
4 | preset: '../../jest.preset.js',
5 | globals: {
6 | 'ts-jest': {
7 | tsconfig: '/tsconfig.spec.json',
8 | },
9 | },
10 | transform: {
11 | '^.+\\.[tj]s$': 'ts-jest',
12 | },
13 | moduleFileExtensions: ['ts', 'js', 'html'],
14 | coverageDirectory: '../../coverage/libs/blog-api',
15 | };
16 |
--------------------------------------------------------------------------------
/system/libs/blog-api/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@system/blog-api",
3 | "version": "0.0.1",
4 | "type": "commonjs"
5 | }
6 |
--------------------------------------------------------------------------------
/system/libs/blog-api/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './models';
2 | export * from './lib';
3 |
--------------------------------------------------------------------------------
/system/libs/blog-api/src/lib/account.ts:
--------------------------------------------------------------------------------
1 | import type { SignOutResponse, SignInPayload, SignInResponse } from '../models';
2 | import { pst } from './instance';
3 |
4 | export const signOut = async (): Promise => {
5 | const { data } = await pst('Account/SignOut');
6 |
7 | return data;
8 | };
9 |
10 | export const signIn = async (
11 | payload: SignInPayload
12 | ): Promise => {
13 | await pst('Account/SignIn', payload);
14 | };
15 |
--------------------------------------------------------------------------------
/system/libs/blog-api/src/lib/articles.ts:
--------------------------------------------------------------------------------
1 | import type { GetArticlesResponse, GetArticlesSearchParams } from '../models';
2 | import { get } from './instance';
3 |
4 | export const getArticles = async (
5 | params?: GetArticlesSearchParams
6 | ): Promise => {
7 | const { data } = await get('Articles', { params });
8 |
9 | return data;
10 | };
11 |
--------------------------------------------------------------------------------
/system/libs/blog-api/src/lib/index.ts:
--------------------------------------------------------------------------------
1 | export * from './articles';
2 | export * from './account';
3 |
--------------------------------------------------------------------------------
/system/libs/blog-api/src/lib/instance.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | declare const process: {
4 | env: {
5 | NEXT_PUBLIC_API_URL?: string;
6 | };
7 | };
8 |
9 | const getAPIUrl = () => {
10 | const url = process.env.NEXT_PUBLIC_API_URL;
11 |
12 | if (url === undefined) {
13 | throw Error('Lack of process.env.NEXT_PUBLIC_API_URL');
14 | }
15 |
16 | return url;
17 | };
18 |
19 | const {
20 | get,
21 | post: pst,
22 | put,
23 | patch: pch,
24 | delete: del,
25 | } = axios.create({
26 | baseURL: getAPIUrl(),
27 | withCredentials: true,
28 | headers: {
29 | 'Content-Type': 'application/json',
30 | },
31 | });
32 |
33 | export { get, pst, put, pch, del };
34 |
--------------------------------------------------------------------------------
/system/libs/blog-api/src/models/account.ts:
--------------------------------------------------------------------------------
1 | import type { Login, Password } from './general';
2 |
3 | type SignInResponse = void;
4 | interface SignInPayload {
5 | login: Login;
6 | password: Password;
7 | }
8 |
9 | type SignOutResponse = void;
10 |
11 | export type { SignInResponse, SignInPayload, SignOutResponse };
12 |
--------------------------------------------------------------------------------
/system/libs/blog-api/src/models/articles.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Description,
3 | Email,
4 | Id,
5 | Name,
6 | PaginatedResponse,
7 | Title,
8 | Url,
9 | } from './general';
10 |
11 | type ArticleStatus = 'Draft' | 'WaitingForApproval' | 'NeedWork' | 'Accepted';
12 |
13 | interface ArticleDto {
14 | id: Id;
15 | title: Title;
16 | description: Description;
17 | authorEmail: Email;
18 | authorName: Name;
19 | thumbnailUrl: Url;
20 | status: ArticleStatus;
21 | url: Url;
22 | }
23 |
24 | type GetArticlesResponse = PaginatedResponse;
25 | interface GetArticlesSearchParams {
26 | Search?: string;
27 | ItemsPerPage?: number;
28 | CurrentPage?: number;
29 | }
30 |
31 | export type {
32 | ArticleDto,
33 | ArticleStatus,
34 | GetArticlesResponse,
35 | GetArticlesSearchParams,
36 | };
37 |
--------------------------------------------------------------------------------
/system/libs/blog-api/src/models/general.ts:
--------------------------------------------------------------------------------
1 | type Description = string;
2 | type Id = string;
3 | type Title = string;
4 | type Email = string;
5 | type Name = string;
6 | type Url = string;
7 | type Login = string;
8 | type Password = string;
9 | type DateStamp = string;
10 |
11 | interface ResponseError {
12 | key: string;
13 | message: 'string';
14 | }
15 |
16 | interface Response {
17 | success: boolean;
18 | hasErrors: boolean;
19 | errors: ResponseError[];
20 | data: D;
21 | }
22 |
23 | interface PaginatedResponse extends Response {
24 | itemsPerPage: number;
25 | totalPages: number;
26 | currentPage: number;
27 | currentPageItemsNumber: number;
28 | }
29 |
30 | export type {
31 | Id,
32 | Description,
33 | Title,
34 | Name,
35 | Email,
36 | Url,
37 | Response,
38 | PaginatedResponse,
39 | Login,
40 | Password,
41 | DateStamp,
42 | };
43 |
--------------------------------------------------------------------------------
/system/libs/blog-api/src/models/index.ts:
--------------------------------------------------------------------------------
1 | export * from './account';
2 | export * from './articles';
3 | export * from './general';
4 |
--------------------------------------------------------------------------------
/system/libs/blog-api/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "module": "commonjs",
5 | "forceConsistentCasingInFileNames": true,
6 | "strict": true,
7 | "noImplicitOverride": true,
8 | "noPropertyAccessFromIndexSignature": true,
9 | "noImplicitReturns": true,
10 | "noFallthroughCasesInSwitch": true
11 | },
12 | "files": [],
13 | "include": [],
14 | "references": [
15 | {
16 | "path": "./tsconfig.lib.json"
17 | },
18 | {
19 | "path": "./tsconfig.spec.json"
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/system/libs/blog-api/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "declaration": true,
6 | "types": ["node"]
7 | },
8 | "include": ["src/**/*.ts"],
9 | "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/system/libs/blog-api/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": [
9 | "jest.config.ts",
10 | "src/**/*.test.ts",
11 | "src/**/*.spec.ts",
12 | "src/**/*.d.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/system/libs/figa-hooks/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["plugin:@nrwl/nx/react", "../../.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 |
--------------------------------------------------------------------------------
/system/libs/figa-hooks/README.md:
--------------------------------------------------------------------------------
1 | # figa-hooks
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Running unit tests
6 |
7 | Run `nx test figa-hooks` to execute the unit tests via [Vitest](https://vitest.dev/).
8 |
--------------------------------------------------------------------------------
/system/libs/figa-hooks/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'figa-hooks',
4 | preset: '../../jest.preset.js',
5 | transform: {
6 | '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest',
7 | '^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nrwl/react/babel'] }],
8 | },
9 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
10 | coverageDirectory: '../../coverage/libs/figa-hooks',
11 | };
12 |
--------------------------------------------------------------------------------
/system/libs/figa-hooks/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@system/figa-hooks",
3 | "version": "0.0.1",
4 | "main": "./index.js",
5 | "types": "./index.d.ts",
6 | "exports": {
7 | ".": {
8 | "import": "./index.mjs",
9 | "require": "./index.js"
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/system/libs/figa-hooks/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/use-portal';
2 | export * from './lib/use-toggle';
3 | export * from './lib/use-intersection-observer';
4 | export * from './lib/use-click-outside';
5 | export * from './lib/use-stepper';
6 | export * from './lib/use-scroll-y';
7 | export * from './lib/use-client-effect';
8 | export * from './lib/use-client-layout-effect';
9 |
--------------------------------------------------------------------------------
/system/libs/figa-hooks/src/lib/use-click-outside/defs.ts:
--------------------------------------------------------------------------------
1 | import type { MutableRefObject } from 'react';
2 |
3 | /**
4 | * We'll pass this config object to hook.
5 | */
6 | interface UseClickOutsideConfig {
7 | /**
8 | * Function to call when clicked outside.
9 | */
10 | onOutside: () => void;
11 | }
12 |
13 | /**
14 | * We'll return this object from hook.
15 | */
16 | interface UseClickOutsideReturn {
17 | /**
18 | * Reference to any HTML element.
19 | * The @T parameter is the type of the element we'll
20 | * pass. It must extend from @HTMLElement type.
21 | */
22 | ref: MutableRefObject;
23 | }
24 |
25 | export type { UseClickOutsideConfig, UseClickOutsideReturn };
26 |
--------------------------------------------------------------------------------
/system/libs/figa-hooks/src/lib/use-click-outside/index.ts:
--------------------------------------------------------------------------------
1 | export * from './defs';
2 | export * from './use-click-outside';
3 |
--------------------------------------------------------------------------------
/system/libs/figa-hooks/src/lib/use-client-effect/index.ts:
--------------------------------------------------------------------------------
1 | export * from './use-client-effect';
2 |
--------------------------------------------------------------------------------
/system/libs/figa-hooks/src/lib/use-client-effect/use-client-effect.ts:
--------------------------------------------------------------------------------
1 | import { type DependencyList, useEffect, type EffectCallback } from 'react';
2 |
3 | import { isClient } from '@system/utils';
4 |
5 | /**
6 | * Works exactly like useEffect hook but only on the client side.
7 | *
8 | * @param {cb} - Callback function to call if dependency changes.
9 | * @returns {deps} - List of deps. When one of them changes reference or value
10 | * it will trigger a callback.
11 | */
12 | const useClientEffect = (cb: EffectCallback, deps: DependencyList = []) => {
13 | useEffect(() => {
14 | if (isClient()) {
15 | const cleanUp = cb();
16 |
17 | return () => {
18 | cleanUp && cleanUp();
19 | };
20 | }
21 | // eslint-disable-next-line react-hooks/exhaustive-deps
22 | }, deps);
23 | };
24 |
25 | export { useClientEffect };
26 |
--------------------------------------------------------------------------------
/system/libs/figa-hooks/src/lib/use-client-layout-effect/index.ts:
--------------------------------------------------------------------------------
1 | export * from './use-client-layout-effect';
2 |
--------------------------------------------------------------------------------
/system/libs/figa-hooks/src/lib/use-client-layout-effect/use-client-layout-effect.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | type DependencyList,
3 | useLayoutEffect,
4 | type EffectCallback,
5 | } from 'react';
6 |
7 | import { isClient } from '@system/utils';
8 |
9 | /**
10 | * Works exactly like useLayoutEffect hook but only on the client side.
11 | *
12 | * @param {cb} - Callback function to call if dependency changes.
13 | * @returns {deps} - List of deps. When one of them changes reference or value
14 | * it will trigger a callback.
15 | */
16 | const useClientLayoutEffect = (
17 | cb: EffectCallback,
18 | deps: DependencyList = []
19 | ) => {
20 | useLayoutEffect(() => {
21 | if (isClient()) {
22 | const cleanUp = cb();
23 |
24 | return () => {
25 | cleanUp && cleanUp();
26 | };
27 | }
28 | // eslint-disable-next-line react-hooks/exhaustive-deps
29 | }, deps);
30 | };
31 |
32 | export { useClientLayoutEffect };
33 |
--------------------------------------------------------------------------------
/system/libs/figa-hooks/src/lib/use-intersection-observer/defs.ts:
--------------------------------------------------------------------------------
1 | import type { MutableRefObject } from 'react';
2 |
3 | /**
4 | * We'll pass this config object to hook.
5 | */
6 | type IntersectionObserverConfig = IntersectionObserverInit;
7 |
8 | /**
9 | * We'll return this object from hook.
10 | */
11 | interface IntersectionObserverReturn {
12 | /**
13 | * Reference to any HTML element.
14 | * The @T parameter is the type of the element we'll
15 | * pass. It must extend from @HTMLElement type.
16 | */
17 | ref: MutableRefObject;
18 | /**
19 | * Indicates whether the item is visible.
20 | */
21 | visible: boolean;
22 | }
23 |
24 | export type { IntersectionObserverConfig, IntersectionObserverReturn };
25 |
--------------------------------------------------------------------------------
/system/libs/figa-hooks/src/lib/use-intersection-observer/index.ts:
--------------------------------------------------------------------------------
1 | export * from './defs';
2 | export * from './use-intersection-observer';
3 |
--------------------------------------------------------------------------------
/system/libs/figa-hooks/src/lib/use-portal/defs.ts:
--------------------------------------------------------------------------------
1 | import type { ReactNode, ReactPortal } from 'react';
2 |
3 | type RenderPortal = (children: ReactNode) => ReactPortal | null;
4 |
5 | type UsePortal = () => {
6 | render: RenderPortal;
7 | };
8 |
9 | export type { UsePortal, RenderPortal };
10 |
--------------------------------------------------------------------------------
/system/libs/figa-hooks/src/lib/use-portal/index.ts:
--------------------------------------------------------------------------------
1 | export * from './defs';
2 | export * from './use-portal';
3 |
--------------------------------------------------------------------------------
/system/libs/figa-hooks/src/lib/use-scroll-y/index.ts:
--------------------------------------------------------------------------------
1 | export * from './defs';
2 | export * from './use-scroll-y';
3 |
--------------------------------------------------------------------------------
/system/libs/figa-hooks/src/lib/use-stepper/index.ts:
--------------------------------------------------------------------------------
1 | export * from './use-stepper';
2 | export * from './defs';
3 |
--------------------------------------------------------------------------------
/system/libs/figa-hooks/src/lib/use-toggle/defs.ts:
--------------------------------------------------------------------------------
1 | interface UseToggleReturn {
2 | isOpen: boolean;
3 | open: () => void;
4 | close: () => void;
5 | toggle: () => void;
6 | }
7 |
8 | type UseTogglePayload = [boolean?];
9 |
10 | export type { UseTogglePayload, UseToggleReturn };
11 |
--------------------------------------------------------------------------------
/system/libs/figa-hooks/src/lib/use-toggle/index.ts:
--------------------------------------------------------------------------------
1 | export * from './defs';
2 | export * from './use-toggle';
3 |
--------------------------------------------------------------------------------
/system/libs/figa-hooks/src/lib/use-toggle/use-toggle.tsx:
--------------------------------------------------------------------------------
1 | import type { UseTogglePayload, UseToggleReturn } from './defs';
2 |
3 | import { useState } from 'react';
4 |
5 | const useToggle = (...payload: UseTogglePayload): UseToggleReturn => {
6 | const [initialOpen = false] = payload;
7 | const [isOpen, setIsOpen] = useState(initialOpen);
8 |
9 | const open = () => {
10 | setIsOpen(true);
11 | };
12 |
13 | const close = () => {
14 | setIsOpen(false);
15 | };
16 |
17 | const toggle = () => {
18 | setIsOpen((prev) => !prev);
19 | };
20 |
21 | return { isOpen, open, close, toggle };
22 | };
23 |
24 | export { useToggle };
25 |
--------------------------------------------------------------------------------
/system/libs/figa-hooks/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsx": "react-jsx",
4 | "allowJs": false,
5 | "esModuleInterop": false,
6 | "allowSyntheticDefaultImports": true,
7 | "strict": true,
8 | "types": ["vite/client"]
9 | },
10 | "files": [],
11 | "include": [],
12 | "references": [
13 | {
14 | "path": "./tsconfig.lib.json"
15 | },
16 | {
17 | "path": "./tsconfig.spec.json"
18 | }
19 | ],
20 | "extends": "../../tsconfig.base.json"
21 | }
22 |
--------------------------------------------------------------------------------
/system/libs/figa-hooks/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "types": ["node"]
6 | },
7 | "files": [
8 | "../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
9 | "../../node_modules/@nrwl/react/typings/image.d.ts"
10 | ],
11 | "exclude": [
12 | "**/*.spec.ts",
13 | "**/*.test.ts",
14 | "**/*.spec.tsx",
15 | "**/*.test.tsx",
16 | "**/*.spec.js",
17 | "**/*.test.js",
18 | "**/*.spec.jsx",
19 | "**/*.test.jsx"
20 | ],
21 | "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
22 | }
23 |
--------------------------------------------------------------------------------
/system/libs/figa-hooks/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": [
9 | "jest.config.ts",
10 | "src/**/*.test.ts",
11 | "src/**/*.spec.ts",
12 | "src/**/*.test.tsx",
13 | "src/**/*.spec.tsx",
14 | "src/**/*.test.js",
15 | "src/**/*.spec.js",
16 | "src/**/*.test.jsx",
17 | "src/**/*.spec.jsx",
18 | "src/**/*.d.ts"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["plugin:@nrwl/nx/react", "../../.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 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/.storybook/main.js:
--------------------------------------------------------------------------------
1 | const { mergeConfig } = require('vite');
2 | const viteTsConfigPaths = require('vite-tsconfig-paths').default;
3 |
4 | module.exports = {
5 | core: { builder: '@storybook/builder-vite' },
6 | stories: [
7 | '../src/lib/**/*.stories.mdx',
8 | '../src/lib/**/*.stories.@(js|jsx|ts|tsx)',
9 | ],
10 | addons: ['@storybook/addon-essentials'],
11 | staticDirs: [{ from: '../../../assets', to: '/' }],
12 | async viteFinal(config, { configType }) {
13 | return mergeConfig(config, {
14 | plugins: [
15 | viteTsConfigPaths({
16 | root: '../../../',
17 | }),
18 | ],
19 | });
20 | },
21 | };
22 |
23 | // To customize your Vite configuration you can use the viteFinal field.
24 | // Check https://storybook.js.org/docs/react/builders/vite#configuration
25 | // and https://nx.dev/packages/storybook/documents/custom-builder-configs
26 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/.storybook/preview.tsx:
--------------------------------------------------------------------------------
1 | import type { DecoratorFunction } from '@storybook/addons';
2 | import type { Story } from '@storybook/react';
3 | import { ThemeProvider } from '../src/lib/theme-provider';
4 | import { Sandbox } from '../src/lib/sandbox';
5 |
6 | const withGlobalStyleDecorator: DecoratorFunction = (
7 | StoryComponent: Story
8 | ) => (
9 |
10 |
11 |
12 |
13 |
14 | );
15 |
16 | const decorators = [withGlobalStyleDecorator];
17 |
18 | export { decorators };
19 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/.storybook/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "emitDecoratorMetadata": true,
5 | "outDir": ""
6 | },
7 | "files": [
8 | "../../../node_modules/@nrwl/react/typings/styled-jsx.d.ts",
9 | "../../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
10 | "../../../node_modules/@nrwl/react/typings/image.d.ts"
11 | ],
12 | "exclude": [
13 | "../**/*.spec.ts",
14 | "../**/*.spec.js",
15 | "../**/*.spec.tsx",
16 | "../**/*.spec.jsx"
17 | ],
18 | "include": [
19 | "../src/**/*.stories.ts",
20 | "../src/**/*.stories.js",
21 | "../src/**/*.stories.jsx",
22 | "../src/**/*.stories.tsx",
23 | "../src/**/*.stories.mdx",
24 | "*.js",
25 | "preview.tsx"
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/README.md:
--------------------------------------------------------------------------------
1 | # figa-ui
2 |
3 | This library was generated with [Nx](https://nx.dev).
4 |
5 | ## Running unit tests
6 |
7 | Run `nx test figa-ui` to execute the unit tests via [Vitest](https://vitest.dev/).
8 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/jest.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | export default {
3 | displayName: 'figa-ui',
4 | preset: '../../jest.preset.js',
5 | transform: {
6 | '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest',
7 | '^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nrwl/react/babel'] }],
8 | },
9 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
10 | coverageDirectory: '../../coverage/libs/figa-ui',
11 | };
12 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@system/figa-ui",
3 | "version": "0.0.1",
4 | "main": "./index.js",
5 | "types": "./index.d.ts",
6 | "exports": {
7 | ".": {
8 | "import": "./index.mjs",
9 | "require": "./index.js"
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/font';
2 | export * from './lib/theme-provider';
3 | export * from './lib/sandbox';
4 | export * from './lib/modal';
5 | export * from './lib/button';
6 | export * from './lib/select';
7 | export * from './lib/navigation';
8 | export * from './lib/logo';
9 | export * from './lib/link';
10 | export * from './lib/emoji-picker';
11 | export * from './lib/progress-circle';
12 | export * from './lib/input';
13 | export * from './lib/footer';
14 | export * from './lib/box';
15 | export * from './lib/layout';
16 | export * from './lib/article-layout';
17 | export * from './lib/left-bar';
18 | export * from './lib/popover';
19 | export * from './lib/code-block';
20 | export * from './lib/logo-graphic';
21 | export * from './lib/list';
22 | export * from './lib/blockquote';
23 | export * from './lib/code';
24 | export * from './lib/icon';
25 | export * from './lib/shared';
26 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/article-layout/__snapshots__/article-layout.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Article layout can be used when [FRAGILE] assigns classes 1`] = `
4 |
5 |
8 |
9 | `;
10 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/article-layout/article-layout.test.tsx:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import { ArticleLayout } from './article-layout';
3 |
4 | describe('Article layout can be used when', () => {
5 | it('[FRAGILE] assigns classes', () => {
6 | const { container, asFragment } = render(
7 |
8 | <>>
9 |
10 | );
11 |
12 | const articleLayout = container.querySelector('.article-layout');
13 |
14 | expect(articleLayout?.className).toContain('article-layout my-class');
15 | expect(asFragment()).toMatchSnapshot();
16 | });
17 |
18 | it('renders content', () => {
19 | render(
20 |
21 | Content
22 |
23 | );
24 |
25 | screen.getByText(/Content/);
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/article-layout/defs.ts:
--------------------------------------------------------------------------------
1 | import type { ReactNode } from 'react';
2 |
3 | interface ArticleLayoutProps {
4 | className?: string;
5 | children: ReactNode;
6 | }
7 |
8 | export type { ArticleLayoutProps };
9 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/article-layout/index.ts:
--------------------------------------------------------------------------------
1 | export * from './article-layout';
2 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/blockquote/__snapshots__/blockquote.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Blockquote can be used when [FRAGILE] assigns classes 1`] = `
4 |
5 |
8 |
11 | My text
12 |
13 |
14 |
15 | `;
16 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/blockquote/blockquote.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Story, Meta } from '@storybook/react';
2 |
3 | import { Blockquote } from './blockquote';
4 | import { Box } from '../box';
5 | import { FONT_VARIANTS } from '../font';
6 |
7 | export default {
8 | component: Blockquote,
9 | title: 'Blockquote',
10 | } as Meta;
11 |
12 | const Template: Story = () => {
13 | return (
14 | 150)}>
15 | {FONT_VARIANTS.map((variant) => (
16 |
17 | ))}
18 |
19 | );
20 | };
21 |
22 | export const Default = Template.bind({});
23 | Default.args = {};
24 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/blockquote/blockquote.test.tsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 | import { Blockquote } from './blockquote';
3 |
4 | describe('Blockquote can be used when', () => {
5 | it('[FRAGILE] assigns classes', () => {
6 | const { asFragment } = render(
7 |
8 | My text
9 |
10 | );
11 |
12 | expect(asFragment()).toMatchSnapshot();
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/blockquote/blockquote.tsx:
--------------------------------------------------------------------------------
1 | import { Font } from '../font';
2 | import type { BlockquoteProps } from './defs';
3 |
4 | import c from 'classnames';
5 |
6 | const Blockquote = ({ children, className, variant }: BlockquoteProps) => {
7 | return (
8 |
9 | {children}
10 |
11 | );
12 | };
13 |
14 | export { Blockquote };
15 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/blockquote/defs.ts:
--------------------------------------------------------------------------------
1 | import type { ReactNode } from 'react';
2 | import type { FontVariant } from '../font';
3 |
4 | interface BlockquoteProps {
5 | className?: string;
6 | variant: FontVariant;
7 | children: ReactNode;
8 | }
9 |
10 | export type { BlockquoteProps };
11 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/blockquote/index.ts:
--------------------------------------------------------------------------------
1 | export * from './blockquote';
2 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/box/index.ts:
--------------------------------------------------------------------------------
1 | export * from './defs';
2 | export * from './box';
3 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/button/__snapshots__/button.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Button can be used when [FRAGILE] renders with default props 1`] = `
4 |
5 |
8 |
9 | `;
10 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/button/button.tsx:
--------------------------------------------------------------------------------
1 | import type { ButtonProps } from './defs';
2 |
3 | const Button = ({ className, ...rest }: ButtonProps) => {
4 | const {
5 | shape = 'rectangle',
6 | size = 3,
7 | variant = 'filled',
8 | motive = 'primary',
9 | } = rest;
10 |
11 | const formattedClassName = className ? ` ${className}` : '';
12 |
13 | return (
14 |
18 | );
19 | };
20 |
21 | export { Button };
22 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/button/consts.ts:
--------------------------------------------------------------------------------
1 | const BUTTON_SIZES = [1, 2, 3, 4, 5] as const;
2 | const BUTTON_SHAPES = ['rounded', 'rectangle'] as const;
3 | const BUTTON_VARIANTS = ['outlined', 'filled'] as const;
4 | const BUTTON_MOTIVES = ['primary', 'secondary'] as const;
5 |
6 | export { BUTTON_SIZES, BUTTON_SHAPES, BUTTON_VARIANTS, BUTTON_MOTIVES };
7 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/button/defs.ts:
--------------------------------------------------------------------------------
1 | import type { ButtonHTMLAttributes, DetailedHTMLProps } from 'react';
2 | import type {
3 | BUTTON_MOTIVES,
4 | BUTTON_SHAPES,
5 | BUTTON_SIZES,
6 | BUTTON_VARIANTS,
7 | } from './consts';
8 |
9 | type ButtonSize = (typeof BUTTON_SIZES)[number];
10 | type ButtonShape = (typeof BUTTON_SHAPES)[number];
11 | type ButtonVariant = (typeof BUTTON_VARIANTS)[number];
12 | type ButtonMotive = (typeof BUTTON_MOTIVES)[number];
13 |
14 | interface ButtonProps
15 | extends DetailedHTMLProps<
16 | ButtonHTMLAttributes,
17 | HTMLButtonElement
18 | > {
19 | size?: ButtonSize;
20 | shape?: ButtonShape;
21 | variant?: ButtonVariant;
22 | motive?: ButtonMotive;
23 | }
24 |
25 | export type {
26 | ButtonProps,
27 | ButtonSize,
28 | ButtonShape,
29 | ButtonVariant,
30 | ButtonMotive,
31 | };
32 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/button/index.ts:
--------------------------------------------------------------------------------
1 | export * from './button';
2 | export * from './defs';
3 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/code-block/code-block.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Story, Meta } from '@storybook/react';
2 |
3 | import { CodeBlock } from './code-block';
4 | import { Box } from '../box';
5 | import { Font } from '../font';
6 |
7 | export default {
8 | component: CodeBlock,
9 | title: 'CodeBlock',
10 | } as Meta;
11 |
12 | const Template: Story = () => {
13 | return (
14 |
15 | (
17 | <>
18 | {Dots} Header
19 | >
20 | )}
21 | >
22 | Content
23 |
24 |
25 | Content
26 |
27 |
28 | );
29 | };
30 |
31 | export const Default = Template.bind({});
32 | Default.args = {};
33 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/code-block/code-block.tsx:
--------------------------------------------------------------------------------
1 | import c from 'classnames';
2 | import type { CodeBlockProps } from './defs';
3 |
4 | const Dots = (
5 |
10 | );
11 |
12 | const CodeBlock = ({ className, header, children }: CodeBlockProps) => {
13 | return (
14 |
15 |
{header ? header(Dots) : Dots}
16 |
17 |
{children}
18 |
19 | );
20 | };
21 |
22 | export { CodeBlock };
23 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/code-block/defs.ts:
--------------------------------------------------------------------------------
1 | import type { ReactNode } from 'react';
2 |
3 | interface CodeBlockProps {
4 | className?: string;
5 | header?: (dots: ReactNode) => ReactNode;
6 | children: ReactNode;
7 | }
8 |
9 | export type { CodeBlockProps };
10 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/code-block/index.ts:
--------------------------------------------------------------------------------
1 | export * from './defs';
2 | export * from './code-block';
3 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/code/defs.ts:
--------------------------------------------------------------------------------
1 | interface CodeProps {
2 | className?: string;
3 | children: string;
4 | readonly?: boolean;
5 | }
6 |
7 | interface SetupConfig extends Pick {
8 | parent: Element;
9 | }
10 |
11 | export type { CodeProps, SetupConfig };
12 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/code/index.ts:
--------------------------------------------------------------------------------
1 | export * from './code';
2 | export * from './defs';
3 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/code/setup.test.ts:
--------------------------------------------------------------------------------
1 | import { setup } from './setup';
2 |
3 | describe('Code works when: ', () => {
4 | const children = "const a = 'hello';";
5 |
6 | it('creates instance', () => {
7 | const view = setup({
8 | children,
9 | parent: document.createElement('div'),
10 | });
11 |
12 | expect(view.state.doc.toString()).toBe(children);
13 | expect(view.state.readOnly).toBe(false);
14 | });
15 |
16 | it('disables auto select in readonly mode', () => {
17 | const view = setup({
18 | children,
19 | parent: document.createElement('div'),
20 | readonly: true,
21 | });
22 |
23 | expect(view.state.doc.toString()).toBe(children);
24 | expect(view.state.readOnly).toBe(true);
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/emoji-picker/defs.ts:
--------------------------------------------------------------------------------
1 | type Emoji = string;
2 |
3 | interface EmojiPickerDataItem {
4 | emoji: Emoji;
5 | name: string;
6 | }
7 |
8 | type EmojiPickerData = EmojiPickerDataItem[];
9 |
10 | interface EmojiPickerProps {
11 | className?: string;
12 | data?: EmojiPickerData;
13 | title: string;
14 | onSelect: (emoji: Emoji) => void;
15 | }
16 |
17 | export type { EmojiPickerProps, EmojiPickerDataItem, EmojiPickerData, Emoji };
18 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/emoji-picker/emoji-picker.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Story, Meta } from '@storybook/react';
2 | import type { EmojiPickerProps } from './defs';
3 |
4 | import { EmojiPicker } from './emoji-picker';
5 |
6 | export default {
7 | component: EmojiPicker,
8 | title: 'EmojiPicker',
9 | } as Meta;
10 |
11 | const Template: Story = (args) => ;
12 |
13 | export const Default = Template.bind({});
14 | Default.args = {
15 | title: 'Pick your emoji',
16 | };
17 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/emoji-picker/index.ts:
--------------------------------------------------------------------------------
1 | export * from './emoji-picker';
2 | export * from './defs';
3 | export * from './consts';
4 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/font/consts.ts:
--------------------------------------------------------------------------------
1 | const FONT_VARIANTS = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'b1', 'b2'] as const;
2 | const FONT_MOTIVES = ['default', 'primary'] as const;
3 |
4 | export { FONT_VARIANTS, FONT_MOTIVES };
5 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/font/defs.ts:
--------------------------------------------------------------------------------
1 | import type { ReactNode } from 'react';
2 | import type { FONT_MOTIVES, FONT_VARIANTS } from './consts';
3 |
4 | type FontVariant = (typeof FONT_VARIANTS)[number];
5 | type FontMotive = (typeof FONT_MOTIVES)[number];
6 |
7 | type SupportedFontElement =
8 | | 'h1'
9 | | 'h2'
10 | | 'h3'
11 | | 'h4'
12 | | 'h5'
13 | | 'h6'
14 | | 'span'
15 | | 'b'
16 | | 'strong'
17 | | 'i'
18 | | 'p'
19 | | 'em';
20 |
21 | type VariantElementMap = Record;
22 |
23 | interface FontProps {
24 | className?: string;
25 | element?: SupportedFontElement;
26 | variant: FontVariant;
27 | children: ReactNode;
28 | italic?: boolean;
29 | motive?: FontMotive;
30 | bold?: boolean;
31 | }
32 |
33 | export type {
34 | FontProps,
35 | FontVariant,
36 | SupportedFontElement,
37 | VariantElementMap,
38 | FontMotive,
39 | };
40 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/font/font.tsx:
--------------------------------------------------------------------------------
1 | import type { FontProps, VariantElementMap } from './defs';
2 |
3 | import { createElement } from 'react';
4 | import c from 'classnames';
5 |
6 | const FONT_VARIANT_ELEMENT_MAP: VariantElementMap = {
7 | h1: 'h1',
8 | h2: 'h2',
9 | h3: 'h3',
10 | h4: 'h4',
11 | h5: 'h5',
12 | h6: 'h6',
13 | b1: 'p',
14 | b2: 'p',
15 | };
16 |
17 | const Font = ({
18 | className,
19 | variant,
20 | children,
21 | element,
22 | italic,
23 | motive = 'default',
24 | bold,
25 | }: FontProps) => {
26 | return createElement(element ?? FONT_VARIANT_ELEMENT_MAP[variant], {
27 | className: c('font', variant, motive, className, { italic }, { bold }),
28 | children,
29 | });
30 | };
31 |
32 | export { Font };
33 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/font/index.ts:
--------------------------------------------------------------------------------
1 | export * from './font';
2 | export * from './defs';
3 | export * from './consts';
4 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/footer/defs.ts:
--------------------------------------------------------------------------------
1 | import type { ReactNode } from 'react';
2 |
3 | interface FooterProps {
4 | className?: string;
5 | socials: ReactNode;
6 | blocks: ReactNode;
7 | logo: ReactNode;
8 | }
9 |
10 | export type { FooterProps };
11 |
--------------------------------------------------------------------------------
/system/libs/figa-ui/src/lib/footer/footer.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Story, Meta } from '@storybook/react';
2 |
3 | import { Footer } from './footer';
4 | import { Font } from '../font';
5 |
6 | export default {
7 | component: Footer,
8 | title: 'Footer',
9 | } as Meta;
10 |
11 | const Template: Story = () => {
12 | return (
13 |