├── packages ├── admin │ ├── src │ │ ├── setupTests.js │ │ ├── react-app-env.d.ts │ │ ├── user │ │ │ ├── presentation │ │ │ │ └── images │ │ │ │ │ └── login.png │ │ │ └── domain │ │ │ │ ├── Errors.ts │ │ │ │ ├── Boundaries.ts │ │ │ │ ├── RemoveCurrentUserUseCase.ts │ │ │ │ ├── GetCurrentUserUseCase.ts │ │ │ │ └── LoginUseCase.ts │ │ ├── common │ │ │ ├── presentation │ │ │ │ ├── layouts │ │ │ │ │ ├── logo.png │ │ │ │ │ ├── minimal │ │ │ │ │ │ ├── MinimalLayout.tsx │ │ │ │ │ │ └── Topbar.tsx │ │ │ │ │ └── main │ │ │ │ │ │ └── Footer.tsx │ │ │ │ ├── pages │ │ │ │ │ └── not-found.png │ │ │ │ ├── bloc │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── basicActions.ts │ │ │ │ │ ├── BlocBuilder.tsx │ │ │ │ │ └── Bloc.ts │ │ │ │ ├── state │ │ │ │ │ ├── DetailPageState.ts │ │ │ │ │ ├── ListPageState.ts │ │ │ │ │ └── ListState.ts │ │ │ │ └── components │ │ │ │ │ ├── add-fab-button │ │ │ │ │ └── AddFabButton.tsx │ │ │ │ │ └── form-builder │ │ │ │ │ └── TagsTextField.tsx │ │ │ ├── domain │ │ │ │ ├── utils.ts │ │ │ │ └── Errors.ts │ │ │ ├── data │ │ │ │ ├── TokenLocalStorage.ts │ │ │ │ └── Base64ImageConverter.ts │ │ │ └── testing │ │ │ │ ├── mocks │ │ │ │ └── MockTokenStorage.ts │ │ │ │ ├── scenarios │ │ │ │ └── GenericScenarios.ts │ │ │ │ └── testing_library │ │ │ │ └── RedirectTester.tsx │ │ ├── app │ │ │ ├── AppState.tsx │ │ │ ├── theme │ │ │ │ ├── overrides │ │ │ │ │ ├── MuiTypography.ts │ │ │ │ │ ├── MuiPaper.ts │ │ │ │ │ ├── MuiTableHead.ts │ │ │ │ │ ├── MuiFormLabel.ts │ │ │ │ │ ├── MuiTableCell.ts │ │ │ │ │ ├── MuiButton.ts │ │ │ │ │ ├── MuiIconButton.ts │ │ │ │ │ ├── MuiOutlinedInput.ts │ │ │ │ │ ├── MuiTableRow.ts │ │ │ │ │ └── index.ts │ │ │ │ └── index.tsx │ │ │ ├── __test__ │ │ │ │ └── App.spec.tsx │ │ │ ├── App.css │ │ │ ├── AppContext.tsx │ │ │ └── App.tsx │ │ ├── notifications │ │ │ ├── domain │ │ │ │ ├── Errors.ts │ │ │ │ ├── Boundaries.ts │ │ │ │ └── SendPushNotificationUseCase.ts │ │ │ └── presentation │ │ │ │ ├── SendUrlNotificationPage.tsx │ │ │ │ ├── SendVideoNotificationPage.tsx │ │ │ │ └── SendRankingNotificationPage.tsx │ │ ├── rankings │ │ │ ├── domain │ │ │ │ ├── Boundaries.ts │ │ │ │ └── GetRankingsUseCase.ts │ │ │ ├── data │ │ │ │ └── RankingApiRepository.ts │ │ │ └── RankingDIModule.ts │ │ ├── dashboard │ │ │ └── presentation │ │ │ │ └── DashboardPage.tsx │ │ ├── events │ │ │ ├── domain │ │ │ │ ├── SaveEventUseCase.ts │ │ │ │ ├── Boundaries.ts │ │ │ │ ├── GetEventsUseCase.ts │ │ │ │ ├── DeleteEventUseCase.ts │ │ │ │ └── GetEventByIdUseCase.ts │ │ │ ├── presentation │ │ │ │ ├── event-detail │ │ │ │ │ ├── EventDetailPage.tsx │ │ │ │ │ └── __tests__ │ │ │ │ │ │ └── data.json │ │ │ │ └── event-list │ │ │ │ │ ├── EventListPage.tsx │ │ │ │ │ └── __test__ │ │ │ │ │ └── EventListPage.spec.tsx │ │ │ └── data │ │ │ │ └── EventApiRepository.ts │ │ ├── videos │ │ │ ├── domain │ │ │ │ ├── SaveVideoUseCase.ts │ │ │ │ ├── Boundaries.ts │ │ │ │ ├── GetVideosUseCase.ts │ │ │ │ ├── DeleteVideoUseCase.ts │ │ │ │ ├── GetVideoByIdUseCase.ts │ │ │ │ └── DuplicateVideoUseCase.ts │ │ │ ├── presentation │ │ │ │ ├── video-detail │ │ │ │ │ └── VideoDetailPage.tsx │ │ │ │ └── video-list │ │ │ │ │ └── VideoListPage.tsx │ │ │ └── data │ │ │ │ └── VideoApiRepository.ts │ │ ├── categories │ │ │ ├── domain │ │ │ │ ├── SaveCategoryUseCase.ts │ │ │ │ ├── Boundaries.ts │ │ │ │ ├── GetCategoriesUseCase.ts │ │ │ │ ├── DeleteCategoryUseCase.ts │ │ │ │ └── GetCategoryByIdUseCase.ts │ │ │ ├── presentation │ │ │ │ ├── category-detail │ │ │ │ │ ├── CategoryDetailPage.tsx │ │ │ │ │ └── __tests__ │ │ │ │ │ │ └── data.json │ │ │ │ └── category-list │ │ │ │ │ ├── CategoryListPage.tsx │ │ │ │ │ └── __test__ │ │ │ │ │ └── CategoryListPage.spec.tsx │ │ │ └── data │ │ │ │ └── CategoryApiRepository.ts │ │ ├── countries │ │ │ ├── presentation │ │ │ │ ├── country-detail │ │ │ │ │ └── CountryDetailPage.tsx │ │ │ │ └── country-list │ │ │ │ │ ├── CountryListPage.tsx │ │ │ │ │ └── __test__ │ │ │ │ │ └── CountryListPage.spec.tsx │ │ │ ├── domain │ │ │ │ ├── Boundaries.ts │ │ │ │ ├── GetCountriesUseCase.ts │ │ │ │ ├── DeleteCountryUseCase.ts │ │ │ │ ├── GetCountryByIdUseCase.ts │ │ │ │ └── SaveCountryUseCase.ts │ │ │ └── data │ │ │ │ └── CountryApiRepository.ts │ │ ├── news │ │ │ ├── presentation │ │ │ │ ├── news-feed-detail │ │ │ │ │ └── NewsFeedDetailPage.tsx │ │ │ │ └── news-feed-list │ │ │ │ │ ├── NewsFeedListPage.tsx │ │ │ │ │ └── __test__ │ │ │ │ │ └── NewsFeedListPage.spec.tsx │ │ │ ├── domain │ │ │ │ ├── Boundaries.ts │ │ │ │ ├── GetNewsFeedsUseCase.ts │ │ │ │ ├── DeleteNewsFeedsUseCase.ts │ │ │ │ ├── GetNewsFeedByIdUseCase.ts │ │ │ │ └── SaveNewsFeedUseCase.ts │ │ │ └── data │ │ │ │ └── NewsFeedApiRepository.ts │ │ ├── event-types │ │ │ ├── domain │ │ │ │ ├── SaveEventTypeUseCase.ts │ │ │ │ ├── Boundaries.ts │ │ │ │ ├── GetEventTypesUseCase.ts │ │ │ │ ├── DeleteEventTypeUseCase.ts │ │ │ │ └── GetEventTypeByIdUseCase.ts │ │ │ ├── presentation │ │ │ │ ├── event-type-list │ │ │ │ │ ├── EventTypeListPage.tsx │ │ │ │ │ └── __test__ │ │ │ │ │ │ └── EventListPage.spec.tsx │ │ │ │ └── event-type-detail │ │ │ │ │ └── EventTypeDetailPage.tsx │ │ │ └── data │ │ │ │ └── EventTypeApiRepository.ts │ │ ├── competitors │ │ │ ├── presentation │ │ │ │ ├── compeltitor-detail │ │ │ │ │ └── CompetitorDetailPage.tsx │ │ │ │ └── compeltitor-list │ │ │ │ │ └── CompetitorListPage.tsx │ │ │ ├── domain │ │ │ │ ├── Boundaries.ts │ │ │ │ ├── GetCompetitorsUseCase.ts │ │ │ │ ├── DeleteCompetitorUseCase.ts │ │ │ │ ├── GetCompetitorByIdUseCase.ts │ │ │ │ └── SaveCompetitorUseCase.ts │ │ │ └── data │ │ │ │ └── CompetitorApiRepository.ts │ │ ├── category-types │ │ │ ├── domain │ │ │ │ ├── SaveCategoryTypeUseCase.ts │ │ │ │ ├── Boundaries.ts │ │ │ │ ├── GetCategoryTypesUseCase.ts │ │ │ │ ├── DeleteCategoryTypeUseCase.ts │ │ │ │ └── GetCategoryTypeByIdUseCase.ts │ │ │ ├── presentation │ │ │ │ ├── category-type-detail │ │ │ │ │ └── CategoryTypeDetailPage.tsx │ │ │ │ └── category-type-list │ │ │ │ │ ├── CategoryTypeListPage.tsx │ │ │ │ │ └── __test__ │ │ │ │ │ └── CategoryTypeListPage.spec.tsx │ │ │ └── data │ │ │ │ └── CategoryTypeApiRepository.ts │ │ └── index.tsx │ ├── cypress.json │ ├── public │ │ ├── robots.txt │ │ ├── favicon.ico │ │ ├── logo192.png │ │ ├── logo512.png │ │ └── manifest.json │ ├── cypress │ │ ├── fixtures │ │ │ └── example.json │ │ ├── support │ │ │ ├── commands.d.ts │ │ │ ├── index.ts │ │ │ └── commands.js │ │ ├── tsconfig.json │ │ ├── integration │ │ │ ├── countryList.spec.ts │ │ │ ├── competitorList.spec.ts │ │ │ ├── categoryTypeList.spec.ts │ │ │ ├── evenTypeList.spec.ts │ │ │ ├── VideoList.spec.ts │ │ │ ├── categoryList.spec.ts │ │ │ ├── newsFeedList.spec.ts │ │ │ ├── evenList.spec.ts │ │ │ ├── sendPushNotification.spec.ts │ │ │ ├── login.spec.ts │ │ │ ├── eventTypeDetail.spec.ts │ │ │ ├── categoryTypeDetail.spec.ts │ │ │ ├── countryDetail.spec.ts │ │ │ └── categoryDetail.spec.ts │ │ └── plugins │ │ │ └── index.js │ ├── tsconfig.json │ └── .eslintrc.js ├── core │ ├── src │ │ ├── utils │ │ │ ├── index.ts │ │ │ ├── arr.ts │ │ │ └── validations.ts │ │ ├── entities │ │ │ ├── video │ │ │ │ └── index.ts │ │ │ ├── ranking │ │ │ │ └── index.ts │ │ │ ├── competitor │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ └── Entity.ts │ │ ├── index.ts │ │ ├── value-objects │ │ │ ├── ValueObject.ts │ │ │ ├── Password.ts │ │ │ ├── __tests__ │ │ │ │ ├── Password.spec.ts │ │ │ │ └── Email.spec.ts │ │ │ ├── ImageUrl.ts │ │ │ └── Email.ts │ │ └── types │ │ │ ├── Errors.ts │ │ │ └── MaybeAsync.ts │ ├── tsconfig.json │ ├── package.json │ └── README.md └── server │ ├── src │ ├── common │ │ ├── data │ │ │ ├── Types.ts │ │ │ ├── utils.ts │ │ │ └── MongoConector.ts │ │ ├── api │ │ │ ├── ActionResult.ts │ │ │ ├── Errors.ts │ │ │ └── testUtils │ │ │ │ ├── DataCreator.ts │ │ │ │ ├── FakeUserRepository.ts │ │ │ │ ├── FakeImageRepository.ts │ │ │ │ └── jsonParser.ts │ │ └── domain │ │ │ └── utils.ts │ ├── authentication │ │ ├── PermisionError.ts │ │ └── JwtDefaultAuthenticator.ts │ ├── event-types │ │ ├── api │ │ │ ├── eventTypeSchema.ts │ │ │ └── __tests__ │ │ │ │ └── data.json │ │ ├── domain │ │ │ ├── boundaries │ │ │ │ └── EventTypeRepository.ts │ │ │ └── usecases │ │ │ │ ├── GetEventTypesUseCase.ts │ │ │ │ ├── GetEventTypeByIdUseCase.ts │ │ │ │ ├── CreateEventTypeUseCase.ts │ │ │ │ └── UpdateEventTypeUseCase.ts │ │ └── data │ │ │ └── EventTypeMongoRepository.ts │ ├── category-types │ │ ├── api │ │ │ ├── categoryTypeSchema.ts │ │ │ └── __tests__ │ │ │ │ └── data.json │ │ ├── domain │ │ │ ├── boundaries │ │ │ │ └── CategoryTypeRepository.ts │ │ │ └── usecases │ │ │ │ ├── GetCategoryTypesUseCase.ts │ │ │ │ ├── GetCategoryTypeByIdUseCase.ts │ │ │ │ └── CreateCategoryTypeUseCase.ts │ │ └── data │ │ │ └── CategoryTypeMongoRepository.ts │ ├── index.ts │ ├── settings │ │ ├── domain │ │ │ ├── boundaries │ │ │ │ └── SettingsRepository.ts │ │ │ ├── entities │ │ │ │ └── Settings.ts │ │ │ └── usecases │ │ │ │ └── GetSettingsUseCase.ts │ │ └── data │ │ │ ├── SettingsDB.ts │ │ │ └── SettingsMongoRepository.ts │ ├── ranking │ │ ├── domain │ │ │ ├── boundaries │ │ │ │ ├── RankingRepository.ts │ │ │ │ └── RankingEntryRepository.ts │ │ │ └── usecases │ │ │ │ ├── GetRankingsUseCase.ts │ │ │ │ └── GetRankingEntriesUseCase.ts │ │ └── api │ │ │ ├── RankingController.ts │ │ │ ├── RankingRoutes.ts │ │ │ ├── RankingEntriesRoutes.ts │ │ │ └── RankingEntryController.ts │ ├── countries │ │ ├── api │ │ │ └── countrySchema.ts │ │ ├── domain │ │ │ ├── boundaries │ │ │ │ └── CountryRepository.ts │ │ │ └── usecases │ │ │ │ ├── GetCountriesUseCase.ts │ │ │ │ ├── GetCountryByIdUseCase.ts │ │ │ │ ├── CreateCountryUseCase.ts │ │ │ │ └── UpdateCountryUseCase.ts │ │ └── data │ │ │ └── CountryMongoRepository.ts │ ├── users │ │ ├── api │ │ │ ├── UserAPI.ts │ │ │ └── UserRoutes.ts │ │ ├── data │ │ │ └── UserDB.ts │ │ └── domain │ │ │ ├── boundaries │ │ │ └── UserRepository.ts │ │ │ └── usecases │ │ │ ├── GetUserByIdUseCase.ts │ │ │ └── GetUserByUsernameAndPasswordUseCase.ts │ ├── events │ │ ├── api │ │ │ └── eventSchema.ts │ │ └── domain │ │ │ ├── usecases │ │ │ ├── GetEventsUseCase.ts │ │ │ └── GetEventByIdUseCase.ts │ │ │ └── boundaries │ │ │ └── EventRepository.ts │ ├── categories │ │ ├── api │ │ │ └── categorySchema.ts │ │ └── domain │ │ │ ├── boundaries │ │ │ └── CategoryRepository.ts │ │ │ └── usecases │ │ │ ├── GetCategoriesUseCase.ts │ │ │ └── GetCategoryByIdUseCase.ts │ ├── newsfeeds │ │ ├── api │ │ │ └── newsFeedSchema.ts │ │ ├── domain │ │ │ ├── boundaries │ │ │ │ └── NewsFeedRepository.ts │ │ │ └── usecases │ │ │ │ ├── GetNewsFeedsUseCase.ts │ │ │ │ └── GetNewsFeedByIdUseCase.ts │ │ └── data │ │ │ └── NewsFeedMongoRepository.ts │ ├── currentnews │ │ ├── domain │ │ │ ├── entities │ │ │ │ └── CurrentNews.ts │ │ │ ├── boundaries │ │ │ │ └── CurrentNewsRepository.ts │ │ │ └── usecases │ │ │ │ └── GetCurrentNewsUseCase.ts │ │ ├── api │ │ │ ├── CurrentNewsController.ts │ │ │ └── CurrentNewsRoutes.ts │ │ └── CurrentNewsDIModule.ts │ ├── socialnews │ │ ├── domain │ │ │ ├── usecases │ │ │ │ └── GetSocialNewsUseCase.ts │ │ │ ├── boundaries │ │ │ │ └── SocialNewsRepository.ts │ │ │ └── entities │ │ │ │ └── SocialNews.ts │ │ ├── api │ │ │ ├── SocialNewsController.ts │ │ │ └── SocialNewsRoutes.ts │ │ └── SocialNewsDIModule.ts │ ├── images │ │ └── domain │ │ │ └── ImageRepository.ts │ ├── news-import │ │ ├── importNews.ts │ │ └── newsImporterFactory.ts │ ├── videos │ │ ├── domain │ │ │ ├── boundaries │ │ │ │ └── VideoRepository.ts │ │ │ └── usecases │ │ │ │ ├── GetVideosUseCase.ts │ │ │ │ ├── GetVideoByIdUseCase.ts │ │ │ │ ├── DeleteVideoUseCase.ts │ │ │ │ └── utils.ts │ │ └── api │ │ │ └── videoSchema.ts │ ├── competitors │ │ ├── domain │ │ │ ├── boundaries │ │ │ │ └── CompetitorRepository.ts │ │ │ └── usecases │ │ │ │ └── GetCompetitorsUseCase.ts │ │ └── api │ │ │ └── competitorSchema.ts │ └── ranking-import │ │ └── importRanking.ts │ ├── tsconfig.json │ ├── .gitignore │ ├── migrations │ └── 20220621143647-create-ranking.ts │ └── migrate-mongo-config.js ├── logo.png ├── .husky └── pre-push ├── .vscode └── settings.json ├── tsconfig.json ├── .prettierrc.js ├── .eslintrc.js └── .gitignore /packages/admin/src/setupTests.js: -------------------------------------------------------------------------------- 1 | jest.setTimeout(180000); 2 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xurxodev/karate-stars-web/HEAD/logo.png -------------------------------------------------------------------------------- /packages/admin/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/core/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./validations"; 2 | export * from "./arr"; 3 | -------------------------------------------------------------------------------- /packages/core/src/entities/video/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Video"; 2 | export * from "./VideoLink"; 3 | -------------------------------------------------------------------------------- /packages/admin/cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseUrl": "http://localhost:3000/admin", 3 | "video": false 4 | } -------------------------------------------------------------------------------- /packages/server/src/common/data/Types.ts: -------------------------------------------------------------------------------- 1 | export type MongoCollection = { 2 | _id: string; 3 | }; 4 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn prettify && yarn lint && yarn test 5 | -------------------------------------------------------------------------------- /packages/admin/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /packages/core/src/entities/ranking/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Ranking"; 2 | export * from "./RankingEntry"; 3 | -------------------------------------------------------------------------------- /packages/admin/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xurxodev/karate-stars-web/HEAD/packages/admin/public/favicon.ico -------------------------------------------------------------------------------- /packages/admin/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xurxodev/karate-stars-web/HEAD/packages/admin/public/logo192.png -------------------------------------------------------------------------------- /packages/admin/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xurxodev/karate-stars-web/HEAD/packages/admin/public/logo512.png -------------------------------------------------------------------------------- /packages/server/src/common/api/ActionResult.ts: -------------------------------------------------------------------------------- 1 | export interface ActionResult { 2 | ok: boolean; 3 | count: number; 4 | } 5 | -------------------------------------------------------------------------------- /packages/core/src/entities/competitor/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Competitor"; 2 | export * from "./Achievement"; 3 | export * from "./SocialLink"; 4 | -------------------------------------------------------------------------------- /packages/server/src/authentication/PermisionError.ts: -------------------------------------------------------------------------------- 1 | export type PermissionError = { 2 | kind: "PermissionError"; 3 | message: string; 4 | }; 5 | -------------------------------------------------------------------------------- /packages/admin/src/user/presentation/images/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xurxodev/karate-stars-web/HEAD/packages/admin/src/user/presentation/images/login.png -------------------------------------------------------------------------------- /packages/admin/src/common/presentation/layouts/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xurxodev/karate-stars-web/HEAD/packages/admin/src/common/presentation/layouts/logo.png -------------------------------------------------------------------------------- /packages/admin/src/common/presentation/pages/not-found.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xurxodev/karate-stars-web/HEAD/packages/admin/src/common/presentation/pages/not-found.png -------------------------------------------------------------------------------- /packages/admin/src/common/presentation/bloc/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Bloc"; 2 | 3 | export { default as Bloc } from "./Bloc"; 4 | export { default as BlocBuilder } from "./BlocBuilder"; 5 | -------------------------------------------------------------------------------- /packages/admin/src/app/AppState.tsx: -------------------------------------------------------------------------------- 1 | import { User } from "karate-stars-core"; 2 | 3 | export default interface AppState { 4 | currentUser?: User; 5 | isAuthenticated?: boolean; 6 | } 7 | -------------------------------------------------------------------------------- /packages/admin/src/app/theme/overrides/MuiTypography.ts: -------------------------------------------------------------------------------- 1 | const overrideStyles = { 2 | gutterBottom: { 3 | marginBottom: 8, 4 | }, 5 | }; 6 | 7 | export default overrideStyles; 8 | -------------------------------------------------------------------------------- /packages/server/src/event-types/api/eventTypeSchema.ts: -------------------------------------------------------------------------------- 1 | import Joi from "joi"; 2 | 3 | export const eventTypeSchema = Joi.object({ 4 | id: Joi.string(), 5 | name: Joi.string(), 6 | }); 7 | -------------------------------------------------------------------------------- /packages/server/src/category-types/api/categoryTypeSchema.ts: -------------------------------------------------------------------------------- 1 | import Joi from "joi"; 2 | 3 | export const categoryTypeSchema = Joi.object({ 4 | id: Joi.string(), 5 | name: Joi.string(), 6 | }); 7 | -------------------------------------------------------------------------------- /packages/admin/src/user/domain/Errors.ts: -------------------------------------------------------------------------------- 1 | import { ApiError, UnexpectedError, Unauthorized } from "../../common/domain/Errors"; 2 | 3 | export type GetUserError = ApiError | UnexpectedError | Unauthorized; 4 | -------------------------------------------------------------------------------- /packages/admin/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } -------------------------------------------------------------------------------- /packages/server/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as CompositionRoot from "./CompositionRoot"; 2 | import { Server } from "./server"; 3 | 4 | CompositionRoot.init(); 5 | 6 | const server = new Server(); 7 | server.start(); 8 | -------------------------------------------------------------------------------- /packages/server/src/settings/domain/boundaries/SettingsRepository.ts: -------------------------------------------------------------------------------- 1 | import { Settings } from "../entities/Settings"; 2 | 3 | export default interface SettingsRepository { 4 | get(): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /packages/server/src/common/data/utils.ts: -------------------------------------------------------------------------------- 1 | export function renameProp(oldProp: string, newProp: string, { [oldProp]: old, ...others }) { 2 | return { 3 | [newProp]: old, 4 | ...others, 5 | }; 6 | } 7 | -------------------------------------------------------------------------------- /packages/server/src/ranking/domain/boundaries/RankingRepository.ts: -------------------------------------------------------------------------------- 1 | import { Ranking } from "karate-stars-core"; 2 | 3 | export default interface RankingRepository { 4 | getAll(toImport?: true): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /packages/admin/src/notifications/domain/Errors.ts: -------------------------------------------------------------------------------- 1 | import { ApiError, UnexpectedError, Unauthorized } from "../../common/domain/Errors"; 2 | 3 | export type SendPushNotificationError = ApiError | UnexpectedError | Unauthorized; 4 | -------------------------------------------------------------------------------- /packages/server/src/settings/data/SettingsDB.ts: -------------------------------------------------------------------------------- 1 | export interface SettingsDB { 2 | _id: string; 3 | socialNews: SettingsSocialNewsDB; 4 | } 5 | 6 | export interface SettingsSocialNewsDB { 7 | search: string; 8 | } 9 | -------------------------------------------------------------------------------- /packages/admin/src/app/theme/overrides/MuiPaper.ts: -------------------------------------------------------------------------------- 1 | const overrideStyles = { 2 | elevation1: { 3 | boxShadow: "0 0 0 1px rgba(63,63,68,0.05), 0 1px 3px 0 rgba(63,63,68,0.15)", 4 | }, 5 | }; 6 | 7 | export default overrideStyles; 8 | -------------------------------------------------------------------------------- /packages/server/src/countries/api/countrySchema.ts: -------------------------------------------------------------------------------- 1 | import Joi from "joi"; 2 | 3 | export const countrySchema = Joi.object({ 4 | id: Joi.string(), 5 | name: Joi.string(), 6 | iso2: Joi.string().max(2), 7 | image: Joi.string(), 8 | }); 9 | -------------------------------------------------------------------------------- /packages/server/src/users/api/UserAPI.ts: -------------------------------------------------------------------------------- 1 | export interface UserAPI { 2 | id: string; 3 | name: string; 4 | image: string; 5 | email: string; 6 | password: string; 7 | isAdmin: boolean; 8 | isClientUser: boolean; 9 | } 10 | -------------------------------------------------------------------------------- /packages/server/src/users/data/UserDB.ts: -------------------------------------------------------------------------------- 1 | export interface UserDB { 2 | _id: string; 3 | name: string; 4 | image: string; 5 | username: string; 6 | password: string; 7 | isAdmin: boolean; 8 | isClientUser: boolean; 9 | } 10 | -------------------------------------------------------------------------------- /packages/admin/src/app/theme/overrides/MuiTableHead.ts: -------------------------------------------------------------------------------- 1 | import { colors } from "@material-ui/core"; 2 | 3 | const overrideColors = { 4 | root: { 5 | backgroundColor: colors.grey[50], 6 | }, 7 | }; 8 | 9 | export default overrideColors; 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "prettier.eslintIntegration": true, 4 | "eslint.validate": [ 5 | "javascript", 6 | "javascriptreact", 7 | "typescript", 8 | "typescriptreact" 9 | ] 10 | } -------------------------------------------------------------------------------- /packages/admin/src/rankings/domain/Boundaries.ts: -------------------------------------------------------------------------------- 1 | import { Either, Ranking } from "karate-stars-core"; 2 | import { DataError } from "../../common/domain/Errors"; 3 | 4 | export interface RankingRepository { 5 | getAll(): Promise>; 6 | } 7 | -------------------------------------------------------------------------------- /packages/admin/cypress/support/commands.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace Cypress { 2 | interface Chainable { 3 | /** 4 | * Custom command to realize login in API. 5 | * @example cy.login('greeting') 6 | */ 7 | login(): Chainable 8 | } 9 | } -------------------------------------------------------------------------------- /packages/server/src/settings/domain/entities/Settings.ts: -------------------------------------------------------------------------------- 1 | import { Id } from "karate-stars-core"; 2 | 3 | export interface Settings { 4 | identifier: Id; 5 | socialNews: SettingsSocialNews; 6 | } 7 | 8 | export interface SettingsSocialNews { 9 | search: string; 10 | } 11 | -------------------------------------------------------------------------------- /packages/admin/src/app/theme/overrides/MuiFormLabel.ts: -------------------------------------------------------------------------------- 1 | import palette from "../palette"; 2 | 3 | const overrideStyles = { 4 | root: { 5 | "&$focused": { 6 | color: palette.text.primary, 7 | }, 8 | }, 9 | }; 10 | 11 | export default overrideStyles; 12 | -------------------------------------------------------------------------------- /packages/server/src/events/api/eventSchema.ts: -------------------------------------------------------------------------------- 1 | import Joi from "joi"; 2 | 3 | export const eventSchema = Joi.object({ 4 | id: Joi.string(), 5 | name: Joi.string(), 6 | typeId: Joi.string(), 7 | startDate: Joi.date(), 8 | endDate: Joi.date(), 9 | url: Joi.string().optional(), 10 | }); 11 | -------------------------------------------------------------------------------- /packages/admin/src/app/theme/overrides/MuiTableCell.ts: -------------------------------------------------------------------------------- 1 | import palette from "../palette"; 2 | import typography from "../typography"; 3 | 4 | const styles = { 5 | root: { 6 | ...typography.body1, 7 | borderBottom: `1px solid ${palette.divider}`, 8 | }, 9 | }; 10 | 11 | export default styles; 12 | -------------------------------------------------------------------------------- /packages/server/src/users/domain/boundaries/UserRepository.ts: -------------------------------------------------------------------------------- 1 | import { User, Maybe, Id, Email, Password } from "karate-stars-core"; 2 | 3 | export default interface UserRepository<> { 4 | getByUsernameAndPassword(username: Email, password: Password): Promise>; 5 | getById(userId: Id): Promise>; 6 | } 7 | -------------------------------------------------------------------------------- /packages/admin/src/app/theme/overrides/MuiButton.ts: -------------------------------------------------------------------------------- 1 | const overrideStyles = { 2 | contained: { 3 | boxShadow: 4 | "0 1px 1px 0 rgba(0,0,0,0.14), 0 2px 1px -1px rgba(0,0,0,0.12), 0 1px 3px 0 rgba(0,0,0,0.20)", 5 | backgroundColor: "#FFFFFF", 6 | }, 7 | }; 8 | 9 | export default overrideStyles; 10 | -------------------------------------------------------------------------------- /packages/server/src/categories/api/categorySchema.ts: -------------------------------------------------------------------------------- 1 | import Joi from "joi"; 2 | 3 | export const categorySchema = Joi.object({ 4 | id: Joi.string(), 5 | name: Joi.string(), 6 | typeId: Joi.string(), 7 | wkfId: Joi.string().allow(null), 8 | paraKarate: Joi.boolean(), 9 | main: Joi.boolean(), 10 | }); 11 | -------------------------------------------------------------------------------- /packages/admin/src/app/__test__/App.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import App from "../App"; 4 | 5 | it("renders without crashing", () => { 6 | const div = document.createElement("div"); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /packages/admin/src/app/theme/overrides/MuiIconButton.ts: -------------------------------------------------------------------------------- 1 | //import palette from '../palette'; 2 | 3 | const overrideStyles = { 4 | root: { 5 | //color: palette.icon, 6 | "&:hover": { 7 | backgroundColor: "rgba(0, 0, 0, 0.03)", 8 | }, 9 | }, 10 | }; 11 | 12 | export default overrideStyles; 13 | -------------------------------------------------------------------------------- /packages/admin/src/app/theme/overrides/MuiOutlinedInput.ts: -------------------------------------------------------------------------------- 1 | import palette from "../palette"; 2 | 3 | const overrideStyles = { 4 | root: { 5 | "&$focused $notchedOutline": { 6 | borderColor: palette.text.primary, 7 | borderWidth: 2, 8 | }, 9 | }, 10 | }; 11 | 12 | export default overrideStyles; 13 | -------------------------------------------------------------------------------- /packages/core/src/utils/arr.ts: -------------------------------------------------------------------------------- 1 | function chunk(array: T[], chunkSize: number): T[][] { 2 | const res = []; 3 | for (let i = 0; i < array.length; i += chunkSize) { 4 | const chunk = array.slice(i, i + chunkSize); 5 | res.push(chunk); 6 | } 7 | return res; 8 | } 9 | 10 | export const arr = { 11 | chunk, 12 | }; 13 | -------------------------------------------------------------------------------- /packages/admin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "noEmit": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "module": "esnext", 12 | "jsx": "react-jsx" 13 | }, 14 | "include": [ 15 | "src", 16 | ], 17 | } -------------------------------------------------------------------------------- /packages/server/src/newsfeeds/api/newsFeedSchema.ts: -------------------------------------------------------------------------------- 1 | import Joi from "joi"; 2 | 3 | export const newsFeedSchema = Joi.object({ 4 | id: Joi.string(), 5 | name: Joi.string(), 6 | language: Joi.string(), 7 | type: Joi.string().valid("rss", "atom"), 8 | image: Joi.string(), 9 | url: Joi.string(), 10 | categories: Joi.array().items(Joi.string()), 11 | }); 12 | -------------------------------------------------------------------------------- /packages/admin/src/user/domain/Boundaries.ts: -------------------------------------------------------------------------------- 1 | import { Either, User, Credentials } from "karate-stars-core"; 2 | import { GetUserError } from "./Errors"; 3 | 4 | export default interface UserRepository { 5 | getByEmailAndPassword(credentials: Credentials): Promise>; 6 | getCurrent(): Promise>; 7 | removeCurrent(): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "allowJs": false, 5 | "skipLibCheck": true, 6 | "esModuleInterop": true, 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "moduleResolution": "node", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true 13 | }, 14 | } -------------------------------------------------------------------------------- /packages/core/src/entities/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./competitor"; 2 | export * from "./Category"; 3 | export * from "./CategoryType"; 4 | export * from "./Country"; 5 | export { Entity, EntityData } from "./Entity"; 6 | export * from "./Event"; 7 | export * from "./EventType"; 8 | export * from "./NewsFeed"; 9 | export * from "./User"; 10 | export * from "./video"; 11 | export * from "./ranking"; 12 | -------------------------------------------------------------------------------- /packages/admin/src/dashboard/presentation/DashboardPage.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import MainLayout from "../../common/presentation/layouts/main/MainLayout"; 3 | 4 | const DashboardPage: React.FC = () => { 5 | return ( 6 | 7 |

Dashboard!!!

8 |
9 | ); 10 | }; 11 | 12 | export default DashboardPage; 13 | -------------------------------------------------------------------------------- /packages/server/src/settings/domain/usecases/GetSettingsUseCase.ts: -------------------------------------------------------------------------------- 1 | import SettingsRepository from "../boundaries/SettingsRepository"; 2 | import { Settings } from "../entities/Settings"; 3 | 4 | export default class GetSettingsUseCase { 5 | constructor(private repository: SettingsRepository) {} 6 | 7 | public execute(): Promise { 8 | return this.repository.get(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/admin/cypress/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "baseUrl": "../node_modules", 5 | "target": "es5", 6 | "lib": [ 7 | "es5", 8 | "dom" 9 | ], 10 | "types": [ 11 | "cypress", 12 | "@testing-library/cypress" 13 | ] 14 | }, 15 | "include": [ 16 | "**/*.ts" 17 | ] 18 | } -------------------------------------------------------------------------------- /packages/admin/src/app/App.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | html { 8 | height: 100%; 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | } 12 | 13 | body { 14 | background-color: #f4f6f8; 15 | height: 100%; 16 | } 17 | 18 | a { 19 | text-decoration: none; 20 | } 21 | 22 | #root { 23 | height: 100%; 24 | } 25 | -------------------------------------------------------------------------------- /packages/server/src/currentnews/domain/entities/CurrentNews.ts: -------------------------------------------------------------------------------- 1 | export interface CurrentNews { 2 | summary: CurrentNewsSummary; 3 | source: NewsSource; 4 | } 5 | 6 | export interface CurrentNewsSummary { 7 | title: string; 8 | image: string | undefined; 9 | date: string; 10 | link: string; 11 | } 12 | 13 | export interface NewsSource { 14 | name: string; 15 | image: string; 16 | url: string; 17 | } 18 | -------------------------------------------------------------------------------- /packages/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "lib": [ 5 | "esnext" 6 | ], 7 | "outDir": "./build", 8 | "sourceMap": true, 9 | "noImplicitAny": false, 10 | "esModuleInterop": true, 11 | "module": "commonjs", 12 | }, 13 | "include": [ 14 | "./src/**/*" 15 | ], 16 | "exclude": [ 17 | "node_modules", 18 | "**/*.spec.ts" 19 | ] 20 | } -------------------------------------------------------------------------------- /packages/admin/src/user/domain/RemoveCurrentUserUseCase.ts: -------------------------------------------------------------------------------- 1 | import UserRepository from "./Boundaries"; 2 | 3 | export default class RemoveCurrentUserUseCase { 4 | private userRepository: UserRepository; 5 | 6 | constructor(userRepository: UserRepository) { 7 | this.userRepository = userRepository; 8 | } 9 | 10 | execute(): Promise { 11 | return this.userRepository.removeCurrent(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | module.exports = { 4 | printWidth: 100, 5 | tabWidth: 4, 6 | useTabs: false, 7 | semi: true, 8 | singleQuote: false, 9 | trailingComma: "es5", 10 | bracketSpacing: true, 11 | bracketSameLine: true, 12 | arrowParens: "avoid", 13 | rangeStart: 0, 14 | rangeEnd: Infinity, 15 | proseWrap: "preserve", 16 | requirePragma: false, 17 | insertPragma: false, 18 | }; 19 | -------------------------------------------------------------------------------- /packages/server/src/socialnews/domain/usecases/GetSocialNewsUseCase.ts: -------------------------------------------------------------------------------- 1 | import SocialNewsRepository from "../boundaries/SocialNewsRepository"; 2 | import { SocialNews } from "../entities/SocialNews"; 3 | 4 | export default class GetSocialNewsUseCase { 5 | constructor(private socialNewsRepository: SocialNewsRepository) {} 6 | 7 | public async execute(): Promise { 8 | return this.socialNewsRepository.get(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/admin/src/app/theme/overrides/MuiTableRow.ts: -------------------------------------------------------------------------------- 1 | import palette from "../palette"; 2 | 3 | const overridePalette = { 4 | root: { 5 | "&$selected": { 6 | backgroundColor: palette.background.default, 7 | }, 8 | "&$hover": { 9 | "&:hover": { 10 | backgroundColor: palette.background.default, 11 | }, 12 | }, 13 | }, 14 | }; 15 | 16 | export default overridePalette; 17 | -------------------------------------------------------------------------------- /packages/admin/src/common/domain/utils.ts: -------------------------------------------------------------------------------- 1 | import { EitherAsync, Id } from "karate-stars-core"; 2 | import { DataError } from "./Errors"; 3 | 4 | export function createIdOrUnexpectedError(id: string): EitherAsync { 5 | const error = { 6 | kind: "UnexpectedError", 7 | message: new Error(`Id ${id} not found`), 8 | } as DataError; 9 | 10 | return EitherAsync.fromEither(Id.createExisted(id)).mapLeft(() => error); 11 | } 12 | -------------------------------------------------------------------------------- /packages/admin/src/common/data/TokenLocalStorage.ts: -------------------------------------------------------------------------------- 1 | export interface TokenStorage { 2 | get(): string | null; 3 | save(token: string): void; 4 | } 5 | 6 | export class TokenLocalStorage implements TokenStorage { 7 | tokenKey = "apiToken"; 8 | 9 | get(): string | null { 10 | return localStorage.getItem(this.tokenKey); 11 | } 12 | save(token: string): void { 13 | localStorage.setItem(this.tokenKey, token); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/admin/src/app/AppContext.tsx: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import React from "react"; 3 | import AppBloc from "./AppBloc"; 4 | 5 | export const AppBlocContext = React.createContext(undefined); 6 | 7 | export function useAppBlocContext() { 8 | const context = useContext(AppBlocContext); 9 | if (context) { 10 | return context; 11 | } else { 12 | throw new Error("AppContext not found"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/admin/src/app/theme/index.tsx: -------------------------------------------------------------------------------- 1 | import { createTheme } from "@material-ui/core/styles"; 2 | import palette from "./palette"; 3 | import typography from "./typography"; 4 | import overrides from "./overrides"; 5 | 6 | // A custom theme for this app 7 | const theme = createTheme({ 8 | palette, 9 | typography, 10 | overrides, 11 | zIndex: { 12 | appBar: 1200, 13 | drawer: 1100, 14 | }, 15 | }); 16 | 17 | export default theme; 18 | -------------------------------------------------------------------------------- /packages/admin/src/notifications/domain/Boundaries.ts: -------------------------------------------------------------------------------- 1 | import { SendPushNotificationError } from "./Errors"; 2 | import { Notification } from "./entities/Notification"; 3 | import { Either } from "karate-stars-core"; 4 | 5 | export type SendPushNotificationSuccess = true; 6 | 7 | export interface PushNotificationRepository { 8 | send( 9 | notification: Notification 10 | ): Promise>; 11 | } 12 | -------------------------------------------------------------------------------- /packages/admin/src/events/domain/SaveEventUseCase.ts: -------------------------------------------------------------------------------- 1 | import { EventRepository } from "./Boundaries"; 2 | import { Either, Event } from "karate-stars-core"; 3 | import { DataError } from "../../common/domain/Errors"; 4 | 5 | export default class SaveEventUseCase { 6 | constructor(private eventRepository: EventRepository) {} 7 | 8 | async execute(entity: Event): Promise> { 9 | return this.eventRepository.save(entity); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/admin/src/videos/domain/SaveVideoUseCase.ts: -------------------------------------------------------------------------------- 1 | import { VideoRepository } from "./Boundaries"; 2 | import { Either, Video } from "karate-stars-core"; 3 | import { DataError } from "../../common/domain/Errors"; 4 | 5 | export default class SaveVideoUseCase { 6 | constructor(private videoRepository: VideoRepository) {} 7 | 8 | async execute(entity: Video): Promise> { 9 | return this.videoRepository.save(entity); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/server/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | build 11 | 12 | tsconfig.tsbuildinfo 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | firebase-admin.config.json 26 | -------------------------------------------------------------------------------- /packages/admin/src/events/presentation/event-detail/EventDetailPage.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import DetailPage from "../../../common/presentation/components/detail-page/DetailPage"; 3 | import { di } from "../../../CompositionRoot"; 4 | import EventDetailBloc from "./EventDetailBloc"; 5 | 6 | const EventDetailPage: React.FC = () => { 7 | const bloc = di.get(EventDetailBloc); 8 | 9 | return ; 10 | }; 11 | 12 | export default EventDetailPage; 13 | -------------------------------------------------------------------------------- /packages/admin/src/events/presentation/event-list/EventListPage.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { di } from "../../../CompositionRoot"; 3 | import EventListBloc from "./EventListBloc"; 4 | import ListPage from "../../../common/presentation/components/list-page/ListPage"; 5 | 6 | const EventListPage: React.FC = () => { 7 | const bloc = di.get(EventListBloc); 8 | 9 | return ; 10 | }; 11 | 12 | export default EventListPage; 13 | -------------------------------------------------------------------------------- /packages/admin/src/videos/presentation/video-detail/VideoDetailPage.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import DetailPage from "../../../common/presentation/components/detail-page/DetailPage"; 3 | import { di } from "../../../CompositionRoot"; 4 | import VideoDetailBloc from "./VideoDetailBloc"; 5 | 6 | const VideoDetailPage: React.FC = () => { 7 | const bloc = di.get(VideoDetailBloc); 8 | 9 | return ; 10 | }; 11 | 12 | export default VideoDetailPage; 13 | -------------------------------------------------------------------------------- /packages/admin/src/videos/presentation/video-list/VideoListPage.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { di } from "../../../CompositionRoot"; 3 | import VideoListBloc from "./VideoListBloc"; 4 | import ListPage from "../../../common/presentation/components/list-page/ListPage"; 5 | 6 | const VideoListPage: React.FC = () => { 7 | const bloc = di.get(VideoListBloc); 8 | 9 | return ; 10 | }; 11 | 12 | export default VideoListPage; 13 | -------------------------------------------------------------------------------- /packages/admin/src/categories/domain/SaveCategoryUseCase.ts: -------------------------------------------------------------------------------- 1 | import { CategoryRepository } from "./Boundaries"; 2 | import { Either, Category } from "karate-stars-core"; 3 | import { DataError } from "../../common/domain/Errors"; 4 | 5 | export default class SaveCategoryUseCase { 6 | constructor(private CategoryRepository: CategoryRepository) {} 7 | 8 | async execute(entity: Category): Promise> { 9 | return this.CategoryRepository.save(entity); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/admin/src/countries/presentation/country-detail/CountryDetailPage.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import DetailPage from "../../../common/presentation/components/detail-page/DetailPage"; 3 | import { di } from "../../../CompositionRoot"; 4 | import CountryDetailBloc from "./CountryDetailBloc"; 5 | 6 | const CountryDetailPage: React.FC = () => { 7 | const bloc = di.get(CountryDetailBloc); 8 | 9 | return ; 10 | }; 11 | 12 | export default CountryDetailPage; 13 | -------------------------------------------------------------------------------- /packages/admin/src/countries/presentation/country-list/CountryListPage.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { di } from "../../../CompositionRoot"; 3 | import CountryListBloc from "./CountryListBloc"; 4 | import ListPage from "../../../common/presentation/components/list-page/ListPage"; 5 | 6 | const CountryListPage: React.FC = () => { 7 | const bloc = di.get(CountryListBloc); 8 | 9 | return ; 10 | }; 11 | 12 | export default CountryListPage; 13 | -------------------------------------------------------------------------------- /packages/admin/src/news/presentation/news-feed-detail/NewsFeedDetailPage.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import DetailPage from "../../../common/presentation/components/detail-page/DetailPage"; 3 | import { di } from "../../../CompositionRoot"; 4 | import NewsFeedDetailBloc from "./NewsFeedDetailBloc"; 5 | 6 | const NewsFeedDetailPage: React.FC = () => { 7 | const bloc = di.get(NewsFeedDetailBloc); 8 | 9 | return ; 10 | }; 11 | 12 | export default NewsFeedDetailPage; 13 | -------------------------------------------------------------------------------- /packages/admin/src/news/presentation/news-feed-list/NewsFeedListPage.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { di } from "../../../CompositionRoot"; 3 | import NewsFeedListBloc from "./NewsFeedListBloc"; 4 | import ListPage from "../../../common/presentation/components/list-page/ListPage"; 5 | 6 | const NewsFeedListPage: React.FC = () => { 7 | const bloc = di.get(NewsFeedListBloc); 8 | 9 | return ; 10 | }; 11 | 12 | export default NewsFeedListPage; 13 | -------------------------------------------------------------------------------- /packages/admin/src/categories/presentation/category-detail/CategoryDetailPage.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import DetailPage from "../../../common/presentation/components/detail-page/DetailPage"; 3 | import { di } from "../../../CompositionRoot"; 4 | import CategoryDetailBloc from "./CategoryDetailBloc"; 5 | 6 | const CategoryDetailPage: React.FC = () => { 7 | const bloc = di.get(CategoryDetailBloc); 8 | 9 | return ; 10 | }; 11 | 12 | export default CategoryDetailPage; 13 | -------------------------------------------------------------------------------- /packages/admin/src/categories/presentation/category-list/CategoryListPage.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { di } from "../../../CompositionRoot"; 3 | import CategoryListBloc from "./CategoryListBloc"; 4 | import ListPage from "../../../common/presentation/components/list-page/ListPage"; 5 | 6 | const CategoryListPage: React.FC = () => { 7 | const bloc = di.get(CategoryListBloc); 8 | 9 | return ; 10 | }; 11 | 12 | export default CategoryListPage; 13 | -------------------------------------------------------------------------------- /packages/admin/src/common/domain/Errors.ts: -------------------------------------------------------------------------------- 1 | export interface ApiError { 2 | kind: "ApiError"; 3 | error: string; 4 | statusCode: number; 5 | message: string; 6 | } 7 | 8 | export interface UnexpectedError { 9 | kind: "UnexpectedError"; 10 | message: Error; 11 | } 12 | 13 | export interface Unauthorized { 14 | kind: "Unauthorized"; 15 | } 16 | 17 | export interface NotFound { 18 | kind: "NotFound"; 19 | } 20 | 21 | export type DataError = ApiError | UnexpectedError | Unauthorized; 22 | -------------------------------------------------------------------------------- /packages/admin/src/event-types/domain/SaveEventTypeUseCase.ts: -------------------------------------------------------------------------------- 1 | import { EventTypeRepository } from "./Boundaries"; 2 | import { Either, EventType } from "karate-stars-core"; 3 | import { DataError } from "../../common/domain/Errors"; 4 | 5 | export default class SaveEventTypeUseCase { 6 | constructor(private eventTypeRepository: EventTypeRepository) {} 7 | 8 | async execute(entity: EventType): Promise> { 9 | return this.eventTypeRepository.save(entity); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/admin/cypress/integration/countryList.spec.ts: -------------------------------------------------------------------------------- 1 | describe("Countries page", () => { 2 | beforeEach(() => { 3 | cy.login(); 4 | cy.visit("#/countries"); 5 | }); 6 | 7 | it("should show rows", () => { 8 | cy.findAllByRole("row").should("have.length.greaterThan", 1); 9 | }); 10 | 11 | it("should search by text", () => { 12 | cy.findByPlaceholderText("Search ...").type("Spain"); 13 | cy.findAllByRole("row").should("have.length", 2); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/admin/src/event-types/presentation/event-type-list/EventTypeListPage.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { di } from "../../../CompositionRoot"; 3 | import EventListBloc from "./EventTypeListBloc"; 4 | import ListPage from "../../../common/presentation/components/list-page/ListPage"; 5 | 6 | const EventTypeListPage: React.FC = () => { 7 | const bloc = di.get(EventListBloc); 8 | 9 | return ; 10 | }; 11 | 12 | export default EventTypeListPage; 13 | -------------------------------------------------------------------------------- /packages/admin/src/event-types/presentation/event-type-detail/EventTypeDetailPage.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import DetailPage from "../../../common/presentation/components/detail-page/DetailPage"; 3 | import { di } from "../../../CompositionRoot"; 4 | import EventTypeDetailBloc from "./EventTypeDetailBloc"; 5 | 6 | const EventTypeDetailPage: React.FC = () => { 7 | const bloc = di.get(EventTypeDetailBloc); 8 | 9 | return ; 10 | }; 11 | 12 | export default EventTypeDetailPage; 13 | -------------------------------------------------------------------------------- /packages/admin/cypress/integration/competitorList.spec.ts: -------------------------------------------------------------------------------- 1 | describe("Competitors page", () => { 2 | beforeEach(() => { 3 | cy.login(); 4 | cy.visit("#/competitors"); 5 | }); 6 | 7 | it("should show rows", () => { 8 | cy.findAllByRole("row").should("have.length.greaterThan", 1); 9 | }); 10 | 11 | it("should search by text", () => { 12 | cy.findByPlaceholderText("Search ...").type("Aghayev"); 13 | cy.findAllByRole("row").should("have.length", 2); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/admin/src/competitors/presentation/compeltitor-detail/CompetitorDetailPage.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import DetailPage from "../../../common/presentation/components/detail-page/DetailPage"; 3 | import { di } from "../../../CompositionRoot"; 4 | import CompetitorDetailBloc from "./CompetitorDetailBloc"; 5 | 6 | const CompetitorDetailPage: React.FC = () => { 7 | const bloc = di.get(CompetitorDetailBloc); 8 | 9 | return ; 10 | }; 11 | 12 | export default CompetitorDetailPage; 13 | -------------------------------------------------------------------------------- /packages/admin/src/events/domain/Boundaries.ts: -------------------------------------------------------------------------------- 1 | import { Either, Id, Event } from "karate-stars-core"; 2 | import { DataError } from "../../common/domain/Errors"; 3 | 4 | export interface EventRepository { 5 | getAll(): Promise>; 6 | getById(id: Id): Promise>; 7 | deleteById(id: Id): Promise>; 8 | save(entity: Event): Promise>; 9 | saveImage(entityId: Id, file: File): Promise>; 10 | } 11 | -------------------------------------------------------------------------------- /packages/admin/src/videos/domain/Boundaries.ts: -------------------------------------------------------------------------------- 1 | import { Either, Id, Video } from "karate-stars-core"; 2 | import { DataError } from "../../common/domain/Errors"; 3 | 4 | export interface VideoRepository { 5 | getAll(): Promise>; 6 | getById(id: Id): Promise>; 7 | deleteById(id: Id): Promise>; 8 | save(entity: Video): Promise>; 9 | saveImage(entityId: Id, file: File): Promise>; 10 | } 11 | -------------------------------------------------------------------------------- /packages/admin/cypress/integration/categoryTypeList.spec.ts: -------------------------------------------------------------------------------- 1 | describe("Category Types page", () => { 2 | beforeEach(() => { 3 | cy.login(); 4 | cy.visit("#/category-types"); 5 | }); 6 | 7 | it("should show rows", () => { 8 | cy.findAllByRole("row").should("have.length.greaterThan", 1); 9 | }); 10 | 11 | it("should search by text", () => { 12 | cy.findByPlaceholderText("Search ...").type("Kumite"); 13 | cy.findAllByRole("row").should("have.length", 2); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/admin/cypress/integration/evenTypeList.spec.ts: -------------------------------------------------------------------------------- 1 | describe("Events page", () => { 2 | beforeEach(() => { 3 | cy.login(); 4 | cy.visit("#/event-types"); 5 | }); 6 | 7 | it("should show rows", () => { 8 | cy.findAllByRole("row").should("have.length.greaterThan", 1); 9 | }); 10 | 11 | it("should search by text", () => { 12 | cy.findByPlaceholderText("Search ...").type("World Championships"); 13 | cy.findAllByRole("row").should("have.length", 2); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/server/src/common/domain/utils.ts: -------------------------------------------------------------------------------- 1 | import { EitherAsync, Id } from "karate-stars-core"; 2 | import { ResourceNotFoundError } from "../api/Errors"; 3 | 4 | export function createIdOrResourceNotFound( 5 | id: string 6 | ): EitherAsync { 7 | const notFoundError = { 8 | kind: "ResourceNotFound", 9 | message: `Id ${id} not found`, 10 | } as ResourceNotFoundError; 11 | 12 | return EitherAsync.fromEither(Id.createExisted(id)).mapLeft(() => notFoundError); 13 | } 14 | -------------------------------------------------------------------------------- /packages/admin/cypress/integration/VideoList.spec.ts: -------------------------------------------------------------------------------- 1 | describe("Videos page", () => { 2 | beforeEach(() => { 3 | cy.login(); 4 | cy.visit("#/videos"); 5 | }); 6 | 7 | it("should show rows", () => { 8 | cy.findAllByRole("row").should("have.length.greaterThan", 1); 9 | }); 10 | 11 | it("should search by text", () => { 12 | cy.findByPlaceholderText("Search ...").type("A. Biamonti (FRA) - B. Kandaz (TUR)"); 13 | cy.findAllByRole("row").should("have.length", 2); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/admin/cypress/integration/categoryList.spec.ts: -------------------------------------------------------------------------------- 1 | describe("Categories page", () => { 2 | beforeEach(() => { 3 | cy.login(); 4 | cy.visit("#/categories"); 5 | }); 6 | 7 | it("should show rows", () => { 8 | cy.findAllByRole("row").should("have.length.greaterThan", 1); 9 | }); 10 | 11 | it("should search by text", () => { 12 | cy.findByPlaceholderText("Search ...").type("Female Kumite -50 Kg"); 13 | cy.findAllByRole("row").should("have.length", 2); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/admin/cypress/integration/newsFeedList.spec.ts: -------------------------------------------------------------------------------- 1 | describe("News Feeds page", () => { 2 | beforeEach(() => { 3 | cy.login(); 4 | cy.visit("#/news-feeds"); 5 | }); 6 | 7 | it("should show news feeds", () => { 8 | cy.findAllByRole("row").should("have.length.greaterThan", 1); 9 | }); 10 | 11 | it("should search by text", () => { 12 | cy.findByPlaceholderText("Search ...").type("WKF News Center"); 13 | cy.findAllByRole("row").should("have.length", 2); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/admin/src/category-types/domain/SaveCategoryTypeUseCase.ts: -------------------------------------------------------------------------------- 1 | import { CategoryTypeRepository } from "./Boundaries"; 2 | import { Either, CategoryType } from "karate-stars-core"; 3 | import { DataError } from "../../common/domain/Errors"; 4 | 5 | export default class SaveCategoryTypeUseCase { 6 | constructor(private CategoryTypeRepository: CategoryTypeRepository) {} 7 | 8 | async execute(entity: CategoryType): Promise> { 9 | return this.CategoryTypeRepository.save(entity); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/admin/src/countries/domain/Boundaries.ts: -------------------------------------------------------------------------------- 1 | import { Either, Id, Country } from "karate-stars-core"; 2 | import { DataError } from "../../common/domain/Errors"; 3 | 4 | export interface CountryRepository { 5 | getAll(): Promise>; 6 | getById(id: Id): Promise>; 7 | deleteById(id: Id): Promise>; 8 | save(entity: Country): Promise>; 9 | saveImage(entityId: Id, file: File): Promise>; 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./build", 4 | "rootDir": "./src", 5 | "composite": true, 6 | "declaration": true, 7 | "allowJs": false, 8 | "target": "es5", 9 | "sourceMap": true, 10 | "module": "commonjs", 11 | "moduleResolution": "node", 12 | "esModuleInterop": true, 13 | "lib": [ 14 | "esnext" 15 | ], 16 | }, 17 | "include": [ 18 | "./src/**/*" 19 | ], 20 | "exclude": [ 21 | "node_modules", 22 | "**/*.spec.ts" 23 | ], 24 | } -------------------------------------------------------------------------------- /packages/admin/cypress/integration/evenList.spec.ts: -------------------------------------------------------------------------------- 1 | describe("Event Types page", () => { 2 | beforeEach(() => { 3 | cy.login(); 4 | cy.visit("#/events"); 5 | }); 6 | 7 | it("should show rows", () => { 8 | cy.findAllByRole("row").should("have.length.greaterThan", 1); 9 | }); 10 | 11 | it("should search by text", () => { 12 | cy.findByPlaceholderText("Search ...").type("World Championships Granada 1992"); 13 | cy.findAllByRole("row").should("have.length", 2); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/admin/src/category-types/presentation/category-type-detail/CategoryTypeDetailPage.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import DetailPage from "../../../common/presentation/components/detail-page/DetailPage"; 3 | import { di } from "../../../CompositionRoot"; 4 | import CategoryTypeDetailBloc from "./CategoryTypeDetailBloc"; 5 | 6 | const CategoryTypeDetailPage: React.FC = () => { 7 | const bloc = di.get(CategoryTypeDetailBloc); 8 | 9 | return ; 10 | }; 11 | 12 | export default CategoryTypeDetailPage; 13 | -------------------------------------------------------------------------------- /packages/admin/src/news/domain/Boundaries.ts: -------------------------------------------------------------------------------- 1 | import { Either, Id, NewsFeed } from "karate-stars-core"; 2 | import { DataError } from "../../common/domain/Errors"; 3 | 4 | export interface NewsFeedRepository { 5 | getAll(): Promise>; 6 | getById(id: Id): Promise>; 7 | deleteById(id: Id): Promise>; 8 | save(newsFeed: NewsFeed): Promise>; 9 | saveImage(newsFeedId: Id, file: File): Promise>; 10 | } 11 | -------------------------------------------------------------------------------- /packages/admin/src/categories/domain/Boundaries.ts: -------------------------------------------------------------------------------- 1 | import { Either, Id, Category } from "karate-stars-core"; 2 | import { DataError } from "../../common/domain/Errors"; 3 | 4 | export interface CategoryRepository { 5 | getAll(): Promise>; 6 | getById(id: Id): Promise>; 7 | deleteById(id: Id): Promise>; 8 | save(entity: Category): Promise>; 9 | saveImage(entityId: Id, file: File): Promise>; 10 | } 11 | -------------------------------------------------------------------------------- /packages/admin/src/category-types/presentation/category-type-list/CategoryTypeListPage.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { di } from "../../../CompositionRoot"; 3 | import CategoryTypeListBloc from "./CategoryTypeListBloc"; 4 | import ListPage from "../../../common/presentation/components/list-page/ListPage"; 5 | 6 | const CategoryTypeListPage: React.FC = () => { 7 | const bloc = di.get(CategoryTypeListBloc); 8 | 9 | return ; 10 | }; 11 | 12 | export default CategoryTypeListPage; 13 | -------------------------------------------------------------------------------- /packages/admin/src/event-types/domain/Boundaries.ts: -------------------------------------------------------------------------------- 1 | import { Either, Id, EventType } from "karate-stars-core"; 2 | import { DataError } from "../../common/domain/Errors"; 3 | 4 | export interface EventTypeRepository { 5 | getAll(): Promise>; 6 | getById(id: Id): Promise>; 7 | deleteById(id: Id): Promise>; 8 | save(entity: EventType): Promise>; 9 | saveImage(entityId: Id, file: File): Promise>; 10 | } 11 | -------------------------------------------------------------------------------- /packages/admin/src/user/domain/GetCurrentUserUseCase.ts: -------------------------------------------------------------------------------- 1 | import { GetUserError } from "./Errors"; 2 | import { Either, User } from "karate-stars-core"; 3 | import UserRepository from "./Boundaries"; 4 | 5 | export default class GetCurrentUserUseCase { 6 | private userRepository: UserRepository; 7 | 8 | constructor(userRepository: UserRepository) { 9 | this.userRepository = userRepository; 10 | } 11 | 12 | execute(): Promise> { 13 | return this.userRepository.getCurrent(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/admin/src/competitors/domain/Boundaries.ts: -------------------------------------------------------------------------------- 1 | import { Either, Id, Competitor } from "karate-stars-core"; 2 | import { DataError } from "../../common/domain/Errors"; 3 | 4 | export interface CompetitorRepository { 5 | getAll(): Promise>; 6 | getById(id: Id): Promise>; 7 | deleteById(id: Id): Promise>; 8 | save(competirtor: Competitor): Promise>; 9 | saveImage(newsFeedId: Id, file: File): Promise>; 10 | } 11 | -------------------------------------------------------------------------------- /packages/admin/src/category-types/domain/Boundaries.ts: -------------------------------------------------------------------------------- 1 | import { Either, Id, CategoryType } from "karate-stars-core"; 2 | import { DataError } from "../../common/domain/Errors"; 3 | 4 | export interface CategoryTypeRepository { 5 | getAll(): Promise>; 6 | getById(id: Id): Promise>; 7 | deleteById(id: Id): Promise>; 8 | save(entity: CategoryType): Promise>; 9 | saveImage(entityId: Id, file: File): Promise>; 10 | } 11 | -------------------------------------------------------------------------------- /packages/admin/src/common/presentation/state/DetailPageState.ts: -------------------------------------------------------------------------------- 1 | import { FormState } from "./FormState"; 2 | 3 | export interface DetailLoadingState { 4 | kind: "DetailLoadingState"; 5 | } 6 | 7 | export interface DetailFormUpdatedState { 8 | kind: "DetailFormUpdatedState"; 9 | form: FormState; 10 | } 11 | 12 | export interface DetailErrorState { 13 | kind: "DetailErrorState"; 14 | message: string; 15 | } 16 | 17 | export type DetailPageState = (DetailLoadingState | DetailFormUpdatedState | DetailErrorState) & { 18 | title: string; 19 | }; 20 | -------------------------------------------------------------------------------- /packages/admin/src/events/domain/GetEventsUseCase.ts: -------------------------------------------------------------------------------- 1 | import { EventRepository } from "./Boundaries"; 2 | import { EventData, Either } from "karate-stars-core"; 3 | import { DataError } from "../../common/domain/Errors"; 4 | 5 | export default class GetEventsUseCase { 6 | constructor(private eventRepository: EventRepository) {} 7 | 8 | async execute(): Promise> { 9 | const response = await this.eventRepository.getAll(); 10 | 11 | return response.map(items => items.map(item => item.toData())); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/admin/src/videos/domain/GetVideosUseCase.ts: -------------------------------------------------------------------------------- 1 | import { VideoRepository } from "./Boundaries"; 2 | import { VideoData, Either } from "karate-stars-core"; 3 | import { DataError } from "../../common/domain/Errors"; 4 | 5 | export default class GetVideosUseCase { 6 | constructor(private videoRepository: VideoRepository) {} 7 | 8 | async execute(): Promise> { 9 | const response = await this.videoRepository.getAll(); 10 | 11 | return response.map(items => items.map(item => item.toData())); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/server/src/socialnews/domain/boundaries/SocialNewsRepository.ts: -------------------------------------------------------------------------------- 1 | import { Either } from "karate-stars-core"; 2 | import { ActionResult } from "../../../common/api/ActionResult"; 3 | import { UnexpectedError } from "../../../common/api/Errors"; 4 | import { SocialNews } from "../entities/SocialNews"; 5 | 6 | export default interface SocialNewsRepository<> { 7 | get(): Promise; 8 | } 9 | 10 | export interface SocialNewsWritableRepository<> { 11 | replaceAll(entities: SocialNews[]): Promise>; 12 | } 13 | -------------------------------------------------------------------------------- /packages/server/src/ranking/domain/boundaries/RankingEntryRepository.ts: -------------------------------------------------------------------------------- 1 | import { Either, Id, RankingEntry } from "karate-stars-core"; 2 | import { ActionResult } from "../../../common/api/ActionResult"; 3 | import { UnexpectedError } from "../../../common/api/Errors"; 4 | 5 | export default interface RankingEntryReadableRepository { 6 | get(rankingId: Id, categoryId: Id): Promise; 7 | } 8 | 9 | export interface RankingEntryWritableRepository { 10 | replaceAll(rankingEntries: RankingEntry[]): Promise>; 11 | } 12 | -------------------------------------------------------------------------------- /packages/server/src/socialnews/domain/entities/SocialNews.ts: -------------------------------------------------------------------------------- 1 | export interface SocialNews { 2 | summary: SocialNewsSummary; 3 | network: Network; 4 | user: SocialUser; 5 | } 6 | 7 | export interface SocialNewsSummary { 8 | title: string; 9 | image: string | undefined; 10 | video: string | undefined; 11 | date: string; 12 | link: string; 13 | } 14 | 15 | export interface SocialUser { 16 | name: string; 17 | image: string; 18 | url: string; 19 | userName: string; 20 | } 21 | 22 | export type Network = "twitter" | "instagram"; 23 | -------------------------------------------------------------------------------- /packages/server/src/images/domain/ImageRepository.ts: -------------------------------------------------------------------------------- 1 | import { Either } from "karate-stars-core"; 2 | import { Stream } from "stream"; 3 | import { UnexpectedError } from "../../common/api/Errors"; 4 | 5 | export type ImageType = "competitors" | "feeds" | "flags"; 6 | 7 | export interface ImageRepository { 8 | uploadNewImage( 9 | type: ImageType, 10 | filename: string, 11 | stream: Stream 12 | ): Promise>; 13 | 14 | deleteImage(type: ImageType, filename: string): Promise>; 15 | } 16 | -------------------------------------------------------------------------------- /packages/admin/src/countries/domain/GetCountriesUseCase.ts: -------------------------------------------------------------------------------- 1 | import { CountryRepository } from "./Boundaries"; 2 | import { CountryData, Either } from "karate-stars-core"; 3 | import { DataError } from "../../common/domain/Errors"; 4 | 5 | export default class GetCountriesUseCase { 6 | constructor(private CountryRepository: CountryRepository) {} 7 | 8 | async execute(): Promise> { 9 | const response = await this.CountryRepository.getAll(); 10 | 11 | return response.map(items => items.map(item => item.toData())); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/admin/src/news/domain/GetNewsFeedsUseCase.ts: -------------------------------------------------------------------------------- 1 | import { NewsFeedRepository } from "./Boundaries"; 2 | import { Either, NewsFeedData } from "karate-stars-core"; 3 | import { DataError } from "../../common/domain/Errors"; 4 | 5 | export default class GetNewsFeedsUseCase { 6 | constructor(private newsFeedRepository: NewsFeedRepository) {} 7 | 8 | async execute(): Promise> { 9 | const response = await this.newsFeedRepository.getAll(); 10 | 11 | return response.map(feeds => feeds.map(feed => feed.toData())); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/admin/src/rankings/domain/GetRankingsUseCase.ts: -------------------------------------------------------------------------------- 1 | import { RankingRepository } from "./Boundaries"; 2 | import { Either, RankingData } from "karate-stars-core"; 3 | import { DataError } from "../../common/domain/Errors"; 4 | 5 | export default class GetRankingsUseCase { 6 | constructor(private rankingRepository: RankingRepository) {} 7 | 8 | async execute(): Promise> { 9 | const response = await this.rankingRepository.getAll(); 10 | 11 | return response.map(items => items.map(item => item.toData())); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/admin/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import App from "./app/App"; 4 | 5 | //import reportWebVitals from "./reportWebVitals"; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById("root") 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | //reportWebVitals(); 18 | -------------------------------------------------------------------------------- /packages/admin/src/user/domain/LoginUseCase.ts: -------------------------------------------------------------------------------- 1 | import { GetUserError } from "./Errors"; 2 | import { Either, User, Credentials } from "karate-stars-core"; 3 | import UserRepository from "./Boundaries"; 4 | 5 | export default class LoginUseCase { 6 | private userRepository: UserRepository; 7 | 8 | constructor(userRepository: UserRepository) { 9 | this.userRepository = userRepository; 10 | } 11 | 12 | execute(credentials: Credentials): Promise> { 13 | return this.userRepository.getByEmailAndPassword(credentials); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/server/src/currentnews/domain/boundaries/CurrentNewsRepository.ts: -------------------------------------------------------------------------------- 1 | import { Either, NewsFeed } from "karate-stars-core"; 2 | import { ActionResult } from "../../../common/api/ActionResult"; 3 | import { UnexpectedError } from "../../../common/api/Errors"; 4 | import { CurrentNews } from "../entities/CurrentNews"; 5 | 6 | export interface CurrentNewsRepository { 7 | get(feeds: NewsFeed[]): Promise; 8 | } 9 | 10 | export interface CurrentNewsWritableRepository<> { 11 | replaceAll(entities: CurrentNews[]): Promise>; 12 | } 13 | -------------------------------------------------------------------------------- /packages/admin/src/categories/domain/GetCategoriesUseCase.ts: -------------------------------------------------------------------------------- 1 | import { CategoryRepository } from "./Boundaries"; 2 | import { CategoryData, Either } from "karate-stars-core"; 3 | import { DataError } from "../../common/domain/Errors"; 4 | 5 | export default class GetCategoriesUseCase { 6 | constructor(private CategoryRepository: CategoryRepository) {} 7 | 8 | async execute(): Promise> { 9 | const response = await this.CategoryRepository.getAll(); 10 | 11 | return response.map(items => items.map(item => item.toData())); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/admin/src/competitors/presentation/compeltitor-list/CompetitorListPage.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { di } from "../../../CompositionRoot"; 3 | import CompetitorListBloc from "./CompetitorListBloc"; 4 | import ListPage from "../../../common/presentation/components/list-page/ListPage"; 5 | import { withRouter } from "react-router-dom"; 6 | 7 | const CompetitorListPage: React.FC = () => { 8 | const bloc = di.get(CompetitorListBloc); 9 | 10 | return ; 11 | }; 12 | 13 | export default withRouter(CompetitorListPage); 14 | -------------------------------------------------------------------------------- /packages/server/src/common/data/MongoConector.ts: -------------------------------------------------------------------------------- 1 | import { Db, MongoClient } from "mongodb"; 2 | 3 | export class MongoConector { 4 | mongoClient: MongoClient; 5 | cachedDB: Db | null = null; 6 | 7 | constructor(private mongodbConecction: string) { 8 | this.mongoClient = new MongoClient(this.mongodbConecction); 9 | } 10 | 11 | async db(): Promise { 12 | if (!this.cachedDB) { 13 | await this.mongoClient.connect(); 14 | } 15 | 16 | this.cachedDB = this.mongoClient.db(); 17 | 18 | return this.cachedDB; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/admin/src/event-types/domain/GetEventTypesUseCase.ts: -------------------------------------------------------------------------------- 1 | import { EventTypeRepository } from "./Boundaries"; 2 | import { EventTypeData, Either } from "karate-stars-core"; 3 | import { DataError } from "../../common/domain/Errors"; 4 | 5 | export default class GetEventTypesUseCase { 6 | constructor(private eventTypeRepository: EventTypeRepository) {} 7 | 8 | async execute(): Promise> { 9 | const response = await this.eventTypeRepository.getAll(); 10 | 11 | return response.map(items => items.map(item => item.toData())); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/server/src/events/domain/usecases/GetEventsUseCase.ts: -------------------------------------------------------------------------------- 1 | import { Either, EventData } from "karate-stars-core"; 2 | import { UnexpectedError } from "../../../common/api/Errors"; 3 | 4 | import EventRepository from "../boundaries/EventRepository"; 5 | 6 | export class GetEventsUseCase { 7 | constructor(private eventRepository: EventRepository) {} 8 | 9 | public async execute(): Promise> { 10 | const Events = await this.eventRepository.getAll(); 11 | 12 | return Either.right(Events.map(entity => entity.toData())); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/server/src/news-import/importNews.ts: -------------------------------------------------------------------------------- 1 | import { newsImporterFactory } from "./newsImporterFactory"; 2 | 3 | export interface NewsImporter { 4 | execute(): Promise; 5 | } 6 | 7 | async function execute() { 8 | const strategies = process.env.IMPORT_NEWS_STRATEGY?.split(",") ?? []; 9 | 10 | const importers = newsImporterFactory.createStrategies(strategies); 11 | 12 | for (const importer of importers) { 13 | await importer.execute(); 14 | } 15 | 16 | console.log("Import news finished successfully!!"); 17 | process.exit(); 18 | } 19 | 20 | execute(); 21 | -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./di/DependencyLocator"; 2 | 3 | export * from "./entities"; 4 | 5 | export * from "./types/Either"; 6 | export * from "./types/EitherAsync"; 7 | export * from "./types/Maybe"; 8 | export * from "./types/MaybeAsync"; 9 | export * from "./types/Errors"; 10 | 11 | export * from "./utils"; 12 | 13 | export * from "./value-objects/ValueObject"; 14 | export * from "./value-objects/Id"; 15 | export * from "./value-objects/Email"; 16 | export * from "./value-objects/Password"; 17 | export * from "./value-objects/Url"; 18 | export * from "./value-objects/Credentials"; 19 | -------------------------------------------------------------------------------- /packages/server/src/events/domain/boundaries/EventRepository.ts: -------------------------------------------------------------------------------- 1 | import { Either, Id, Event } from "karate-stars-core"; 2 | import { ActionResult } from "../../../common/api/ActionResult"; 3 | import { ResourceNotFoundError, UnexpectedError } from "../../../common/api/Errors"; 4 | 5 | export default interface EventRepository { 6 | getAll(): Promise; 7 | getById(id: Id): Promise>; 8 | delete(id: Id): Promise>; 9 | save(entity: Event): Promise>; 10 | } 11 | -------------------------------------------------------------------------------- /packages/server/src/socialnews/api/SocialNewsController.ts: -------------------------------------------------------------------------------- 1 | import * as hapi from "@hapi/hapi"; 2 | import GetSocialNewsUseCase from "../domain/usecases/GetSocialNewsUseCase"; 3 | 4 | export default class SocialNewsController { 5 | private getSocialNewsUseCase: GetSocialNewsUseCase; 6 | 7 | constructor(getSocialNewsUseCase: GetSocialNewsUseCase) { 8 | this.getSocialNewsUseCase = getSocialNewsUseCase; 9 | } 10 | 11 | public get(_request: hapi.Request, _h: hapi.ResponseToolkit): hapi.Lifecycle.ReturnValue { 12 | return this.getSocialNewsUseCase.execute(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/server/src/videos/domain/boundaries/VideoRepository.ts: -------------------------------------------------------------------------------- 1 | import { Either, Id, Video } from "karate-stars-core"; 2 | import { ActionResult } from "../../../common/api/ActionResult"; 3 | import { ResourceNotFoundError, UnexpectedError } from "../../../common/api/Errors"; 4 | 5 | export default interface VideoRepository { 6 | getAll(): Promise; 7 | getById(id: Id): Promise>; 8 | delete(id: Id): Promise>; 9 | save(entity: Video): Promise>; 10 | } 11 | -------------------------------------------------------------------------------- /packages/server/src/videos/domain/usecases/GetVideosUseCase.ts: -------------------------------------------------------------------------------- 1 | import { Either, VideoData } from "karate-stars-core"; 2 | import { UnexpectedError } from "../../../common/api/Errors"; 3 | 4 | import VideoRepository from "../boundaries/VideoRepository"; 5 | 6 | export class GetVideosUseCase { 7 | constructor(private videoRepository: VideoRepository) {} 8 | 9 | public async run(): Promise> { 10 | const categories = await this.videoRepository.getAll(); 11 | 12 | return Either.right(categories.map(entity => entity.toData())); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/admin/src/categories/presentation/category-detail/__tests__/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "categories": [ 3 | { 4 | "id": "wZgo8Vp77gR", 5 | "name": "Female Kumite -68 Kg", 6 | "typeId": "Gps5nVcCdjV", 7 | "paraKarate": false, 8 | "main": true, 9 | "wkfId": "AA" 10 | } 11 | ], 12 | "categoryTypes": [ 13 | { 14 | "id": "Gps5nVcCdjV", 15 | "name": "Kumite" 16 | }, 17 | { 18 | "id": "qWPs4i1e78g", 19 | "name": "Kata" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /packages/admin/src/competitors/domain/GetCompetitorsUseCase.ts: -------------------------------------------------------------------------------- 1 | import { CompetitorRepository } from "./Boundaries"; 2 | import { CompetitorData, Either } from "karate-stars-core"; 3 | import { DataError } from "../../common/domain/Errors"; 4 | 5 | export default class GetCompetitorsUseCase { 6 | constructor(private competitorRepository: CompetitorRepository) {} 7 | 8 | async execute(): Promise> { 9 | const response = await this.competitorRepository.getAll(); 10 | 11 | return response.map(items => items.map(item => item.toData())); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/server/src/currentnews/api/CurrentNewsController.ts: -------------------------------------------------------------------------------- 1 | import * as hapi from "@hapi/hapi"; 2 | import GetCurrentNewsUseCase from "../domain/usecases/GetCurrentNewsUseCase"; 3 | 4 | export default class CurrentNewsController { 5 | private getCurrentNewsUseCase: GetCurrentNewsUseCase; 6 | 7 | constructor(getCurrentNewsUseCase: GetCurrentNewsUseCase) { 8 | this.getCurrentNewsUseCase = getCurrentNewsUseCase; 9 | } 10 | 11 | public get(_request: hapi.Request, _h: hapi.ResponseToolkit): hapi.Lifecycle.ReturnValue { 12 | return this.getCurrentNewsUseCase.execute(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/server/src/countries/domain/boundaries/CountryRepository.ts: -------------------------------------------------------------------------------- 1 | import { Country, Either, Id } from "karate-stars-core"; 2 | import { ActionResult } from "../../../common/api/ActionResult"; 3 | import { ResourceNotFoundError, UnexpectedError } from "../../../common/api/Errors"; 4 | 5 | export default interface CountryRepository { 6 | getAll(): Promise; 7 | getById(id: Id): Promise>; 8 | delete(id: Id): Promise>; 9 | save(entity: Country): Promise>; 10 | } 11 | -------------------------------------------------------------------------------- /packages/admin/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Admin", 3 | "name": "Karate Stars Admin", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": "./admin", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } -------------------------------------------------------------------------------- /packages/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "karate-stars-core", 3 | "version": "0.1.0", 4 | "description": "Karate stars Core. Developed using TypeScript", 5 | "main": "build/index.js", 6 | "repository": "https://github.com/xurxodev/karate-stars-api", 7 | "author": "Jorge Sánchez ", 8 | "license": "GPL-3.0-or-later", 9 | "scripts": { 10 | "build": "tsc --build", 11 | "start-dev": "tsc --watch", 12 | "test": "jest" 13 | }, 14 | "dependencies": {}, 15 | "devDependencies": { 16 | "@types/jest": "^26.0.20", 17 | "jest": "26.6.0", 18 | "ts-jest": "^26.5.3" 19 | } 20 | } -------------------------------------------------------------------------------- /packages/server/src/categories/domain/boundaries/CategoryRepository.ts: -------------------------------------------------------------------------------- 1 | import { Category, Either, Id } from "karate-stars-core"; 2 | import { ActionResult } from "../../../common/api/ActionResult"; 3 | import { ResourceNotFoundError, UnexpectedError } from "../../../common/api/Errors"; 4 | 5 | export default interface CategoryRepository { 6 | getAll(): Promise; 7 | getById(id: Id): Promise>; 8 | delete(id: Id): Promise>; 9 | save(entity: Category): Promise>; 10 | } 11 | -------------------------------------------------------------------------------- /packages/server/src/newsfeeds/domain/boundaries/NewsFeedRepository.ts: -------------------------------------------------------------------------------- 1 | import { Either, Id, NewsFeed } from "karate-stars-core"; 2 | import { ActionResult } from "../../../common/api/ActionResult"; 3 | import { ResourceNotFoundError, UnexpectedError } from "../../../common/api/Errors"; 4 | 5 | export default interface NewsFeedRepository { 6 | getAll(): Promise; 7 | getById(id: Id): Promise>; 8 | delete(id: Id): Promise>; 9 | save(entity: NewsFeed): Promise>; 10 | } 11 | -------------------------------------------------------------------------------- /packages/server/src/ranking/domain/usecases/GetRankingsUseCase.ts: -------------------------------------------------------------------------------- 1 | import { Either, RankingData } from "karate-stars-core"; 2 | import { UnexpectedError } from "../../../common/api/Errors"; 3 | 4 | import RankingRepository from "../boundaries/RankingRepository"; 5 | 6 | export class GetRankingsUseCase { 7 | constructor(private rankingRepository: RankingRepository) {} 8 | 9 | public async execute(): Promise> { 10 | const rankings = await this.rankingRepository.getAll(); 11 | 12 | return Either.right(rankings.map(ranking => ranking.toData())); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/admin/src/category-types/domain/GetCategoryTypesUseCase.ts: -------------------------------------------------------------------------------- 1 | import { CategoryTypeRepository } from "./Boundaries"; 2 | import { CategoryTypeData, Either } from "karate-stars-core"; 3 | import { DataError } from "../../common/domain/Errors"; 4 | 5 | export default class GetCategoryTypesUseCase { 6 | constructor(private CategoryTypeRepository: CategoryTypeRepository) {} 7 | 8 | async execute(): Promise> { 9 | const response = await this.CategoryTypeRepository.getAll(); 10 | 11 | return response.map(items => items.map(item => item.toData())); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/server/src/countries/domain/usecases/GetCountriesUseCase.ts: -------------------------------------------------------------------------------- 1 | import { Either, CountryData } from "karate-stars-core"; 2 | import { UnexpectedError } from "../../../common/api/Errors"; 3 | 4 | import CountryRepository from "../boundaries/CountryRepository"; 5 | 6 | export class GetCountriesUseCase { 7 | constructor(private countryRepository: CountryRepository) {} 8 | 9 | public async execute(): Promise> { 10 | const categories = await this.countryRepository.getAll(); 11 | 12 | return Either.right(categories.map(entity => entity.toData())); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/server/src/event-types/domain/boundaries/EventTypeRepository.ts: -------------------------------------------------------------------------------- 1 | import { Either, Id, EventType } from "karate-stars-core"; 2 | import { ActionResult } from "../../../common/api/ActionResult"; 3 | import { ResourceNotFoundError, UnexpectedError } from "../../../common/api/Errors"; 4 | 5 | export default interface EventTypeRepository { 6 | getAll(): Promise; 7 | getById(id: Id): Promise>; 8 | delete(id: Id): Promise>; 9 | save(entity: EventType): Promise>; 10 | } 11 | -------------------------------------------------------------------------------- /packages/server/src/categories/domain/usecases/GetCategoriesUseCase.ts: -------------------------------------------------------------------------------- 1 | import { Either, CategoryData } from "karate-stars-core"; 2 | import { UnexpectedError } from "../../../common/api/Errors"; 3 | 4 | import CategoryRepository from "../boundaries/CategoryRepository"; 5 | 6 | export class GetCategoriesUseCase { 7 | constructor(private categoryRepository: CategoryRepository) {} 8 | 9 | public async execute(): Promise> { 10 | const categories = await this.categoryRepository.getAll(); 11 | 12 | return Either.right(categories.map(entity => entity.toData())); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/server/src/competitors/domain/boundaries/CompetitorRepository.ts: -------------------------------------------------------------------------------- 1 | import { Competitor, Either, Id } from "karate-stars-core"; 2 | import { ActionResult } from "../../../common/api/ActionResult"; 3 | import { ResourceNotFoundError, UnexpectedError } from "../../../common/api/Errors"; 4 | 5 | export default interface CompetitorRepository { 6 | getAll(): Promise; 7 | getById(id: Id): Promise>; 8 | delete(id: Id): Promise>; 9 | save(entity: Competitor): Promise>; 10 | } 11 | -------------------------------------------------------------------------------- /packages/server/src/users/domain/usecases/GetUserByIdUseCase.ts: -------------------------------------------------------------------------------- 1 | import UserRepository from "../boundaries/UserRepository"; 2 | import { Maybe, UserData, Id } from "karate-stars-core"; 3 | 4 | export default class GetUserByIdUseCase { 5 | private repository: UserRepository; 6 | 7 | constructor(resository: UserRepository) { 8 | this.repository = resository; 9 | } 10 | 11 | public async execute(userId: string): Promise> { 12 | const userResult = await this.repository.getById(Id.createExisted(userId).getOrThrow()); 13 | 14 | return userResult.map(user => user.toData()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/server/src/videos/api/videoSchema.ts: -------------------------------------------------------------------------------- 1 | import Joi from "joi"; 2 | 3 | export const videoLinkSchema = Joi.object({ 4 | id: Joi.string(), 5 | type: Joi.string().valid("youtube", "facebook", "vimeo"), 6 | }); 7 | 8 | export const videoSchema = Joi.object({ 9 | id: Joi.string(), 10 | links: Joi.array().items(videoLinkSchema), 11 | title: Joi.string(), 12 | description: Joi.string(), 13 | subtitle: Joi.string(), 14 | competitors: Joi.array().items(Joi.string()), 15 | eventDate: Joi.date().iso(), 16 | createdDate: Joi.date().iso(), 17 | order: Joi.number(), 18 | isLive: Joi.boolean(), 19 | }); 20 | -------------------------------------------------------------------------------- /packages/admin/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["../../.eslintrc.js", "react-app", "plugin:react/recommended"], 3 | parser: "@typescript-eslint/parser", 4 | env: { 5 | node: true, 6 | }, 7 | parserOptions: { 8 | ecmaFeatures: { 9 | jsx: true, 10 | }, 11 | }, 12 | rules: { 13 | "react/prop-types": "off", 14 | "react-hooks/exhaustive-deps": "off", 15 | "react/jsx-uses-react": "off", 16 | "react/react-in-jsx-scope": "off", 17 | }, 18 | settings: { 19 | react: { 20 | version: "detect", 21 | }, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /packages/server/src/common/api/Errors.ts: -------------------------------------------------------------------------------- 1 | import { ValidationError } from "karate-stars-core"; 2 | 3 | export type UnexpectedError = { 4 | kind: "UnexpectedError"; 5 | error: unknown; 6 | }; 7 | 8 | export type ResourceNotFoundError = { 9 | kind: "ResourceNotFound"; 10 | message: string; 11 | }; 12 | 13 | export type UnauthorizedError = { 14 | kind: "Unauthorized"; 15 | message: string; 16 | }; 17 | 18 | export type ValidationErrors = { 19 | kind: "ValidationErrors"; 20 | errors: ValidationError[]; 21 | }; 22 | 23 | export type ConflictError = { 24 | kind: "ConflictError"; 25 | message: string; 26 | }; 27 | -------------------------------------------------------------------------------- /packages/server/src/common/api/testUtils/DataCreator.ts: -------------------------------------------------------------------------------- 1 | import { Entity, EntityData } from "karate-stars-core"; 2 | 3 | export interface ServerDataCreator< 4 | TEntityData extends EntityData, 5 | TEntity extends Entity 6 | > { 7 | repositoryKey: string; 8 | items: () => TEntity[]; 9 | } 10 | 11 | export interface TestDataCreator { 12 | givenAValidNewItem: () => TEntityData; 13 | givenAInvalidNewItem: () => TEntityData; 14 | givenAValidModifiedItem: () => TEntityData; 15 | givenAInvalidModifiedItem: () => TEntityData; 16 | givenAItemToDelete: () => TEntityData; 17 | } 18 | -------------------------------------------------------------------------------- /packages/server/src/event-types/domain/usecases/GetEventTypesUseCase.ts: -------------------------------------------------------------------------------- 1 | import { Either, EventTypeData } from "karate-stars-core"; 2 | import { UnexpectedError } from "../../../common/api/Errors"; 3 | 4 | import EventTypeRepository from "../boundaries/EventTypeRepository"; 5 | 6 | export class GetEventTypesUseCase { 7 | constructor(private EventTypeRepository: EventTypeRepository) {} 8 | 9 | public async execute(): Promise> { 10 | const EventTypes = await this.EventTypeRepository.getAll(); 11 | 12 | return Either.right(EventTypes.map(entity => entity.toData())); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/server/src/category-types/domain/boundaries/CategoryTypeRepository.ts: -------------------------------------------------------------------------------- 1 | import { Either, Id, CategoryType } from "karate-stars-core"; 2 | import { ActionResult } from "../../../common/api/ActionResult"; 3 | import { ResourceNotFoundError, UnexpectedError } from "../../../common/api/Errors"; 4 | 5 | export default interface CategoryTypeRepository { 6 | getAll(): Promise; 7 | getById(id: Id): Promise>; 8 | delete(id: Id): Promise>; 9 | save(entity: CategoryType): Promise>; 10 | } 11 | -------------------------------------------------------------------------------- /packages/server/src/common/api/testUtils/FakeUserRepository.ts: -------------------------------------------------------------------------------- 1 | import { Email, Maybe, Password, User, UserData } from "karate-stars-core"; 2 | import { FakeGenericRepository } from "./FakeGenericRepository"; 3 | 4 | export class FakeUserRepository extends FakeGenericRepository { 5 | getByUsernameAndPassword(username: Email, password: Password): Promise> { 6 | const result = Maybe.fromValue( 7 | this.items.find( 8 | u => u.email.value === username.value && u.password.value === password.value 9 | ) 10 | ); 11 | 12 | return Promise.resolve(result); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/server/src/competitors/domain/usecases/GetCompetitorsUseCase.ts: -------------------------------------------------------------------------------- 1 | import { Either, CompetitorData } from "karate-stars-core"; 2 | import { UnexpectedError } from "../../../common/api/Errors"; 3 | 4 | import CompetitorRepository from "../boundaries/CompetitorRepository"; 5 | 6 | export class GetCompetitorsUseCase { 7 | constructor(private competitorRepository: CompetitorRepository) {} 8 | 9 | public async execute(): Promise> { 10 | const competitors = await this.competitorRepository.getAll(); 11 | 12 | return Either.right(competitors.map(competitor => competitor.toData())); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/admin/src/app/theme/overrides/index.ts: -------------------------------------------------------------------------------- 1 | import MuiButton from "./MuiButton"; 2 | import MuiIconButton from "./MuiIconButton"; 3 | import MuiPaper from "./MuiPaper"; 4 | import MuiTableCell from "./MuiTableCell"; 5 | import MuiTableHead from "./MuiTableHead"; 6 | import MuiTypography from "./MuiTypography"; 7 | import MuiFormLabel from "./MuiFormLabel"; 8 | import MuiOutlinedInput from "./MuiOutlinedInput"; 9 | 10 | const overrides = { 11 | MuiButton, 12 | MuiFormLabel, 13 | MuiIconButton, 14 | MuiOutlinedInput, 15 | MuiPaper, 16 | MuiTableCell, 17 | MuiTableHead, 18 | MuiTypography, 19 | }; 20 | 21 | export default overrides; 22 | -------------------------------------------------------------------------------- /packages/admin/src/events/domain/DeleteEventUseCase.ts: -------------------------------------------------------------------------------- 1 | import { EventRepository } from "./Boundaries"; 2 | import { Either } from "karate-stars-core"; 3 | import { DataError } from "../../common/domain/Errors"; 4 | import { createIdOrUnexpectedError } from "../../common/domain/utils"; 5 | 6 | export default class DeleteEventUseCase { 7 | constructor(private eventRepository: EventRepository) {} 8 | 9 | async execute(id: string): Promise> { 10 | const result = await createIdOrUnexpectedError(id) 11 | .flatMap(async id => this.eventRepository.deleteById(id)) 12 | .run(); 13 | 14 | return result; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/admin/src/videos/domain/DeleteVideoUseCase.ts: -------------------------------------------------------------------------------- 1 | import { VideoRepository } from "./Boundaries"; 2 | import { Either } from "karate-stars-core"; 3 | import { DataError } from "../../common/domain/Errors"; 4 | import { createIdOrUnexpectedError } from "../../common/domain/utils"; 5 | 6 | export default class DeleteVideoUseCase { 7 | constructor(private videoRepository: VideoRepository) {} 8 | 9 | async execute(id: string): Promise> { 10 | const result = await createIdOrUnexpectedError(id) 11 | .flatMap(async id => this.videoRepository.deleteById(id)) 12 | .run(); 13 | 14 | return result; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/value-objects/ValueObject.ts: -------------------------------------------------------------------------------- 1 | interface ValueObjectProps { 2 | [index: string]: any; 3 | } 4 | 5 | export abstract class ValueObject { 6 | constructor(protected props: T) { 7 | const baseProps: any = { 8 | ...props, 9 | }; 10 | 11 | this.props = baseProps; 12 | } 13 | 14 | public equals(vo?: ValueObject): boolean { 15 | if (vo === null || vo === undefined) { 16 | return false; 17 | } 18 | if (vo.props === undefined) { 19 | return false; 20 | } 21 | return JSON.stringify(this.props) === JSON.stringify(vo.props); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/server/src/category-types/domain/usecases/GetCategoryTypesUseCase.ts: -------------------------------------------------------------------------------- 1 | import { Either, CategoryTypeData } from "karate-stars-core"; 2 | import { UnexpectedError } from "../../../common/api/Errors"; 3 | 4 | import CategoryTypeRepository from "../boundaries/CategoryTypeRepository"; 5 | 6 | export class GetCategoryTypesUseCase { 7 | constructor(private categoryTypeRepository: CategoryTypeRepository) {} 8 | 9 | public async execute(): Promise> { 10 | const CategoryTypes = await this.categoryTypeRepository.getAll(); 11 | 12 | return Either.right(CategoryTypes.map(categoryType => categoryType.toData())); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/admin/src/countries/domain/DeleteCountryUseCase.ts: -------------------------------------------------------------------------------- 1 | import { CountryRepository } from "./Boundaries"; 2 | import { Either } from "karate-stars-core"; 3 | import { DataError } from "../../common/domain/Errors"; 4 | import { createIdOrUnexpectedError } from "../../common/domain/utils"; 5 | 6 | export default class DeleteCountryUseCase { 7 | constructor(private CountryRepository: CountryRepository) {} 8 | 9 | async execute(id: string): Promise> { 10 | const result = await createIdOrUnexpectedError(id) 11 | .flatMap(async id => this.CountryRepository.deleteById(id)) 12 | .run(); 13 | 14 | return result; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/admin/cypress/support/index.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import "./commands"; 18 | -------------------------------------------------------------------------------- /packages/admin/src/categories/domain/DeleteCategoryUseCase.ts: -------------------------------------------------------------------------------- 1 | import { CategoryRepository } from "./Boundaries"; 2 | import { Either } from "karate-stars-core"; 3 | import { DataError } from "../../common/domain/Errors"; 4 | import { createIdOrUnexpectedError } from "../../common/domain/utils"; 5 | 6 | export default class DeleteCategoryUseCase { 7 | constructor(private CategoryRepository: CategoryRepository) {} 8 | 9 | async execute(id: string): Promise> { 10 | const result = await createIdOrUnexpectedError(id) 11 | .flatMap(async id => this.CategoryRepository.deleteById(id)) 12 | .run(); 13 | 14 | return result; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/admin/src/news/domain/DeleteNewsFeedsUseCase.ts: -------------------------------------------------------------------------------- 1 | import { NewsFeedRepository } from "./Boundaries"; 2 | import { Either } from "karate-stars-core"; 3 | import { DataError } from "../../common/domain/Errors"; 4 | import { createIdOrUnexpectedError } from "../../common/domain/utils"; 5 | 6 | export default class DeleteNewsFeedsUseCase { 7 | constructor(private newsFeedRepository: NewsFeedRepository) {} 8 | 9 | async execute(id: string): Promise> { 10 | const result = await createIdOrUnexpectedError(id) 11 | .flatMap(async id => this.newsFeedRepository.deleteById(id)) 12 | .run(); 13 | 14 | return result; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/admin/src/event-types/domain/DeleteEventTypeUseCase.ts: -------------------------------------------------------------------------------- 1 | import { EventTypeRepository } from "./Boundaries"; 2 | import { Either } from "karate-stars-core"; 3 | import { DataError } from "../../common/domain/Errors"; 4 | import { createIdOrUnexpectedError } from "../../common/domain/utils"; 5 | 6 | export default class DeleteEventTypeUseCase { 7 | constructor(private eventTypeRepository: EventTypeRepository) {} 8 | 9 | async execute(id: string): Promise> { 10 | const result = await createIdOrUnexpectedError(id) 11 | .flatMap(async id => this.eventTypeRepository.deleteById(id)) 12 | .run(); 13 | 14 | return result; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/admin/src/common/presentation/state/ListPageState.ts: -------------------------------------------------------------------------------- 1 | import { IdentifiableObject, ListState } from "./ListState"; 2 | 3 | export interface ListLoadingState { 4 | kind: "ListLoadingState"; 5 | } 6 | 7 | export interface ListLoadedState extends ListState { 8 | kind: "ListLoadedState"; 9 | } 10 | 11 | export interface ListErrorState { 12 | kind: "ListErrorState"; 13 | message: string; 14 | } 15 | 16 | export interface NavigateTo { 17 | kind: "NavigateTo"; 18 | route: string; 19 | } 20 | 21 | export type ListPageState = 22 | | ListLoadingState 23 | | ListLoadedState 24 | | ListErrorState 25 | | NavigateTo; 26 | -------------------------------------------------------------------------------- /packages/admin/src/competitors/domain/DeleteCompetitorUseCase.ts: -------------------------------------------------------------------------------- 1 | import { CompetitorRepository } from "./Boundaries"; 2 | import { Either } from "karate-stars-core"; 3 | import { DataError } from "../../common/domain/Errors"; 4 | import { createIdOrUnexpectedError } from "../../common/domain/utils"; 5 | 6 | export default class DeleteCompetitorUseCase { 7 | constructor(private competitorrepository: CompetitorRepository) {} 8 | 9 | async execute(id: string): Promise> { 10 | const result = await createIdOrUnexpectedError(id) 11 | .flatMap(async id => this.competitorrepository.deleteById(id)) 12 | .run(); 13 | 14 | return result; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/admin/src/events/domain/GetEventByIdUseCase.ts: -------------------------------------------------------------------------------- 1 | import { EventRepository } from "./Boundaries"; 2 | import { Event, EventData, Either } from "karate-stars-core"; 3 | import { DataError } from "../../common/domain/Errors"; 4 | import { createIdOrUnexpectedError } from "../../common/domain/utils"; 5 | 6 | export default class GetEventByIdUseCase { 7 | constructor(private eventRepository: EventRepository) {} 8 | 9 | async execute(id: string): Promise> { 10 | return await createIdOrUnexpectedError(id) 11 | .flatMap(id => this.eventRepository.getById(id)) 12 | .map(newsFeed => newsFeed.toData()) 13 | .run(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/admin/src/videos/domain/GetVideoByIdUseCase.ts: -------------------------------------------------------------------------------- 1 | import { VideoRepository } from "./Boundaries"; 2 | import { Video, VideoData, Either } from "karate-stars-core"; 3 | import { DataError } from "../../common/domain/Errors"; 4 | import { createIdOrUnexpectedError } from "../../common/domain/utils"; 5 | 6 | export default class GetVideoByIdUseCase { 7 | constructor(private videoRepository: VideoRepository) {} 8 | 9 | async execute(id: string): Promise> { 10 | return await createIdOrUnexpectedError(id) 11 | .flatMap