diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..48667da --- /dev/null +++ b/.babelrc @@ -0,0 +1,17 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "targets": { + "node": "current", + }, + }, + ], + ["@babel/preset-react", { + "runtime": "automatic" + }], + "@babel/preset-typescript" + ], + "plugins": ["babel-plugin-transform-import-meta"] +} diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..233e901 --- /dev/null +++ b/.env.development @@ -0,0 +1,10 @@ +VITE_API_ORIGIN = https://admin.dev.dipal.ru/api/v1 +VITE_API_AUTH_ORIGIN = https://auth.dev.dipal.ru/api/v1/auth +VITE_API_AUTH_PHONENUMBER = /start-challenge +VITE_API_AUTH_LOGIN = /login +VITE_API_AUTH_REFRESH = /refresh-token +VITE_API_CREATE_MEMBER = /user_place/members +VITE_API_PLACES = /place +VITE_API_USERS = /profile +VITE_API_USERS_ACCOUNT = /account +VITE_API_USERS_PROFILE = /profile \ No newline at end of file diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..e0b8844 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,11 @@ +/node_modules +/public +/dist +vite.config.ts +.eslintrc.js +/build +/src/vite-env.d.ts +tailwind.config.cjs +postcss.config.cjs +fileMock.js +jest.config.js \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..6028df8 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,83 @@ +{ + "env": { + "browser": true, + "es2021": true + }, + "settings": { + "react": { + "version": "detect" + }, + "import/resolver": { + "alias": { + "map": [["~", "./src"]], + "extensions": [".js",".ts",".tsx", ".d.ts", ".json"] + } + } + }, + "extends": [ + "airbnb", + "eslint:recommended", + "plugin:react/recommended", + "plugin:react/recommended", + "plugin:jest/recommended", + "plugin:prettier/recommended", + "plugin:@typescript-eslint/recommended" + ], + "overrides": [], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "plugins": ["react", "@typescript-eslint", "jest", "prettier"], + "rules": { + "object-curly-spacing": ["error", "always"], + "import/extensions": [ + "error", + "ignorePackages", + { + "js": "never", + "jsx": "never", + "ts": "never", + "tsx": "never" + } + ], + "import/no-extraneous-dependencies": ["error", { "devDependencies": true }], + "class-methods-use-this": "off", + "import/no-import-module-exports": "off", + "react/require-default-props": "off", + "import/prefer-default-export": "off", + "jsx-a11y/click-events-have-key-events": "off", + "jsx-a11y/no-static-element-interactions": "off", + "@typescript-eslint/no-unused-vars": "error", + "@typescript-eslint/no-shadow": "error", + "no-shadow": "off", + "no-return-await": "off", + "import/order": [ + "error", + { + "pathGroups": [ + { + "pattern": "~/**", + "group": "external" + } + ] + } + ], + "prettier/prettier": [ + "error", + { + "usePrettierrc": true, + "jsxSingleQuote": true, + "singleQuote": true, + "endOfLine": "auto" + + } + ], + "jest/valid-title": "off", + "react/button-has-type": "off", + "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx", ".ts", ".tsx"] }], + "react/react-in-jsx-scope": "off", + "@typescript-eslint/no-namespace": "off" + } +} diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100755 index 0000000..fdc2655 --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1,5 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +npm run test && npm run lint +npm run test && npm run lint diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..e54d85c --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,13 @@ +{ + "printWidth": 120, + "tabWidth": 2, + "endOfLine":"auto", + "useTabs": false, + "semi": true, + "singleQuote": true, + "quoteProps": "as-needed", + "jsxSingleQuote": true, + "trailingComma": "all", + "bracketSpacing": true, + "arrowParens": "always" +} diff --git a/jest.config.js b/jest.config.js index ee178f8..e84dfbe 100644 --- a/jest.config.js +++ b/jest.config.js @@ -8,5 +8,7 @@ export default { "\\.(css|less)$": "identity-obj-proxy", "^~(.*)$": "/src$1" }, - transformIgnorePatterns: ['/node_modules/'], + transformIgnorePatterns: [ + "node_modules/(?!(spacetime)/)" + ] } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 39ae599..78b9b10 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7704,6 +7704,14 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, + "swr": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.1.5.tgz", + "integrity": "sha512-/OhfZMcEpuz77KavXST5q6XE9nrOBOVcBLWjMT+oAE/kQHyE3PASrevXCtQDZ8aamntOfFkbVJp7Il9tNBQWrw==", + "requires": { + "use-sync-external-store": "^1.2.0" + } + }, "symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", diff --git a/package.json b/package.json index 0dd2f09..223f7a6 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "build": "tsc && vite build", "preview": "vite preview", "lint": "eslint --fix --ignore-path .eslintignore --ignore-pattern \"!**/.*\" .", - "prepare": "husky install", + "prepare": "husky install && husky add .husky/pre-push \"npm run test && npm run lint\"", "test": "jest", "tsc": "tsc" }, @@ -18,16 +18,17 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-redux": "^8.0.5", - "react-router-dom": "^6.9.0" + "react-router-dom": "^6.9.0", + "swr": "^2.1.5" }, "devDependencies": { - "@babel/preset-env": "^7.20.2", + "@babel/preset-env": "^7.21.5", "@babel/preset-react": "^7.18.6", - "@babel/preset-typescript": "^7.21.0", + "@babel/preset-typescript": "^7.21.5", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^14.0.0", "@testing-library/user-event": "^14.4.3", - "@types/jest": "^29.4.0", + "@types/jest": "^29.5.1", "@types/react": "^18.0.27", "@types/react-dom": "^18.0.10", "@typescript-eslint/eslint-plugin": "^5.54.1", diff --git a/public/assets/icons/logo-black.svg b/public/assets/icons/logo-black.svg new file mode 100644 index 0000000..1299dee --- /dev/null +++ b/public/assets/icons/logo-black.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/business-logic/core/places/common/entity/placeEntity.ts b/src/business-logic/core/places/common/entity/placeEntity.ts new file mode 100644 index 0000000..b171c44 --- /dev/null +++ b/src/business-logic/core/places/common/entity/placeEntity.ts @@ -0,0 +1,9 @@ +type Places = { + placeType: string; + name: string; + qr: null | string; + id: string; + parentId: string | null; +}; + +export default Places; diff --git a/src/business-logic/core/places/common/model/placesModel.ts b/src/business-logic/core/places/common/model/placesModel.ts new file mode 100644 index 0000000..9b73779 --- /dev/null +++ b/src/business-logic/core/places/common/model/placesModel.ts @@ -0,0 +1,21 @@ +import Places from '../entity/placeEntity'; + +class PlacesModel { + private placesList: Places[]; + + private modelTitle = 'places'; + + constructor(data: Places[]) { + this.placesList = data; + } + + getData(): Places[] { + return this.placesList; + } + + getTitle(): string { + return this.modelTitle; + } +} + +export default PlacesModel; diff --git a/src/business-logic/core/places/get-places/data/repository/GetPlacesRepo.ts b/src/business-logic/core/places/get-places/data/repository/GetPlacesRepo.ts new file mode 100644 index 0000000..dd35037 --- /dev/null +++ b/src/business-logic/core/places/get-places/data/repository/GetPlacesRepo.ts @@ -0,0 +1,16 @@ +import PlacesModel from '../../../common/model/placesModel'; +import getPlacesRO from '../response-object/getPlacesRO'; +import { GetPlacesResponse } from '../response-object/protocols'; + +const getPlacesRepo = async (httpHandler: () => Promise) => { + // call httpHandler + const placesResponse = await httpHandler(); + // user response object to turn it to the data that we want + const placesData = getPlacesRO(placesResponse); + // make model + const placesModel = new PlacesModel(placesData); + // return the model + return placesModel; +}; + +export default getPlacesRepo; diff --git a/src/business-logic/core/places/get-places/data/repository/IGetPlacesRepo.ts b/src/business-logic/core/places/get-places/data/repository/IGetPlacesRepo.ts new file mode 100644 index 0000000..86d6e71 --- /dev/null +++ b/src/business-logic/core/places/get-places/data/repository/IGetPlacesRepo.ts @@ -0,0 +1,5 @@ +import PlacesModel from '../../../common/model/placesModel'; + +type IGetPlacesRepo = () => Promise; + +export default IGetPlacesRepo; diff --git a/src/business-logic/core/places/get-places/data/response-object/getPlacesRO.ts b/src/business-logic/core/places/get-places/data/response-object/getPlacesRO.ts new file mode 100644 index 0000000..1b63fe6 --- /dev/null +++ b/src/business-logic/core/places/get-places/data/response-object/getPlacesRO.ts @@ -0,0 +1,19 @@ +/* eslint-disable no-underscore-dangle */ +import { GetPlacesRO, GetPlacesResponse } from './protocols'; + +const getPlacesRO = (placesResponse: GetPlacesResponse): GetPlacesRO => { + return placesResponse.map((placeResponse) => { + return { + id: placeResponse._id, + placeType: placeResponse.place_type, + name: placeResponse.name, + parentId: placeResponse.place_type, + availableServices: placeResponse.available_services, + createdAt: placeResponse.createdAt, + updatedAt: placeResponse.updatedAt, + qr: null, + }; + }); +}; + +export default getPlacesRO; diff --git a/src/business-logic/core/places/get-places/data/response-object/protocols.ts b/src/business-logic/core/places/get-places/data/response-object/protocols.ts new file mode 100644 index 0000000..7455c7c --- /dev/null +++ b/src/business-logic/core/places/get-places/data/response-object/protocols.ts @@ -0,0 +1,18 @@ +import Places from '../../../common/entity/placeEntity'; + +export type GetPlacesResponse = { + _id: string; + place_type: string; + name: string; + parent_id: string | null; + createdAt: string; + updatedAt: string; + available_services: string[]; +}[]; + +export type GetPlacesRO = Places[] & + { + availableServices: string[]; + createdAt: string; + updatedAt: string; + }[]; diff --git a/src/business-logic/core/places/get-places/index.ts b/src/business-logic/core/places/get-places/index.ts new file mode 100644 index 0000000..505e616 --- /dev/null +++ b/src/business-logic/core/places/get-places/index.ts @@ -0,0 +1,3 @@ +import getPlaces from './infra/getPlacesInfra'; + +export default getPlaces; diff --git a/src/business-logic/core/places/get-places/infra/getPlacesInfra.ts b/src/business-logic/core/places/get-places/infra/getPlacesInfra.ts new file mode 100644 index 0000000..c89093a --- /dev/null +++ b/src/business-logic/core/places/get-places/infra/getPlacesInfra.ts @@ -0,0 +1,15 @@ +import getPlacesRepo from '../data/repository/GetPlacesRepo'; +import IGetPlacesRepo from '../data/repository/IGetPlacesRepo'; +import GettingPlacesUsecase from '../usecase/getPlaceUsecase'; +import { IgetPlacesInfra, getPlacesReturnType } from './protocols'; + +const getPlaces = ({ httpHandler }: IgetPlacesInfra): getPlacesReturnType => { + // get httpHandler + const repository: IGetPlacesRepo = () => getPlacesRepo(httpHandler); + // connet usecase and repository + const usecase = new GettingPlacesUsecase(repository); + // return method to use + return () => usecase.execute(); +}; + +export default getPlaces; diff --git a/src/business-logic/core/places/get-places/infra/protocols.ts b/src/business-logic/core/places/get-places/infra/protocols.ts new file mode 100644 index 0000000..92ca2e7 --- /dev/null +++ b/src/business-logic/core/places/get-places/infra/protocols.ts @@ -0,0 +1,8 @@ +import PlacesModel from '../../common/model/placesModel'; +import { GetPlacesResponse } from '../data/response-object/protocols'; + +export interface IgetPlacesInfra { + httpHandler: () => Promise; +} + +export type getPlacesReturnType = () => Promise; diff --git a/src/business-logic/core/places/get-places/port.ts b/src/business-logic/core/places/get-places/port.ts new file mode 100644 index 0000000..844e02d --- /dev/null +++ b/src/business-logic/core/places/get-places/port.ts @@ -0,0 +1,5 @@ +import { getPlacesReturnType, type IgetPlacesInfra } from './infra/protocols'; + +export default IgetPlacesInfra; + +export type getPlacesReturnPort = getPlacesReturnType; diff --git a/src/business-logic/core/places/get-places/usecase/__test__/getPlacesUsecase.test.ts b/src/business-logic/core/places/get-places/usecase/__test__/getPlacesUsecase.test.ts new file mode 100644 index 0000000..947836c --- /dev/null +++ b/src/business-logic/core/places/get-places/usecase/__test__/getPlacesUsecase.test.ts @@ -0,0 +1,27 @@ +import PlacesModel from '../../../common/model/placesModel'; +import IGetPlacesRepo from '../../data/repository/IGetPlacesRepo'; +import { GetPlacesRO } from '../../data/response-object/protocols'; +import GettingPlacesUsecase from '../getPlaceUsecase'; + +const mockedRO: GetPlacesRO = { + availableServices: [''], + createdAt: 'createdAt', + id: 'id', + name: 'name', + parentId: null, + placeType: 'continent', + updatedAt: 'updatedTime', + qr: null, +}; + +const model = new PlacesModel(mockedRO); +const mockedRepo: IGetPlacesRepo = jest.fn().mockImplementation(async () => model); + +describe('getting places usecase tests', () => { + it('call repository on execute usecase', async () => { + const usecase = new GettingPlacesUsecase(mockedRepo); + await usecase.execute(); + + expect(mockedRepo).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/business-logic/core/places/get-places/usecase/getPlaceUsecase.ts b/src/business-logic/core/places/get-places/usecase/getPlaceUsecase.ts new file mode 100644 index 0000000..a69bb29 --- /dev/null +++ b/src/business-logic/core/places/get-places/usecase/getPlaceUsecase.ts @@ -0,0 +1,21 @@ +import PlacesModel from '../../common/model/placesModel'; +import IGetPlacesRepo from '../data/repository/IGetPlacesRepo'; + +/** + * this usecase is responsible for calling the repo and returning the places data from the repository + */ +class GettingPlacesUsecase { + private repository: IGetPlacesRepo; + + constructor(repository: IGetPlacesRepo) { + this.repository = repository; + } + + async execute(): Promise { + // call usecase and return the repo + const placesData = await this.repository(); + return placesData; + } +} + +export default GettingPlacesUsecase; diff --git a/src/business-logic/core/users/common/data/model/usersModel.ts b/src/business-logic/core/users/common/data/model/usersModel.ts new file mode 100644 index 0000000..22df41f --- /dev/null +++ b/src/business-logic/core/users/common/data/model/usersModel.ts @@ -0,0 +1,26 @@ +import Users from '../../entity/entity'; + +class UsersModel { + private usersList: Users[]; + + private modelTitle = 'users'; + + constructor(data: Users[]) { + this.usersList = data; + } + + getData(): Users[] { + return this.usersList; + } + + getTitle(): string { + return this.modelTitle; + } + + addUser(user: Users): Users[] { + this.usersList.push(user); + return this.usersList; + } +} + +export default UsersModel; diff --git a/src/business-logic/core/users/common/entity/entity.ts b/src/business-logic/core/users/common/entity/entity.ts new file mode 100644 index 0000000..90f9f2c --- /dev/null +++ b/src/business-logic/core/users/common/entity/entity.ts @@ -0,0 +1,6 @@ +export default interface Users { + profileId: string; + firstname: string; + lastname: string; + accountId: string; +} diff --git a/src/business-logic/core/users/create-user/create-account/data/dto/createAccountDTO.ts b/src/business-logic/core/users/create-user/create-account/data/dto/createAccountDTO.ts new file mode 100644 index 0000000..fdc699d --- /dev/null +++ b/src/business-logic/core/users/create-user/create-account/data/dto/createAccountDTO.ts @@ -0,0 +1,10 @@ +import { CreateAccountDTOReturnType, INewUserData } from './protocols'; + +const createAccountDTO = (newUser: INewUserData): CreateAccountDTOReturnType => ({ + enabled: true, + firstName: newUser.firstname, + lastName: newUser.lastname, + username: newUser.phonenumber, +}); + +export default createAccountDTO; diff --git a/src/business-logic/core/users/create-user/create-account/data/dto/protocols.ts b/src/business-logic/core/users/create-user/create-account/data/dto/protocols.ts new file mode 100644 index 0000000..1c74a3b --- /dev/null +++ b/src/business-logic/core/users/create-user/create-account/data/dto/protocols.ts @@ -0,0 +1,12 @@ +export interface INewUserData { + firstname: string; + lastname: string; + phonenumber: string; +} + +export type CreateAccountDTOReturnType = { + username: string; + firstName: string; + lastName: string; + enabled: true; +}; diff --git a/src/business-logic/core/users/create-user/create-account/data/repository/ICreateAcountRepo.ts b/src/business-logic/core/users/create-user/create-account/data/repository/ICreateAcountRepo.ts new file mode 100644 index 0000000..2a81155 --- /dev/null +++ b/src/business-logic/core/users/create-user/create-account/data/repository/ICreateAcountRepo.ts @@ -0,0 +1,8 @@ +import { INewUserData } from '../dto/protocols'; +import { CreateAccountROReturnType } from '../response-object/protocols'; + +interface ICreateAcountRepo { + execute: (newUser: INewUserData) => Promise; +} + +export default ICreateAcountRepo; diff --git a/src/business-logic/core/users/create-user/create-account/data/repository/createAcountRepo.ts b/src/business-logic/core/users/create-user/create-account/data/repository/createAcountRepo.ts new file mode 100644 index 0000000..f089659 --- /dev/null +++ b/src/business-logic/core/users/create-user/create-account/data/repository/createAcountRepo.ts @@ -0,0 +1,24 @@ +import createAccountDTO from '../dto/createAccountDTO'; +import { INewUserData } from '../dto/protocols'; +import createAcountRO from '../response-object/createAcountRO'; +import { CreateAccountROReturnType } from '../response-object/protocols'; +import ICreateAcountRepo from './ICreateAcountRepo'; +import { HttpHandler } from './protocols'; + +export default class CreateAccountRepo implements ICreateAcountRepo { + private httpHandler: HttpHandler; + + constructor(httpHandler: HttpHandler) { + this.httpHandler = httpHandler; + } + + execute: (newUser: INewUserData) => Promise = async (newUser: INewUserData) => { + // call dto + const dto = createAccountDTO(newUser); + // call + const newAccountResponse = await this.httpHandler(dto); + // call response object + const newAccount = createAcountRO(newAccountResponse); + return newAccount; + }; +} diff --git a/src/business-logic/core/users/create-user/create-account/data/repository/protocols.ts b/src/business-logic/core/users/create-user/create-account/data/repository/protocols.ts new file mode 100644 index 0000000..9263d97 --- /dev/null +++ b/src/business-logic/core/users/create-user/create-account/data/repository/protocols.ts @@ -0,0 +1,4 @@ +import { INewUserData } from '../dto/protocols'; +import { CreateAcountResponseApi } from '../response-object/protocols'; + +export type HttpHandler = (newUser: INewUserData) => Promise; diff --git a/src/business-logic/core/users/create-user/create-account/data/response-object/createAcountRO.ts b/src/business-logic/core/users/create-user/create-account/data/response-object/createAcountRO.ts new file mode 100644 index 0000000..66c809c --- /dev/null +++ b/src/business-logic/core/users/create-user/create-account/data/response-object/createAcountRO.ts @@ -0,0 +1,8 @@ +import { CreateAccountROReturnType, CreateAcountResponseApi } from './protocols'; + +const createAcountRO = (apiResponse: CreateAcountResponseApi): CreateAccountROReturnType => ({ + accountId: apiResponse.id, + phonenumber: apiResponse.username, +}); + +export default createAcountRO; diff --git a/src/business-logic/core/users/create-user/create-account/data/response-object/protocols.ts b/src/business-logic/core/users/create-user/create-account/data/response-object/protocols.ts new file mode 100644 index 0000000..3ff64ba --- /dev/null +++ b/src/business-logic/core/users/create-user/create-account/data/response-object/protocols.ts @@ -0,0 +1,10 @@ +export type CreateAcountResponseApi = { + nextRequestTimestamp: number; + username: string; + id: string; +}; + +export type CreateAccountROReturnType = { + phonenumber: string; + accountId: string; +}; diff --git a/src/business-logic/core/users/create-user/create-profile/data/dto/createProfileDTO.ts b/src/business-logic/core/users/create-user/create-profile/data/dto/createProfileDTO.ts new file mode 100644 index 0000000..82ad6d3 --- /dev/null +++ b/src/business-logic/core/users/create-user/create-profile/data/dto/createProfileDTO.ts @@ -0,0 +1,9 @@ +import { CreateProfileDtoType, ICreateNewProfileData } from './protocols'; + +const createProfileDTO: CreateProfileDtoType = (userAccount: ICreateNewProfileData) => ({ + account_id: userAccount.accountId, + first_name: userAccount.firstname, + last_name: userAccount.lastname, +}); + +export default createProfileDTO; diff --git a/src/business-logic/core/users/create-user/create-profile/data/dto/protocols.ts b/src/business-logic/core/users/create-user/create-profile/data/dto/protocols.ts new file mode 100644 index 0000000..544a493 --- /dev/null +++ b/src/business-logic/core/users/create-user/create-profile/data/dto/protocols.ts @@ -0,0 +1,12 @@ +import { INewUserData } from '../../../create-account/data/dto/protocols'; +import { CreateAccountROReturnType } from '../../../create-account/data/response-object/protocols'; + +export type ICreateNewProfileData = CreateAccountROReturnType & INewUserData; + +export type CreateProfileDtoReturnType = { + account_id: string; + first_name: string; + last_name: string; +}; + +export type CreateProfileDtoType = (userAccount: ICreateNewProfileData) => CreateProfileDtoReturnType; diff --git a/src/business-logic/core/users/create-user/create-profile/data/repository/CreateRepositoryRepo.ts b/src/business-logic/core/users/create-user/create-profile/data/repository/CreateRepositoryRepo.ts new file mode 100644 index 0000000..698d85d --- /dev/null +++ b/src/business-logic/core/users/create-user/create-profile/data/repository/CreateRepositoryRepo.ts @@ -0,0 +1,22 @@ +/* eslint-disable no-useless-constructor */ +import RepositoryHandler from '~/driven/utils/helpers/repository-handler/repositoryHandler'; +import { CreateProfileDtoReturnType, ICreateNewProfileData } from '../dto/protocols'; +import createProfileDTO from '../dto/createProfileDTO'; +import ICreateProfileRepo from './ICreateProfileRepo'; +import { HttpHandler } from './protocols'; + +export default class CreateProfileRepo + extends RepositoryHandler + implements ICreateProfileRepo +{ + constructor(httpHandler: HttpHandler) { + super(httpHandler); + } + + async execute(accountData: ICreateNewProfileData) { + // create data in dto + const dto = createProfileDTO(accountData); + // call main http handler + return await this.httpHandler(dto); + } +} diff --git a/src/business-logic/core/users/create-user/create-profile/data/repository/ICreateProfileRepo.ts b/src/business-logic/core/users/create-user/create-profile/data/repository/ICreateProfileRepo.ts new file mode 100644 index 0000000..fd73452 --- /dev/null +++ b/src/business-logic/core/users/create-user/create-profile/data/repository/ICreateProfileRepo.ts @@ -0,0 +1,5 @@ +import { ICreateNewProfileData } from '../dto/protocols'; + +export default interface ICreateProfileRepo { + execute: (accountData: ICreateNewProfileData) => Promise; +} diff --git a/src/business-logic/core/users/create-user/create-profile/data/repository/protocols.ts b/src/business-logic/core/users/create-user/create-profile/data/repository/protocols.ts new file mode 100644 index 0000000..7ae431e --- /dev/null +++ b/src/business-logic/core/users/create-user/create-profile/data/repository/protocols.ts @@ -0,0 +1,3 @@ +import { CreateProfileDtoReturnType } from '../dto/protocols'; + +export type HttpHandler = (newUser: CreateProfileDtoReturnType) => Promise; diff --git a/src/business-logic/core/users/create-user/index.ts b/src/business-logic/core/users/create-user/index.ts new file mode 100644 index 0000000..30c0df1 --- /dev/null +++ b/src/business-logic/core/users/create-user/index.ts @@ -0,0 +1,3 @@ +import CreateUserInfra from './infra/createUserInfra'; + +export default CreateUserInfra; diff --git a/src/business-logic/core/users/create-user/infra/createUserInfra.ts b/src/business-logic/core/users/create-user/infra/createUserInfra.ts new file mode 100644 index 0000000..6a032a7 --- /dev/null +++ b/src/business-logic/core/users/create-user/infra/createUserInfra.ts @@ -0,0 +1,29 @@ +import CreateAccountRepo from '../create-account/data/repository/createAcountRepo'; +import { HttpHandler as HttpAccountHandler } from '../create-account/data/repository/protocols'; +import CreateProfileRepo from '../create-profile/data/repository/CreateRepositoryRepo'; +import { HttpHandler as HttpProfileHandler } from '../create-profile/data/repository/protocols'; +import CreateUserUsecase from '../usecase/createUserUsecase'; + +class CreateUserInfra { + private httpAccountHandler: HttpAccountHandler; + + private httpProfileHandler: HttpProfileHandler; + + constructor(httpAccountHandler: HttpAccountHandler, httpProfileHandler: HttpProfileHandler) { + this.httpAccountHandler = httpAccountHandler; + this.httpProfileHandler = httpProfileHandler; + } + + execute() { + // make account repositroy ready + const accountRepository = new CreateAccountRepo(this.httpAccountHandler); + // make profile repository ready + const profileRepository = new CreateProfileRepo(this.httpProfileHandler); + // make usecase ready + const usecase = new CreateUserUsecase(accountRepository, profileRepository); + // return prepared method to call and create user + return usecase; + } +} + +export default CreateUserInfra; diff --git a/src/business-logic/core/users/create-user/ports.ts b/src/business-logic/core/users/create-user/ports.ts new file mode 100644 index 0000000..02ed3c0 --- /dev/null +++ b/src/business-logic/core/users/create-user/ports.ts @@ -0,0 +1,10 @@ +import { HttpHandler as HttpProfileHandler } from './create-profile/data/repository/protocols'; +import { HttpHandler as HttpAccountHandler } from './create-account/data/repository/protocols'; +import CreateUserUsecase from './usecase/createUserUsecase'; + +export default interface createUserPort { + httpAccountHandler: HttpAccountHandler; + httpProfileHandler: HttpProfileHandler; +} + +export type createUserReturnType = CreateUserUsecase; diff --git a/src/business-logic/core/users/create-user/usecase/createUserUsecase.ts b/src/business-logic/core/users/create-user/usecase/createUserUsecase.ts new file mode 100644 index 0000000..aad7d6a --- /dev/null +++ b/src/business-logic/core/users/create-user/usecase/createUserUsecase.ts @@ -0,0 +1,27 @@ +import { INewUserData } from '../create-account/data/dto/protocols'; +import ICreateAcountRepo from '../create-account/data/repository/ICreateAcountRepo'; +import ICreateProfileRepo from '../create-profile/data/repository/ICreateProfileRepo'; + +export default class CreateUserUsecase { + private accountRepository: ICreateAcountRepo; + + private profileRepository: ICreateProfileRepo; + + constructor(accountRepository: ICreateAcountRepo, profileRepository: ICreateProfileRepo) { + this.accountRepository = accountRepository; + this.profileRepository = profileRepository; + } + + async execute(newUser: INewUserData) { + // create acount + const newAccountResponse = await this.accountRepository.execute(newUser); + const newProfileData = { + ...newAccountResponse, + ...newUser, + }; + // create profile by account ID + const newProfileResponse = await this.profileRepository.execute(newProfileData); + + return newProfileResponse; + } +} diff --git a/src/business-logic/core/users/get-users/data/repository/IGetUserRepo.ts b/src/business-logic/core/users/get-users/data/repository/IGetUserRepo.ts new file mode 100644 index 0000000..7199fc9 --- /dev/null +++ b/src/business-logic/core/users/get-users/data/repository/IGetUserRepo.ts @@ -0,0 +1,5 @@ +import UsersModel from '../../../common/data/model/usersModel'; + +type IGetUsersRepo = () => Promise; + +export default IGetUsersRepo; diff --git a/src/business-logic/core/users/get-users/data/repository/getUserRepo.ts b/src/business-logic/core/users/get-users/data/repository/getUserRepo.ts new file mode 100644 index 0000000..2c58291 --- /dev/null +++ b/src/business-logic/core/users/get-users/data/repository/getUserRepo.ts @@ -0,0 +1,16 @@ +import UsersModel from '../../../common/data/model/usersModel'; +import getUsersResponseObject from '../response-object/usersRO'; +import { GetUsersResponse } from '../response-object/protocols'; + +const getUsersRepo = async (httpHandler: () => Promise) => { + // call httpHandler + const usersResponse = await httpHandler(); + // user response object to turn it to the data that we want + const usersData = getUsersResponseObject(usersResponse); + // make model + const usersModel = new UsersModel(usersData); + // return the model + return usersModel; +}; + +export default getUsersRepo; diff --git a/src/business-logic/core/users/get-users/data/response-object/protocols.ts b/src/business-logic/core/users/get-users/data/response-object/protocols.ts new file mode 100644 index 0000000..9a4965c --- /dev/null +++ b/src/business-logic/core/users/get-users/data/response-object/protocols.ts @@ -0,0 +1,18 @@ +export type GetUsersResponse = { + _id: string; + account_type: string; + updated_at: string; + created_at: string; + subscription_method: string; + last_login_method: string; + account_number: null | number; + balance: number; + account_id: string; + user_data: { + _id: string; + first_name: string; + last_name: string; + avatar: string; + }; + __v: number; +}[]; diff --git a/src/business-logic/core/users/get-users/data/response-object/usersRO.ts b/src/business-logic/core/users/get-users/data/response-object/usersRO.ts new file mode 100644 index 0000000..df2b190 --- /dev/null +++ b/src/business-logic/core/users/get-users/data/response-object/usersRO.ts @@ -0,0 +1,16 @@ +/* eslint-disable no-underscore-dangle */ +import Users from '../../../common/entity/entity'; +import { GetUsersResponse } from './protocols'; + +const getUsersResponseObject = (apiResponse: GetUsersResponse): Users[] => { + return apiResponse.map((userItem) => { + return { + profileId: userItem._id, + firstname: userItem.user_data.first_name, + lastname: userItem.user_data.last_name, + accountId: userItem.account_id, + }; + }); +}; + +export default getUsersResponseObject; diff --git a/src/business-logic/core/users/get-users/index.ts b/src/business-logic/core/users/get-users/index.ts new file mode 100644 index 0000000..e875adf --- /dev/null +++ b/src/business-logic/core/users/get-users/index.ts @@ -0,0 +1,3 @@ +import getUsers from './infra/getUsersInfra'; + +export default getUsers; diff --git a/src/business-logic/core/users/get-users/infra/getUsersInfra.ts b/src/business-logic/core/users/get-users/infra/getUsersInfra.ts new file mode 100644 index 0000000..13e305d --- /dev/null +++ b/src/business-logic/core/users/get-users/infra/getUsersInfra.ts @@ -0,0 +1,15 @@ +import IGetUsersRepo from '../data/repository/IGetUserRepo'; +import getUsersRepo from '../data/repository/getUserRepo'; +import GettingUsersUsecase from '../usecase/getUsersUsecase'; +import { IgetusersInfra, getusersReturnType } from './protocols'; + +const getUsers = ({ httpHandler }: IgetusersInfra): getusersReturnType => { + // get httpHandler + const repository: IGetUsersRepo = () => getUsersRepo(httpHandler); + // connet usecase and repository + const usecase = new GettingUsersUsecase(repository); + // return method to use + return () => usecase.execute(); +}; + +export default getUsers; diff --git a/src/business-logic/core/users/get-users/infra/protocols.ts b/src/business-logic/core/users/get-users/infra/protocols.ts new file mode 100644 index 0000000..e26a731 --- /dev/null +++ b/src/business-logic/core/users/get-users/infra/protocols.ts @@ -0,0 +1,8 @@ +import UsersModel from '../../common/data/model/usersModel'; +import { GetUsersResponse } from '../data/response-object/protocols'; + +export interface IgetUsersInfra { + httpHandler: () => Promise; +} + +export type getUsersReturnType = () => Promise; diff --git a/src/business-logic/core/users/get-users/ports.ts b/src/business-logic/core/users/get-users/ports.ts new file mode 100644 index 0000000..81a7ac7 --- /dev/null +++ b/src/business-logic/core/users/get-users/ports.ts @@ -0,0 +1,5 @@ +import { getUsersReturnType, type IgetUsersInfra } from './infra/protocols'; + +export default IgetUsersInfra; + +export type getUsersReturnPort = getUsersReturnType; diff --git a/src/business-logic/core/users/get-users/usecase/getUsersUsecase.ts b/src/business-logic/core/users/get-users/usecase/getUsersUsecase.ts new file mode 100644 index 0000000..ebec0cc --- /dev/null +++ b/src/business-logic/core/users/get-users/usecase/getUsersUsecase.ts @@ -0,0 +1,21 @@ +import UsersModel from '../../common/data/model/usersModel'; +import IGetUsersRepo from '../data/repository/IGetUserRepo'; + +/** + * this usecase is responsible for calling the repo and returning the users data from the repository + */ +class GettingUsersUsecase { + private repository: IGetUsersRepo; + + constructor(repository: IGetUsersRepo) { + this.repository = repository; + } + + async execute(): Promise { + // call usecase and return the repo + const usersData = await this.repository(); + return usersData; + } +} + +export default GettingUsersUsecase; diff --git a/src/business-logic/generic/admin-user/authentication/otp-auth/data/dto/otpAuthDto.ts b/src/business-logic/generic/admin-user/authentication/otp-auth/data/dto/otpAuthDto.ts new file mode 100644 index 0000000..f9547cf --- /dev/null +++ b/src/business-logic/generic/admin-user/authentication/otp-auth/data/dto/otpAuthDto.ts @@ -0,0 +1,14 @@ +import { AdminOtpData } from '../../usecase/otpAuthUsecase'; + +export type OtpAuthDTOReturnType = { + username: string; + password: string; + grant_type: 'otp'; +}; +const OtpAuthDTO = (dataToSend: AdminOtpData): OtpAuthDTOReturnType => ({ + grant_type: 'otp', + password: dataToSend.otp, + username: dataToSend.phonenumber, +}); + +export default OtpAuthDTO; diff --git a/src/business-logic/generic/admin-user/authentication/otp-auth/data/reponse-object/otpAuthRO.ts b/src/business-logic/generic/admin-user/authentication/otp-auth/data/reponse-object/otpAuthRO.ts new file mode 100644 index 0000000..8bc88f0 --- /dev/null +++ b/src/business-logic/generic/admin-user/authentication/otp-auth/data/reponse-object/otpAuthRO.ts @@ -0,0 +1,23 @@ +import AdminUser from '~/business-logic/generic/admin-user/common/entity/adminUserEntity'; + +export type OtpAuthResponse = { + access_token: string; + expires_in: number; + refresh_expires_in: number; + refresh_token: string; + token_type: 'Bearer'; +}; + +export interface IOtpAuthRO extends OtpAuthResponse { + phonenumber: string; +} +const otpAuthRO = (response: IOtpAuthRO): AdminUser => ({ + accessToken: response.access_token, + expiresIn: response.expires_in, + phonenumber: response.phonenumber, + refreshExpiresIn: response.refresh_expires_in, + refreshToken: response.refresh_token, + tokenType: response.token_type, +}); + +export default otpAuthRO; diff --git a/src/business-logic/generic/admin-user/authentication/otp-auth/data/repository/IOtpAuthRepo.ts b/src/business-logic/generic/admin-user/authentication/otp-auth/data/repository/IOtpAuthRepo.ts new file mode 100644 index 0000000..2d10765 --- /dev/null +++ b/src/business-logic/generic/admin-user/authentication/otp-auth/data/repository/IOtpAuthRepo.ts @@ -0,0 +1,6 @@ +import AdminUserModel from '../../../../common/data/model/adminUserModel'; +import { AdminOtpData } from '../../usecase/otpAuthUsecase'; + +export default interface IOtpAuthRepo { + execute(otpData: AdminOtpData): Promise; +} diff --git a/src/business-logic/generic/admin-user/authentication/otp-auth/data/repository/otpAuthRepo.ts b/src/business-logic/generic/admin-user/authentication/otp-auth/data/repository/otpAuthRepo.ts new file mode 100644 index 0000000..080794c --- /dev/null +++ b/src/business-logic/generic/admin-user/authentication/otp-auth/data/repository/otpAuthRepo.ts @@ -0,0 +1,32 @@ +import AdminUserModel from '~/business-logic/generic/admin-user/common/data/model/adminUserModel'; +import { AdminOtpData } from '../../usecase/otpAuthUsecase'; +import otpAuthRO, { OtpAuthResponse } from '../reponse-object/otpAuthRO'; +import OtpAuthDTO, { OtpAuthDTOReturnType } from '../dto/otpAuthDto'; +import IOtpAuthRepo from './IOtpAuthRepo'; + +export type OtpHttpHandler = (data: OtpAuthDTOReturnType) => Promise; + +export default class OtpAuthRepo implements IOtpAuthRepo { + private httpHandler: OtpHttpHandler; + + constructor(httpHandler: OtpHttpHandler) { + this.httpHandler = httpHandler; + } + + async execute(otpData: AdminOtpData): Promise { + // call dto + const dto = OtpAuthDTO(otpData); + // call handler + const response = await this.httpHandler(dto); + // call ro + const responseObjectData = { + ...response, + phonenumber: otpData.phonenumber, + }; + const responseObject = otpAuthRO(responseObjectData); + // make model + const adminModel = new AdminUserModel(responseObject); + // return model + return adminModel; + } +} diff --git a/src/business-logic/generic/admin-user/authentication/otp-auth/index.ts b/src/business-logic/generic/admin-user/authentication/otp-auth/index.ts new file mode 100644 index 0000000..ffeda5a --- /dev/null +++ b/src/business-logic/generic/admin-user/authentication/otp-auth/index.ts @@ -0,0 +1,3 @@ +import otpAuthInfra from './infra/otpAuthInfra'; + +export default otpAuthInfra; diff --git a/src/business-logic/generic/admin-user/authentication/otp-auth/infra/otpAuthInfra.ts b/src/business-logic/generic/admin-user/authentication/otp-auth/infra/otpAuthInfra.ts new file mode 100644 index 0000000..cdacca9 --- /dev/null +++ b/src/business-logic/generic/admin-user/authentication/otp-auth/infra/otpAuthInfra.ts @@ -0,0 +1,13 @@ +import OtpAuthRepo, { OtpHttpHandler } from '../data/repository/otpAuthRepo'; +import OtpAuthUsecase from '../usecase/otpAuthUsecase'; + +const otpAuthInfra = (httpHandler: OtpHttpHandler) => { + // make the repo ready + const repository = new OtpAuthRepo(httpHandler); + // make the usecase ready + const usecase = new OtpAuthUsecase(repository); + // return method + return usecase; +}; + +export default otpAuthInfra; diff --git a/src/business-logic/generic/admin-user/authentication/otp-auth/port.ts b/src/business-logic/generic/admin-user/authentication/otp-auth/port.ts new file mode 100644 index 0000000..bd47f83 --- /dev/null +++ b/src/business-logic/generic/admin-user/authentication/otp-auth/port.ts @@ -0,0 +1,8 @@ +import { OtpHttpHandler } from './data/repository/otpAuthRepo'; +import OtpAuthUsecase from './usecase/otpAuthUsecase'; + +export interface IOtpAuthDrivenPort { + httpHandler: OtpHttpHandler; +} + +export type OtpAuthDrivingPort = OtpAuthUsecase; diff --git a/src/business-logic/generic/admin-user/authentication/otp-auth/usecase/otpAuthUsecase.ts b/src/business-logic/generic/admin-user/authentication/otp-auth/usecase/otpAuthUsecase.ts new file mode 100644 index 0000000..33f1a36 --- /dev/null +++ b/src/business-logic/generic/admin-user/authentication/otp-auth/usecase/otpAuthUsecase.ts @@ -0,0 +1,35 @@ +import AdminUserModel from '../../../common/data/model/adminUserModel'; +import IOtpAuthRepo from '../data/repository/IOtpAuthRepo'; +import OtpAuthUsecaseException from './otpException'; + +export type AdminOtpData = { + otp: string; + phonenumber: string; +}; + +export interface IOtpAuthUsecase { + execute(data: AdminOtpData): Promise; +} + +export default class OtpAuthUsecase implements IOtpAuthUsecase { + private repository: IOtpAuthRepo; + + private validLenghtOfOtp = 6; + + constructor(repository: IOtpAuthRepo) { + this.repository = repository; + } + + async execute(data: AdminOtpData): Promise { + // check length of otp + const isOtpValid = this.isOtpValid(data.otp); + if (!isOtpValid) throw new OtpAuthUsecaseException(); + // call the repo + const updatedAdminModal = await this.repository.execute(data); + return updatedAdminModal; + } + + private isOtpValid(otp: string) { + return !!(otp.length >= this.validLenghtOfOtp); + } +} diff --git a/src/business-logic/generic/admin-user/authentication/otp-auth/usecase/otpException.ts b/src/business-logic/generic/admin-user/authentication/otp-auth/usecase/otpException.ts new file mode 100644 index 0000000..618fa8a --- /dev/null +++ b/src/business-logic/generic/admin-user/authentication/otp-auth/usecase/otpException.ts @@ -0,0 +1 @@ +export default class OtpAuthUsecaseException extends Error {} diff --git a/src/business-logic/generic/admin-user/authentication/phonnumber-auth/data/dto/phonenumberDto.ts b/src/business-logic/generic/admin-user/authentication/phonnumber-auth/data/dto/phonenumberDto.ts new file mode 100644 index 0000000..3b76455 --- /dev/null +++ b/src/business-logic/generic/admin-user/authentication/phonnumber-auth/data/dto/phonenumberDto.ts @@ -0,0 +1,10 @@ +import { AdminData } from '../../usecase/phonenumberAuthUsecase'; + +export type PhonenumberAuthDTOReturnType = { + username: string; +}; +const phonenumberAuthDTO = (adminData: AdminData): PhonenumberAuthDTOReturnType => ({ + username: adminData.phonenumber, +}); + +export default phonenumberAuthDTO; diff --git a/src/business-logic/generic/admin-user/authentication/phonnumber-auth/data/repository/IPhoneNumberAuthRepo.ts b/src/business-logic/generic/admin-user/authentication/phonnumber-auth/data/repository/IPhoneNumberAuthRepo.ts new file mode 100644 index 0000000..48e0864 --- /dev/null +++ b/src/business-logic/generic/admin-user/authentication/phonnumber-auth/data/repository/IPhoneNumberAuthRepo.ts @@ -0,0 +1,5 @@ +import { AdminData } from '../../usecase/phonenumberAuthUsecase'; + +export default interface IPhoneNumberAuthRepo { + execute(adminData: AdminData): Promise; +} diff --git a/src/business-logic/generic/admin-user/authentication/phonnumber-auth/data/repository/phonenumberAuthRepo.ts b/src/business-logic/generic/admin-user/authentication/phonnumber-auth/data/repository/phonenumberAuthRepo.ts new file mode 100644 index 0000000..5746d99 --- /dev/null +++ b/src/business-logic/generic/admin-user/authentication/phonnumber-auth/data/repository/phonenumberAuthRepo.ts @@ -0,0 +1,20 @@ +import { AdminData } from '../../usecase/phonenumberAuthUsecase'; +import phonenumberAuthDTO, { PhonenumberAuthDTOReturnType } from '../dto/phonenumberDto'; +import IPhoneNumberAuthRepo from './IPhoneNumberAuthRepo'; + +export type RepoHttpHandler = (dtoData: PhonenumberAuthDTOReturnType) => Promise; +export default class PhoneNumberAuthRepo implements IPhoneNumberAuthRepo { + private httpHandler: RepoHttpHandler; + + constructor(httpHandler: RepoHttpHandler) { + this.httpHandler = httpHandler; + } + + async execute(adminData: AdminData): Promise { + // make dto + const data = phonenumberAuthDTO(adminData); + // call http handler + const response = await this.httpHandler(data); + return response; + } +} diff --git a/src/business-logic/generic/admin-user/authentication/phonnumber-auth/index.ts b/src/business-logic/generic/admin-user/authentication/phonnumber-auth/index.ts new file mode 100644 index 0000000..c5526dc --- /dev/null +++ b/src/business-logic/generic/admin-user/authentication/phonnumber-auth/index.ts @@ -0,0 +1,3 @@ +import phonenumberAuthInfra from './infra/phonenumberAuthInfra'; + +export default phonenumberAuthInfra; diff --git a/src/business-logic/generic/admin-user/authentication/phonnumber-auth/infra/phonenumberAuthInfra.ts b/src/business-logic/generic/admin-user/authentication/phonnumber-auth/infra/phonenumberAuthInfra.ts new file mode 100644 index 0000000..04d7c46 --- /dev/null +++ b/src/business-logic/generic/admin-user/authentication/phonnumber-auth/infra/phonenumberAuthInfra.ts @@ -0,0 +1,21 @@ +import PhoneNumberAuthRepo, { RepoHttpHandler } from '../data/repository/phonenumberAuthRepo'; +import PhonenumberAuthUsecase from '../usecase/phonenumberAuthUsecase'; + +export interface IPhonenumberAuthInfra { + httpHandler: RepoHttpHandler; + wrongPhoneNumberMessage: string; +} + +const phonenumberAuthInfra = ({ + httpHandler, + wrongPhoneNumberMessage, +}: IPhonenumberAuthInfra): PhonenumberAuthUsecase => { + // prepare repo + const repository = new PhoneNumberAuthRepo(httpHandler); + // prepare usecase + const usecase = new PhonenumberAuthUsecase(repository, wrongPhoneNumberMessage); + // return main method + return usecase; +}; + +export default phonenumberAuthInfra; diff --git a/src/business-logic/generic/admin-user/authentication/phonnumber-auth/port.ts b/src/business-logic/generic/admin-user/authentication/phonnumber-auth/port.ts new file mode 100644 index 0000000..c4c8abf --- /dev/null +++ b/src/business-logic/generic/admin-user/authentication/phonnumber-auth/port.ts @@ -0,0 +1,5 @@ +import { IPhonenumberAuthInfra } from './infra/phonenumberAuthInfra'; +import PhonenumberAuthUsecase from './usecase/phonenumberAuthUsecase'; + +export type IPhonenumberAuthPort = IPhonenumberAuthInfra; +export type PhonenumberReturnTypePort = Promise; diff --git a/src/business-logic/generic/admin-user/authentication/phonnumber-auth/usecase/exception.ts b/src/business-logic/generic/admin-user/authentication/phonnumber-auth/usecase/exception.ts new file mode 100644 index 0000000..ee325b4 --- /dev/null +++ b/src/business-logic/generic/admin-user/authentication/phonnumber-auth/usecase/exception.ts @@ -0,0 +1 @@ +export default class PhonenumberException extends Error {} diff --git a/src/business-logic/generic/admin-user/authentication/phonnumber-auth/usecase/phonenumberAuthUsecase.ts b/src/business-logic/generic/admin-user/authentication/phonnumber-auth/usecase/phonenumberAuthUsecase.ts new file mode 100644 index 0000000..97b16e3 --- /dev/null +++ b/src/business-logic/generic/admin-user/authentication/phonnumber-auth/usecase/phonenumberAuthUsecase.ts @@ -0,0 +1,36 @@ +import IPhoneNumberAuthRepo from '../data/repository/IPhoneNumberAuthRepo'; +import PhonenumberException from './exception'; + +export type AdminData = { + phonenumber: string; +}; + +interface IPhoneNumberAuthUsecase { + execute(adminData: AdminData): Promise; +} + +export default class PhonenumberAuthUsecase implements IPhoneNumberAuthUsecase { + private repository: IPhoneNumberAuthRepo; + + private wrongPhoneNumberMessage: string; + + constructor(repository: IPhoneNumberAuthRepo, wrongPhoneNumberMessage: string) { + this.repository = repository; + this.wrongPhoneNumberMessage = wrongPhoneNumberMessage; + } + + async execute(adminData: AdminData): Promise { + // check phone number regex + const isPhoenumberValid = this.isPhoneNumberValid(adminData.phonenumber); + if (!isPhoenumberValid) throw new PhonenumberException(this.wrongPhoneNumberMessage); + + const response = await this.repository.execute(adminData); + + return response; + } + + private isPhoneNumberValid(phonenumber: string) { + const regex = /\(?([0-9]{3})\)?([ .-]?)([0-9]{3})\2([0-9]{4})/; + return regex.test(phonenumber); + } +} diff --git a/src/business-logic/generic/admin-user/common/data/model/adminUserModel.ts b/src/business-logic/generic/admin-user/common/data/model/adminUserModel.ts new file mode 100644 index 0000000..575343b --- /dev/null +++ b/src/business-logic/generic/admin-user/common/data/model/adminUserModel.ts @@ -0,0 +1,13 @@ +import AdminUser from '../../entity/adminUserEntity'; + +export default class AdminUserModel { + adminUserData: AdminUser; + + constructor(adminUserData: AdminUser) { + this.adminUserData = adminUserData; + } + + get() { + return this.adminUserData; + } +} diff --git a/src/business-logic/generic/admin-user/common/entity/adminUserEntity.ts b/src/business-logic/generic/admin-user/common/entity/adminUserEntity.ts new file mode 100644 index 0000000..1b1a6d0 --- /dev/null +++ b/src/business-logic/generic/admin-user/common/entity/adminUserEntity.ts @@ -0,0 +1,13 @@ +export default abstract class AdminUser { + abstract phonenumber: string; + + abstract accessToken: string; + + abstract refreshToken: string; + + abstract expiresIn: number; + + abstract refreshExpiresIn: number; + + abstract tokenType: 'Bearer'; +} diff --git a/src/driven/adapters/auth-admin-login/authAdminLogin.ts b/src/driven/adapters/auth-admin-login/authAdminLogin.ts new file mode 100644 index 0000000..c5d2f8d --- /dev/null +++ b/src/driven/adapters/auth-admin-login/authAdminLogin.ts @@ -0,0 +1,40 @@ +import { apiUrls } from '~/driven/utils/configs/appConfig'; +import { HttpOptionsType } from '~/driven/boundaries/http-boundary/protocols'; +import { HTTPPovider } from '~/driven/boundaries/http-boundary/httpBoundary'; +import { IOtpAuthDrivenPort } from '~/business-logic/generic/admin-user/authentication/otp-auth/port'; +import { OtpAuthResponse } from '~/business-logic/generic/admin-user/authentication/otp-auth/data/reponse-object/otpAuthRO'; +import { OtpAuthDTOReturnType } from '~/business-logic/generic/admin-user/authentication/otp-auth/data/dto/otpAuthDto'; +import AdminUserModel from '~/business-logic/generic/admin-user/common/data/model/adminUserModel'; + +const authAdminLogin = ( + userAdmin: AdminUserModel | null, + updateAccessToken: (newAccessToken: string) => void, + navigateToAuth: () => void, +): IOtpAuthDrivenPort => { + const httpProvider = new HTTPPovider( + { + accessToken: userAdmin && userAdmin.adminUserData.accessToken && null, + refreshToken: userAdmin && userAdmin.adminUserData.refreshToken && null, + }, + updateAccessToken, + navigateToAuth, + ); + const httpHandler = (data: OtpAuthDTOReturnType): Promise => { + const url = apiUrls.generic.authLogin; + const options: HttpOptionsType = { + url, + headers: { + 'Content-Type': 'application/json', + }, + method: 'POST', + data, + }; + return httpProvider.request(options); + }; + + return { + httpHandler, + }; +}; + +export default authAdminLogin; diff --git a/src/driven/adapters/auth-admin-phonenumber/authAdminPhoneNumberDriven.ts b/src/driven/adapters/auth-admin-phonenumber/authAdminPhoneNumberDriven.ts new file mode 100644 index 0000000..c708240 --- /dev/null +++ b/src/driven/adapters/auth-admin-phonenumber/authAdminPhoneNumberDriven.ts @@ -0,0 +1,44 @@ +import { staticMessages } from '~/driven/utils/constants/staticMessages'; +import { apiUrls } from '~/driven/utils/configs/appConfig'; +import { PhonenumberAuthDTOReturnType } from '~/business-logic/generic/admin-user/authentication/phonnumber-auth/data/dto/phonenumberDto'; +import { IPhonenumberAuthPort } from '~/business-logic/generic/admin-user/authentication/phonnumber-auth/port'; +import { HttpOptionsType } from '~/driven/boundaries/http-boundary/protocols'; +import { HTTPPovider } from '~/driven/boundaries/http-boundary/httpBoundary'; +import AdminUserModel from '~/business-logic/generic/admin-user/common/data/model/adminUserModel'; + +const authAdminPhoneNumberDriven = ( + userAdmin: AdminUserModel | null, + updateAccessToken: (newAccessToken: string) => void, + navigateToAuth: () => void, +): IPhonenumberAuthPort => { + // make http handler ready for the phone number + const wrongPhoneNumberMessage = staticMessages.global.errors.phonenumber; + + const httpProvider = new HTTPPovider( + { + accessToken: userAdmin && userAdmin?.adminUserData.accessToken && null, + refreshToken: userAdmin && userAdmin?.adminUserData.refreshToken && null, + }, + updateAccessToken, + navigateToAuth, + ); + const httpHandler = (data: PhonenumberAuthDTOReturnType) => { + const url = apiUrls.generic.authPhonenumber; + const options: HttpOptionsType = { + url, + headers: { + 'Content-Type': 'application/json', + }, + method: 'POST', + data, + }; + return httpProvider.request(options); + }; + + return { + httpHandler, + wrongPhoneNumberMessage, + }; +}; + +export default authAdminPhoneNumberDriven; diff --git a/src/driven/adapters/create-account-adapter/createAccountAdapter.ts b/src/driven/adapters/create-account-adapter/createAccountAdapter.ts new file mode 100644 index 0000000..b16bc97 --- /dev/null +++ b/src/driven/adapters/create-account-adapter/createAccountAdapter.ts @@ -0,0 +1,42 @@ +import { INewUserData } from '~/business-logic/core/users/create-user/create-account/data/dto/protocols'; +import { CreateAcountResponseApi } from '~/business-logic/core/users/create-user/create-account/data/response-object/protocols'; +import createUserPort from '~/business-logic/core/users/create-user/ports'; +import AdminUserModel from '~/business-logic/generic/admin-user/common/data/model/adminUserModel'; +import { HTTPPovider } from '~/driven/boundaries/http-boundary/httpBoundary'; +import { HttpOptionsType } from '~/driven/boundaries/http-boundary/protocols'; +import { apiUrls } from '~/driven/utils/configs/appConfig'; + +const createAccountAdapter = ( + userAdmin: AdminUserModel, + updateAccessToken: (newAccessToken: string) => void, + navigateToAuth: () => void, +): createUserPort['httpAccountHandler'] => { + // make url + const url = apiUrls.core.createUserAccount; + // call http provider + const httpProvider = new HTTPPovider( + { + accessToken: userAdmin.adminUserData.accessToken, + refreshToken: userAdmin.adminUserData.refreshToken, + }, + updateAccessToken, + navigateToAuth, + ); + + const httpHandler = (newUserData: INewUserData) => { + // api options + const httpOptions: HttpOptionsType = { + url, + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + data: newUserData, + }; + return httpProvider.request(httpOptions); + }; + + return httpHandler; +}; + +export default createAccountAdapter; diff --git a/src/driven/adapters/create-profile-adapter/createProfileAdapter.ts b/src/driven/adapters/create-profile-adapter/createProfileAdapter.ts new file mode 100644 index 0000000..32afa07 --- /dev/null +++ b/src/driven/adapters/create-profile-adapter/createProfileAdapter.ts @@ -0,0 +1,41 @@ +import { CreateProfileDtoReturnType } from '~/business-logic/core/users/create-user/create-profile/data/dto/protocols'; +import createUserPort from '~/business-logic/core/users/create-user/ports'; +import AdminUserModel from '~/business-logic/generic/admin-user/common/data/model/adminUserModel'; +import { HTTPPovider } from '~/driven/boundaries/http-boundary/httpBoundary'; +import { HttpOptionsType } from '~/driven/boundaries/http-boundary/protocols'; +import { apiUrls } from '~/driven/utils/configs/appConfig'; + +const createProfileAdapter = ( + userAdmin: AdminUserModel, + updateAccessToken: (newAccessToken: string) => void, + navigateToAuth: () => void, +): createUserPort['httpProfileHandler'] => { + // make url + const url = apiUrls.core.createUserProfile; + // call http provider + const httpProvider = new HTTPPovider( + { + accessToken: userAdmin.adminUserData.accessToken, + refreshToken: userAdmin.adminUserData.refreshToken, + }, + updateAccessToken, + navigateToAuth, + ); + + const httpHandler = (newAccountData: CreateProfileDtoReturnType) => { + // api options + const httpOptions: HttpOptionsType = { + url, + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + data: newAccountData, + }; + return httpProvider.request(httpOptions); + }; + + return httpHandler; +}; + +export default createProfileAdapter; diff --git a/src/driven/adapters/get-places-adapter/getPlacesAdapter.ts b/src/driven/adapters/get-places-adapter/getPlacesAdapter.ts new file mode 100644 index 0000000..5d6b52c --- /dev/null +++ b/src/driven/adapters/get-places-adapter/getPlacesAdapter.ts @@ -0,0 +1,40 @@ +import { GetPlacesResponse } from '~/business-logic/core/places/get-places/data/response-object/protocols'; +import IGetPlacesPort from '~/business-logic/core/places/get-places/port'; +import { HTTPPovider } from '~/driven/boundaries/http-boundary/httpBoundary'; +import { HttpOptionsType } from '~/driven/boundaries/http-boundary/protocols'; +import { apiUrls } from '~/driven/utils/configs/appConfig'; +import AdminUserModel from '~/business-logic/generic/admin-user/common/data/model/adminUserModel'; +import { getPlacesAdapterReturnType } from './protocols'; + +const getPlacesAdapter = ( + userAdmin: AdminUserModel, + updateAccessToken: (newAccessToken: string) => void, + navigateToAuth: () => void, +): IGetPlacesPort & getPlacesAdapterReturnType => { + // url of api + const url = apiUrls.core.getPlaces; + // make the options of request + const options: HttpOptionsType = { + url, + method: 'GET', + }; + // make the httpHandler + const httpProvider = new HTTPPovider( + { + accessToken: userAdmin.adminUserData.accessToken, + refreshToken: userAdmin.adminUserData.refreshToken, + }, + updateAccessToken, + navigateToAuth, + ); + + const httpHandler = async () => httpProvider.request(options); + + // return the method + return { + httpHandler, + url, + }; +}; + +export default getPlacesAdapter; diff --git a/src/driven/adapters/get-places-adapter/protocols.ts b/src/driven/adapters/get-places-adapter/protocols.ts new file mode 100644 index 0000000..4a89776 --- /dev/null +++ b/src/driven/adapters/get-places-adapter/protocols.ts @@ -0,0 +1,3 @@ +export type getPlacesAdapterReturnType = { + url: string; +}; diff --git a/src/driven/adapters/get-users-adapter/getUsersAdapter.ts b/src/driven/adapters/get-users-adapter/getUsersAdapter.ts new file mode 100644 index 0000000..e46541f --- /dev/null +++ b/src/driven/adapters/get-users-adapter/getUsersAdapter.ts @@ -0,0 +1,40 @@ +import { apiUrls } from '~/driven/utils/configs/appConfig'; +import { HttpOptionsType } from '~/driven/boundaries/http-boundary/protocols'; +import { HTTPPovider } from '~/driven/boundaries/http-boundary/httpBoundary'; +import { GetUsersResponse } from '~/business-logic/core/users/get-users/data/response-object/protocols'; +import IGetUsersPort from '~/business-logic/core/users/get-users/ports'; +import AdminUserModel from '~/business-logic/generic/admin-user/common/data/model/adminUserModel'; +import { getUsersAdapterReturnType } from './protocols'; + +const getUsersAdapter = ( + userAdmin: AdminUserModel, + updateAccessToken: (newAccessToken: string) => void, + navigateToAuth: () => void, +): IGetUsersPort & getUsersAdapterReturnType => { + // url of api + const url = apiUrls.core.getUsers; + // make the options of request + const options: HttpOptionsType = { + url, + method: 'GET', + }; + // make the httpHandler + const httpProvider = new HTTPPovider( + { + accessToken: userAdmin.adminUserData.accessToken, + refreshToken: userAdmin.adminUserData.refreshToken, + }, + updateAccessToken, + navigateToAuth, + ); + + const httpHandler = async () => httpProvider.request(options); + + // return the method + return { + httpHandler, + url, + }; +}; + +export default getUsersAdapter; diff --git a/src/driven/adapters/get-users-adapter/protocols.ts b/src/driven/adapters/get-users-adapter/protocols.ts new file mode 100644 index 0000000..65c2750 --- /dev/null +++ b/src/driven/adapters/get-users-adapter/protocols.ts @@ -0,0 +1,3 @@ +export type getUsersAdapterReturnType = { + url: string; +}; diff --git a/src/driven/boundaries/http-boundary/httpBoundary.ts b/src/driven/boundaries/http-boundary/httpBoundary.ts index f5f5ef5..3f65647 100644 --- a/src/driven/boundaries/http-boundary/httpBoundary.ts +++ b/src/driven/boundaries/http-boundary/httpBoundary.ts @@ -1,11 +1,88 @@ -import axios, { AxiosRequestConfig } from 'axios'; +/* eslint-disable consistent-return */ +/* eslint-disable no-param-reassign */ +import axios, { AxiosInstance } from 'axios'; import { staticMessages } from '~/driven/utils/constants/staticMessages'; +import { ApiGlobalResponseObject } from '~/driven/utils/protocols/serviceProtocols'; +import { apiUrls } from '~/driven/utils/configs/appConfig'; +import { HttpOptionsType } from './protocols'; -export class HTTPBoundary { - async request(options: AxiosRequestConfig) { - const response = await axios(options); +interface IUserTokens { + accessToken: string | null | undefined; + refreshToken: string | null | undefined; +} +export class HTTPPovider { + private userTokens: IUserTokens; + + private updateAccessToken: (newAccessToken: string) => void; + + private navigateToAuth: () => void; + + constructor( + userTokens: IUserTokens, + updateAccessToken: (newAccessToken: string) => void, + navigateToAuth: () => void, + ) { + this.userTokens = userTokens; + this.updateAccessToken = updateAccessToken; + this.navigateToAuth = navigateToAuth; + } + + private initalizeAxiosInstance() { + const instance = axios.create(); + + return instance; + } + + private handleRequestInterceptor() { + const axiosInstance = this.initalizeAxiosInstance(); + axiosInstance.interceptors.request.use((config) => { + config.headers.Authorization = `Bearer ${this.userTokens.accessToken}`; + return config; + }); + + return axiosInstance; + } + + async request(customOptions: HttpOptionsType) { + const axiosInstance = this.handleRequestInterceptor(); + this.responseIncepter(axiosInstance); + + const response = await axiosInstance>(customOptions); if (!response) throw new Error(staticMessages.service.errors[500]); - return response.data; + return response.data.data; + } + + /** + * @todo should be handled in business logic + */ + private async refreshAccessToken() { + try { + const response = await axios.post(apiUrls.generic.authRefresh, { + refresh_token: this.userTokens.refreshToken, + }); + this.updateAccessToken(response.data.access_token as string); + return response.data.access_token; + } catch (err) { + this.navigateToAuth(); + } + } + + private responseIncepter(axiosInstance: AxiosInstance) { + axiosInstance.interceptors.response.use( + (response) => response, + (error) => { + const originalRequest = error.config; + + if (error.response.status === 401 && error.response.message === 'Unauthorized') { + const newAccessToken = this.refreshAccessToken().then(() => { + originalRequest.headers.Authorization = `Bearer ${newAccessToken}`; + return axios(originalRequest); + }); + } + + return Promise.reject(error); + }, + ); } } diff --git a/src/driven/boundaries/http-boundary/protocols.ts b/src/driven/boundaries/http-boundary/protocols.ts new file mode 100644 index 0000000..6690666 --- /dev/null +++ b/src/driven/boundaries/http-boundary/protocols.ts @@ -0,0 +1,3 @@ +import { AxiosRequestConfig } from 'axios'; + +export type HttpOptionsType = AxiosRequestConfig; diff --git a/src/driven/boundaries/state-management-boundary/adapters/adapter.ts b/src/driven/boundaries/state-management-boundary/adapters/adapter.ts deleted file mode 100644 index 354c92b..0000000 --- a/src/driven/boundaries/state-management-boundary/adapters/adapter.ts +++ /dev/null @@ -1,8 +0,0 @@ -import store from '../store/store'; -import userSlice from '../slices/userSlice'; -import { UserState } from '../slices/protocols/userSliceProtocols'; - -export const userAdapter = { - get: store.getState().user, - update: (user: UserState) => store.dispatch(userSlice.actions.update(user)), -}; diff --git a/src/driven/boundaries/state-management-boundary/index.ts b/src/driven/boundaries/state-management-boundary/index.ts deleted file mode 100644 index e1751e0..0000000 --- a/src/driven/boundaries/state-management-boundary/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { userAdapter } from './adapters/adapter'; diff --git a/src/driven/boundaries/state-management-boundary/slices/protocols/userSliceProtocols.ts b/src/driven/boundaries/state-management-boundary/slices/protocols/userSliceProtocols.ts deleted file mode 100644 index 583c7f0..0000000 --- a/src/driven/boundaries/state-management-boundary/slices/protocols/userSliceProtocols.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { UserModel } from '~/business-logic/generic/user/common/domain/model/userModel'; - -export type UserState = UserModel; diff --git a/src/driven/boundaries/state-management-boundary/slices/userSlice.ts b/src/driven/boundaries/state-management-boundary/slices/userSlice.ts deleted file mode 100644 index 0f0d37d..0000000 --- a/src/driven/boundaries/state-management-boundary/slices/userSlice.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { createSlice, SliceCaseReducers } from '@reduxjs/toolkit'; -import { UserState } from './protocols/userSliceProtocols'; - -export const userStateName = 'user'; -const userSlice = createSlice>({ - name: userStateName, - initialState: null, - reducers: { - update: (state, action) => { - if (!action.payload) return state; - return { - ...state, - ...action.payload, - }; - }, - }, -}); - -export default userSlice; diff --git a/src/driven/boundaries/state-management-boundary/store/store.ts b/src/driven/boundaries/state-management-boundary/store/store.ts deleted file mode 100644 index ccf0b9c..0000000 --- a/src/driven/boundaries/state-management-boundary/store/store.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { combineReducers, configureStore } from '@reduxjs/toolkit'; -import userSlice, { userStateName } from '../slices/userSlice'; - -const combinedReducers = combineReducers({ - [userStateName]: userSlice.reducer, -}); - -const store = configureStore({ - reducer: combinedReducers, -}); - -export default store; diff --git a/src/driven/boundaries/state-management/index.ts b/src/driven/boundaries/state-management/index.ts new file mode 100644 index 0000000..7668bcb --- /dev/null +++ b/src/driven/boundaries/state-management/index.ts @@ -0,0 +1,3 @@ +import StateManagementService from './stateManagementService'; + +export default StateManagementService; diff --git a/src/driven/boundaries/state-management/stateManagementProvider.ts b/src/driven/boundaries/state-management/stateManagementProvider.ts new file mode 100644 index 0000000..c430681 --- /dev/null +++ b/src/driven/boundaries/state-management/stateManagementProvider.ts @@ -0,0 +1,6 @@ +export default abstract class StateManagementProvider { + abstract useGetQuery( + key: string, + httpHandler: () => Promise, + ): { data: DataType | undefined; isLoading: boolean; error?: string }; +} diff --git a/src/driven/boundaries/state-management/stateManagementService.ts b/src/driven/boundaries/state-management/stateManagementService.ts new file mode 100644 index 0000000..261c358 --- /dev/null +++ b/src/driven/boundaries/state-management/stateManagementService.ts @@ -0,0 +1,26 @@ +import StateManagementProvider from './stateManagementProvider'; +import SwrBoundary from './swrBoundary'; + +export default class StateManagementService implements StateManagementProvider { + private provider: StateManagementProvider; + + constructor(provider: StateManagementProvider) { + this.provider = provider; + } + + static swr() { + const stateManagement = new StateManagementService(new SwrBoundary()); + return stateManagement; + } + + useGetQuery( + key: string, + httpHandler: () => Promise, + ): { + data: DataType | undefined; + isLoading: boolean; + error?: string | undefined; + } { + return this.provider.useGetQuery(key, httpHandler); + } +} diff --git a/src/driven/boundaries/state-management/swrBoundary.ts b/src/driven/boundaries/state-management/swrBoundary.ts new file mode 100644 index 0000000..3dc3958 --- /dev/null +++ b/src/driven/boundaries/state-management/swrBoundary.ts @@ -0,0 +1,15 @@ +import useSwr from 'swr'; +import StateManagementProvider from './stateManagementProvider'; + +export default class SwrBoundary implements StateManagementProvider { + useGetQuery( + key: string, + httpHandler: () => Promise, + ): { + data: DataType | undefined; + isLoading: boolean; + error?: string | undefined; + } { + return useSwr(key, httpHandler); + } +} diff --git a/src/driven/boundaries/storage-boundary/index.ts b/src/driven/boundaries/storage-boundary/index.ts new file mode 100644 index 0000000..19578bc --- /dev/null +++ b/src/driven/boundaries/storage-boundary/index.ts @@ -0,0 +1,3 @@ +import StorageService from './storageService'; + +export default StorageService; diff --git a/src/driven/boundaries/storage-boundary/localStorageProvider.ts b/src/driven/boundaries/storage-boundary/localStorageProvider.ts new file mode 100644 index 0000000..96a90df --- /dev/null +++ b/src/driven/boundaries/storage-boundary/localStorageProvider.ts @@ -0,0 +1,18 @@ +import StorageProvider from './storageProvider'; + +export default class LocalStorageProvider implements StorageProvider { + setData(key: string, value: ValueType): void { + const jsonValue = JSON.stringify(value); + localStorage.setItem(key, jsonValue); + } + + getData(key: string): ValueType | null { + const jsonValue = localStorage.getItem(key); + const value: ValueType = jsonValue && JSON.parse(jsonValue); + return value; + } + + deleteData(key: string): void { + localStorage.removeItem(key); + } +} diff --git a/src/driven/boundaries/storage-boundary/storageProvider.ts b/src/driven/boundaries/storage-boundary/storageProvider.ts new file mode 100644 index 0000000..26b5cd1 --- /dev/null +++ b/src/driven/boundaries/storage-boundary/storageProvider.ts @@ -0,0 +1,7 @@ +export default abstract class StorageProvider { + abstract setData(key: string, value: ValueType): void; + + abstract getData(key: string): ValueType | null; + + abstract deleteData(key: string): void; +} diff --git a/src/driven/boundaries/storage-boundary/storageService.ts b/src/driven/boundaries/storage-boundary/storageService.ts new file mode 100644 index 0000000..7e73cf7 --- /dev/null +++ b/src/driven/boundaries/storage-boundary/storageService.ts @@ -0,0 +1,27 @@ +import LocalStorageProvider from './localStorageProvider'; +import StorageProvider from './storageProvider'; + +export default class StorageService implements StorageProvider { + private provider: StorageProvider; + + constructor(provider: StorageProvider) { + this.provider = provider; + } + + static localStorage(): StorageService { + const localStorageService = new StorageService(new LocalStorageProvider()); + return localStorageService; + } + + setData(key: string, value: ValueType): void { + return this.provider.setData(key, value); + } + + getData(key: string): ValueType | null { + return this.provider.getData(key); + } + + deleteData(key: string): void { + this.provider.deleteData(key); + } +} diff --git a/src/driven/utils/components/Notification/Notification.tsx b/src/driven/utils/components/Notification/Notification.tsx new file mode 100644 index 0000000..9f62aca --- /dev/null +++ b/src/driven/utils/components/Notification/Notification.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +export interface notifInterface { + type: 'error' | 'success' | 'warning'; + message: string; + time?: number; + onCloseCallback?: () => unknown; +} +export default function Notification({ message, type, time = 5, onCloseCallback }: notifInterface) { + const notifRef = React.useRef(null); + const el = React.useRef(document.createElement('div')); + + React.useEffect(() => { + const portal = document.getElementById('root'); + portal?.appendChild(el.current); + + setTimeout(() => { + el.current.remove(); + if (typeof onCloseCallback !== 'undefined') onCloseCallback(); + }, 1000 * time); + + return () => { + if (typeof onCloseCallback !== 'undefined') onCloseCallback(); + return el.current?.remove(); + }; + }, []); + + return ReactDOM.createPortal( +
+ {message} +
, + el.current, + ); +} diff --git a/src/driven/utils/components/buttons/primary-button/PrimaryButton.tsx b/src/driven/utils/components/buttons/primary-button/PrimaryButton.tsx index 967f21f..d2da017 100644 --- a/src/driven/utils/components/buttons/primary-button/PrimaryButton.tsx +++ b/src/driven/utils/components/buttons/primary-button/PrimaryButton.tsx @@ -1,14 +1,21 @@ -import React from 'react' +import React from 'react'; interface IPrimaryButtonProps { title: string; onClick: (e: React.MouseEvent) => void; className?: string; + isDisabled?: boolean; } export default function PrimaryButton(props: IPrimaryButtonProps) { - const { onClick, title, className } = props; + const { onClick, title, className, isDisabled = false } = props; return ( - - ) + + ); } diff --git a/src/driven/utils/components/inputs/simple-input/SimpleInput.tsx b/src/driven/utils/components/inputs/simple-input/SimpleInput.tsx index 4fd7158..095cef8 100644 --- a/src/driven/utils/components/inputs/simple-input/SimpleInput.tsx +++ b/src/driven/utils/components/inputs/simple-input/SimpleInput.tsx @@ -1,16 +1,41 @@ -import React from 'react' +import React from 'react'; -interface ISimpleInput { - title: string; +export type SetStateInputMethod = (name: NameType, newValue: string) => void; + +interface ISimpleInput { + inputData: { + title: string; + name: string; + }; className?: string; + stateHanlder: { + state: string; + setState: SetStateInputMethod; + }; } -export default function SimpleInput(props: ISimpleInput) { - const { title, className } = props; +export default function SimpleInput(props: ISimpleInput) { + const { className, inputData, stateHanlder } = props; + const { name, title } = inputData; + const { setState, state } = stateHanlder; + + const handleInputChange = (e: React.ChangeEvent) => { + const { value, name: inputName } = e.target; + setState(inputName as NameType, value); + }; + return (
- - + +
- ) + ); } diff --git a/src/driven/utils/components/loading/Loading.tsx b/src/driven/utils/components/loading/Loading.tsx new file mode 100644 index 0000000..cb43672 --- /dev/null +++ b/src/driven/utils/components/loading/Loading.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import style from './style.module.css'; + +export default function Loading() { + return ( +
+
+
+
+
+
+ ); +} diff --git a/src/driven/utils/components/loading/style.module.css b/src/driven/utils/components/loading/style.module.css new file mode 100644 index 0000000..df21453 --- /dev/null +++ b/src/driven/utils/components/loading/style.module.css @@ -0,0 +1,35 @@ +.ldsRing { + display: inline-block; + position: relative; + width: 80px; + height: 80px; +} +.ldsRing div { + box-sizing: border-box; + display: block; + position: absolute; + width: 2rem; + height: 2rem; + margin: 4px; + border: 4px solid #fff; + border-radius: 50%; + animation: ldsRing 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; + border-color: var(--color-primary-main) transparent transparent transparent; +} +.ldsRing div:nth-child(1) { + animation-delay: -0.45s; +} +.ldsRing div:nth-child(2) { + animation-delay: -0.3s; +} +.ldsRing div:nth-child(3) { + animation-delay: -0.15s; +} +@keyframes ldsRing { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} \ No newline at end of file diff --git a/src/driven/utils/components/page-title/pageTitle.tsx b/src/driven/utils/components/page-title/pageTitle.tsx index ca0f4d1..519ef93 100644 --- a/src/driven/utils/components/page-title/pageTitle.tsx +++ b/src/driven/utils/components/page-title/pageTitle.tsx @@ -1,13 +1,11 @@ -import React from 'react' +import React from 'react'; interface IPageTitleProps { - title: string; - className?: string; + title: string; + className?: string; } export default function PageTitle(props: IPageTitleProps) { - const { title, className } = props; - return ( -
{title}
- ) + const { title, className } = props; + return
{title}
; } diff --git a/src/driven/utils/configs/appConfig.ts b/src/driven/utils/configs/appConfig.ts index dac6474..93bd570 100644 --- a/src/driven/utils/configs/appConfig.ts +++ b/src/driven/utils/configs/appConfig.ts @@ -1,27 +1,42 @@ -import { icons } from "../constants/assertUrls"; -import { staticMessages } from "../constants/staticMessages"; +import { icons } from '../constants/assertUrls'; +import { ENVs } from '../constants/envs'; +import { staticMessages } from '../constants/staticMessages'; -export const appConfig = {}; +export const appConfig = { + adminUserStorageKey: 'adminUser', +}; export const routes = { usersList: '/', - createUser: '/create-user' + createUser: '/create-user', + authentication: '/auth', }; export const routesData = { usersList: { path: routes.usersList, icon: icons.users, - title: staticMessages.global.users + title: staticMessages.global.users, }, createUser: { path: routes.createUser, icon: icons.createUser, - title: staticMessages.global.createUser - } -} - -const baseApiUrl = import.meta.env.BASE_API_URL; -export const apiUrls = { - + title: staticMessages.global.createUser, + }, +}; + +const baseApiUrl = ENVs.apiOrigin; +export const apiUrls = { + core: { + getPlaces: `${baseApiUrl}${ENVs.apiGetPlaces}`, + getUsers: `${baseApiUrl}${ENVs.apiGetUsers}`, + createUserAccount: `${baseApiUrl}${ENVs.apiCreateUserAccount}`, + createUserProfile: `${baseApiUrl}${ENVs.apiCreateUserProfile}`, + createMember: `${baseApiUrl}${ENVs.apiCreateMember}`, + }, + generic: { + authPhonenumber: `${ENVs.apiAuthOrigin}${ENVs.apiAuthPhonenumber}`, + authLogin: `${ENVs.apiAuthOrigin}${ENVs.apiAuthLogin}`, + authRefresh: `${ENVs.apiAuthOrigin}${ENVs.apiAuthRefresh}`, + }, }; diff --git a/src/driven/utils/constants/assertUrls.ts b/src/driven/utils/constants/assertUrls.ts index 0e1fdfa..66c35ac 100644 --- a/src/driven/utils/constants/assertUrls.ts +++ b/src/driven/utils/constants/assertUrls.ts @@ -1,7 +1,8 @@ const baseAssetsUrl = 'assets/'; -const baseIconsUrl = baseAssetsUrl + 'icons/'; +const baseIconsUrl = `${baseAssetsUrl}icons/`; export const icons = { - logo: baseIconsUrl + 'logo.svg', - users: baseIconsUrl + 'users.svg', - createUser: baseIconsUrl + 'createuser.svg' -} \ No newline at end of file + logo: `${baseIconsUrl}logo.svg`, + logoBlack: `${baseIconsUrl}logo-black.svg`, + users: `${baseIconsUrl}users.svg`, + createUser: `${baseIconsUrl}createuser.svg`, +}; diff --git a/src/driven/utils/constants/envs.ts b/src/driven/utils/constants/envs.ts new file mode 100644 index 0000000..102978c --- /dev/null +++ b/src/driven/utils/constants/envs.ts @@ -0,0 +1,12 @@ +export const ENVs = { + apiOrigin: process.env.VITE_API_ORIGIN, + apiAuthOrigin: process.env.VITE_API_AUTH_ORIGIN, + apiAuthPhonenumber: process.env.VITE_API_AUTH_PHONENUMBER, + apiAuthLogin: process.env.VITE_API_AUTH_LOGIN, + apiAuthRefresh: process.env.VITE_API_AUTH_REFRESH, + apiGetPlaces: process.env.VITE_API_PLACES, + apiGetUsers: process.env.VITE_API_USERS, + apiCreateUserAccount: process.env.VITE_API_USERS_ACCOUNT, + apiCreateUserProfile: process.env.VITE_API_USERS_PROFILE, + apiCreateMember: process.env.VITE_API_CREATE_MEMBER, +}; diff --git a/src/driven/utils/constants/staticMessages.ts b/src/driven/utils/constants/staticMessages.ts index 4641f23..3acc2a0 100644 --- a/src/driven/utils/constants/staticMessages.ts +++ b/src/driven/utils/constants/staticMessages.ts @@ -2,18 +2,28 @@ export const staticMessages = { global: { errors: { input: 'please fill all inputs correctly', + phonenumber: 'please fill the valid number', + otp: 'please fill the otp fields correctly', }, users: 'Users', submit: 'Submit', - fistname: 'Firstname', + firstname: 'Firstname', lastname: 'Lastname', place_id: 'Place id', title: 'title', status: 'Status', + placeType: 'Place Type', address: 'Address', qrCode: 'qrCode', createUser: 'Create user', - phoneNumber: 'Phone Number' + phonenumber: 'Phone Number', + enterPanel: 'Enter to Panel', + enterPhoneNumber: 'Enter your phone number', + enterOtpCode: 'Enter your Otp Code', + success: { + createUser: 'user created successfully', + createMember: 'member created successfully', + }, }, service: { errors: { diff --git a/src/driven/utils/helpers/contexts/userContext.tsx b/src/driven/utils/helpers/contexts/userContext.tsx new file mode 100644 index 0000000..f53331d --- /dev/null +++ b/src/driven/utils/helpers/contexts/userContext.tsx @@ -0,0 +1,17 @@ +/* eslint-disable react/jsx-no-constructed-context-values */ +import React, { useState } from 'react'; +import AdminUserModel from '~/business-logic/generic/admin-user/common/data/model/adminUserModel'; + +export interface IUserContext { + user: AdminUserModel | null; + setUser: React.Dispatch> | null; +} +export const UserContext = React.createContext({ user: null, setUser: null }); + +export function UserProvider({ children }: React.PropsWithChildren) { + const [user, setUser] = useState(null); + + return {children}; +} + +export const useUser = () => React.useContext(UserContext); diff --git a/src/driven/utils/helpers/globalHelpers.ts b/src/driven/utils/helpers/globalHelpers.ts index cfbe1e1..92fb772 100644 --- a/src/driven/utils/helpers/globalHelpers.ts +++ b/src/driven/utils/helpers/globalHelpers.ts @@ -1,4 +1,9 @@ +import StateManagementService from '~/driven/boundaries/state-management'; +import StorageService from '~/driven/boundaries/storage-boundary'; +import { NavigateFunction } from 'react-router-dom'; +import { appConfig, routes } from '../configs/appConfig'; import { errorHandlingStateTypes, UIErrorHandling } from './protocols/globalHelpersProtocols'; +import { IUserContext } from './contexts/userContext'; export const UIErrorHandlingFactory = ({ state, @@ -13,3 +18,33 @@ export const UIErrorHandlingFactory = ({ message, state, }); + +export const prepareStateManagementForVM = (apiUrl: string, model: () => Promise) => { + const stateManagement = StateManagementService.swr(); + + const useGetPlacesList = () => stateManagement.useGetQuery(apiUrl, model); + + return useGetPlacesList; +}; + +export const checkPhoneNumberInput = (newValue: string) => { + return (Number.isFinite(+newValue) || newValue === '+') && newValue.length <= 12; +}; + +export const updateAccessToken = (newAccessToken: string, userContext: IUserContext) => { + const { setUser, user } = userContext; + if (!user || !setUser) return; + const storage = StorageService.localStorage(); + user.adminUserData.accessToken = newAccessToken; + storage.setData(appConfig.adminUserStorageKey, user); + setUser(user); +}; + +export const navigateToAuth = (userCtx: IUserContext, navigate: NavigateFunction) => { + const { setUser } = userCtx; + const storage = StorageService.localStorage(); + storage.deleteData(appConfig.adminUserStorageKey); + if (!setUser) return; + setUser(null); + navigate(routes.authentication); +}; diff --git a/src/driven/utils/helpers/hooks/getNavigatorAndAccessTokenUpdator.ts b/src/driven/utils/helpers/hooks/getNavigatorAndAccessTokenUpdator.ts new file mode 100644 index 0000000..077d49a --- /dev/null +++ b/src/driven/utils/helpers/hooks/getNavigatorAndAccessTokenUpdator.ts @@ -0,0 +1,24 @@ +import { useNavigate } from 'react-router-dom'; +import { useUser } from '../contexts/userContext'; +import { navigateToAuth, updateAccessToken } from '../globalHelpers'; + +const useGetNavigatorAndTokenUpdater = () => { + const userData = useUser(); + const navigate = useNavigate(); + + const notLoginAuth = () => { + navigateToAuth(userData, navigate); + }; + + const accessTokenUpdateHandler = (newAccessToken: string) => { + updateAccessToken(newAccessToken, userData); + }; + return { + notLoginAuth, + accessTokenUpdateHandler, + userData, + navigate, + }; +}; + +export default useGetNavigatorAndTokenUpdater; diff --git a/src/driven/utils/helpers/repository-handler/repositoryHandler.ts b/src/driven/utils/helpers/repository-handler/repositoryHandler.ts new file mode 100644 index 0000000..163b4d7 --- /dev/null +++ b/src/driven/utils/helpers/repository-handler/repositoryHandler.ts @@ -0,0 +1,9 @@ +type HttpHandler = (newUser: NewDataToAdd) => Promise; + +export default class RepositoryHandler { + protected httpHandler: HttpHandler; + + constructor(httpHandler: HttpHandler) { + this.httpHandler = httpHandler; + } +} diff --git a/src/driven/utils/protocols/serviceProtocols.ts b/src/driven/utils/protocols/serviceProtocols.ts index 3e7fd0b..152c306 100644 --- a/src/driven/utils/protocols/serviceProtocols.ts +++ b/src/driven/utils/protocols/serviceProtocols.ts @@ -1,9 +1,9 @@ export type RequestMethods = 'get' | 'post' | 'put' | 'delete'; -export type apiGlobalResponseObject = { +export type ApiGlobalResponseObject = { type: 'Success' | 'client Error' | string; status: 200 | 400 | 401 | 500 | number; message: string; description: string; data: DataType; -} \ No newline at end of file +}; diff --git a/src/driving/application/core/common/table-row/index.tsx b/src/driving/application/core/common/table-row/index.tsx index e31ec56..353b34c 100644 --- a/src/driving/application/core/common/table-row/index.tsx +++ b/src/driving/application/core/common/table-row/index.tsx @@ -1,3 +1,3 @@ -import TableRow from "./infra/TableRowInfra"; +import TableRow from './infra/TableRowInfra'; -export default TableRow; \ No newline at end of file +export default TableRow; diff --git a/src/driving/application/core/common/table-row/infra/TableRowInfra.tsx b/src/driving/application/core/common/table-row/infra/TableRowInfra.tsx index cc375cb..4ecd83b 100644 --- a/src/driving/application/core/common/table-row/infra/TableRowInfra.tsx +++ b/src/driving/application/core/common/table-row/infra/TableRowInfra.tsx @@ -1,13 +1,13 @@ -import React from 'react' -import { ITableRowInfra } from './protocols' +import React from 'react'; import useTableRowVM from '../viewmodel/tableRowVM'; import TableRowView from '../view/TableRow'; +import { ITableRowInfra } from './protocols'; export default function TableRow(props: ITableRowInfra) { const { rowData, selectedRowId, setSelectedRowId } = props; const { rowId } = rowData; - const { isRowSelected } = useTableRowVM({selectedRowId, rowId}); + const { isRowSelected } = useTableRowVM({ selectedRowId, rowId }); - return + return ; } diff --git a/src/driving/application/core/common/table-row/infra/protocols.ts b/src/driving/application/core/common/table-row/infra/protocols.ts index 7f47ab5..c2a94de 100644 --- a/src/driving/application/core/common/table-row/infra/protocols.ts +++ b/src/driving/application/core/common/table-row/infra/protocols.ts @@ -1,8 +1,8 @@ export interface ITableRowInfra { selectedRowId: string; rowData: { - rowItemsTitle: string[]; + rowItemsTitle: (string | null)[]; rowId: string; - } - setSelectedRowId: React.Dispatch> -} \ No newline at end of file + }; + setSelectedRowId: React.Dispatch>; +} diff --git a/src/driving/application/core/common/table-row/view/TableRow.tsx b/src/driving/application/core/common/table-row/view/TableRow.tsx index b84f925..5156de4 100644 --- a/src/driving/application/core/common/table-row/view/TableRow.tsx +++ b/src/driving/application/core/common/table-row/view/TableRow.tsx @@ -1,4 +1,5 @@ -import React from 'react' +/* eslint-disable react/no-array-index-key */ +import React from 'react'; import RowItem from './table-row-item/view/RowItem'; import { ITableRowProps } from './protocols'; @@ -6,10 +7,15 @@ export default function TableRowView(props: ITableRowProps) { const { isSelected, setSelectedRowId, rowData } = props; const { rowId, rowItemsTitle } = rowData; const columns = rowItemsTitle.map((rowItemTitle, index) => { - return - }) + return ( + + ); + }); - return ( - setSelectedRowId(rowId)}>{ columns } - ) + return setSelectedRowId(rowId)}>{columns}; } diff --git a/src/driving/application/core/common/table-row/view/protocols.ts b/src/driving/application/core/common/table-row/view/protocols.ts index db7d5dc..eeba1fb 100644 --- a/src/driving/application/core/common/table-row/view/protocols.ts +++ b/src/driving/application/core/common/table-row/view/protocols.ts @@ -1,8 +1,8 @@ export interface ITableRowProps { isSelected: boolean; rowData: { - rowItemsTitle: string[]; + rowItemsTitle: (string | null)[]; rowId: string; - } - setSelectedRowId: React.Dispatch> -} \ No newline at end of file + }; + setSelectedRowId: React.Dispatch>; +} diff --git a/src/driving/application/core/common/table-row/view/table-row-item/view/RowItem.tsx b/src/driving/application/core/common/table-row/view/table-row-item/view/RowItem.tsx index 3a98103..e15e71b 100644 --- a/src/driving/application/core/common/table-row/view/table-row-item/view/RowItem.tsx +++ b/src/driving/application/core/common/table-row/view/table-row-item/view/RowItem.tsx @@ -1,9 +1,9 @@ -import React, { useState } from 'react' +import React from 'react'; interface IRowItemProp { - title: string; + title: string | null; hasCheckbox: boolean; - isSelected: boolean; + isSelected: boolean; } export default function RowItem(props: IRowItemProp) { @@ -11,14 +11,17 @@ export default function RowItem(props: IRowItemProp) { return (
- { - hasCheckbox && - - - - } - { title } + {hasCheckbox && ( + + + + )} + {title}
- ) + ); } diff --git a/src/driving/application/core/common/table-row/viewmodel/protocols.ts b/src/driving/application/core/common/table-row/viewmodel/protocols.ts index ac8d8f2..a989642 100644 --- a/src/driving/application/core/common/table-row/viewmodel/protocols.ts +++ b/src/driving/application/core/common/table-row/viewmodel/protocols.ts @@ -5,4 +5,4 @@ export interface IUserTableRowVM { export type tableRowVMReturnType = { isRowSelected: boolean; -} +}; diff --git a/src/driving/application/core/common/table-row/viewmodel/tableRowVM.ts b/src/driving/application/core/common/table-row/viewmodel/tableRowVM.ts index 340c23b..9828d8e 100644 --- a/src/driving/application/core/common/table-row/viewmodel/tableRowVM.ts +++ b/src/driving/application/core/common/table-row/viewmodel/tableRowVM.ts @@ -1,10 +1,10 @@ -import { IUserTableRowVM, tableRowVMReturnType } from "./protocols"; +import { IUserTableRowVM, tableRowVMReturnType } from './protocols'; const useTableRowVM = (dependencies: IUserTableRowVM): tableRowVMReturnType => { const { rowId, selectedRowId } = dependencies; return { - isRowSelected: rowId === selectedRowId - } -} + isRowSelected: rowId === selectedRowId, + }; +}; -export default useTableRowVM; \ No newline at end of file +export default useTableRowVM; diff --git a/src/driving/application/core/create-user/index.tsx b/src/driving/application/core/create-user/index.tsx index 1f35bc6..774a305 100644 --- a/src/driving/application/core/create-user/index.tsx +++ b/src/driving/application/core/create-user/index.tsx @@ -1,3 +1,3 @@ -import CreateUser from "./infra/CreateUser"; +import CreateUser from './infra/CreateUser'; -export default CreateUser; \ No newline at end of file +export default CreateUser; diff --git a/src/driving/application/core/create-user/infra/CreateUser.tsx b/src/driving/application/core/create-user/infra/CreateUser.tsx index efb07df..ec77918 100644 --- a/src/driving/application/core/create-user/infra/CreateUser.tsx +++ b/src/driving/application/core/create-user/infra/CreateUser.tsx @@ -1,6 +1,43 @@ -import React from 'react' -import CreateUserView from '../view/CreateUserView' +import React from 'react'; +import createAccountAdapter from '~/driven/adapters/create-account-adapter/createAccountAdapter'; +import createProfileAdapter from '~/driven/adapters/create-profile-adapter/createProfileAdapter'; +import CreateUserInfra from '~/business-logic/core/users/create-user'; +import AdminUserModel from '~/business-logic/generic/admin-user/common/data/model/adminUserModel'; +import useGetNavigatorAndTokenUpdater from '~/driven/utils/helpers/hooks/getNavigatorAndAccessTokenUpdator'; +import CreateUserView from '../view/CreateUserView'; +import useCreateUserVM from '../viewmodel/CreateUserVM'; +import createUserModel from '../model/createUserModel'; export default function CreateUser() { - return + const { accessTokenUpdateHandler, notLoginAuth, userData } = useGetNavigatorAndTokenUpdater(); + + const { user } = userData; + // get adapters from driven layer + const createAccountDrivenAdapter = createAccountAdapter( + user as AdminUserModel, + accessTokenUpdateHandler, + notLoginAuth, + ); + const createProfileDrivenAdapter = createProfileAdapter( + user as AdminUserModel, + accessTokenUpdateHandler, + notLoginAuth, + ); + // pass to the logic and get the usecase + const createUserInfra = new CreateUserInfra(createAccountDrivenAdapter, createProfileDrivenAdapter); + const createUserLogic = createUserInfra.execute(); + // pass the usecase to the model + const { handleSubmitForm } = createUserModel({ createUserLogic }); + // pass the method to the viewmodel to call on submit + const { stateHandler, onSubmit, inputNames, error, setError } = useCreateUserVM({ handleSubmitForm }); + // get all of the needed information to the user to show + return ( + + ); } diff --git a/src/driving/application/core/create-user/model/createUserModel.ts b/src/driving/application/core/create-user/model/createUserModel.ts new file mode 100644 index 0000000..6c92840 --- /dev/null +++ b/src/driving/application/core/create-user/model/createUserModel.ts @@ -0,0 +1,21 @@ +import CreateUserUsecase from '~/business-logic/core/users/create-user/usecase/createUserUsecase'; +import { INewUserData } from '~/business-logic/core/users/create-user/create-account/data/dto/protocols'; +import IUseCreateUserVm from '../viewmodel/protocols'; + +interface ICreateUserModel { + createUserLogic: CreateUserUsecase; +} + +const createUserModel = (dependencies: ICreateUserModel): IUseCreateUserVm => { + const { createUserLogic } = dependencies; + + const handleSubmitForm = async (newUserData: INewUserData) => { + await createUserLogic.execute(newUserData); + }; + + return { + handleSubmitForm, + }; +}; + +export default createUserModel; diff --git a/src/driving/application/core/create-user/view/CreateUserView.tsx b/src/driving/application/core/create-user/view/CreateUserView.tsx index c80ba27..8fb33ad 100644 --- a/src/driving/application/core/create-user/view/CreateUserView.tsx +++ b/src/driving/application/core/create-user/view/CreateUserView.tsx @@ -1,19 +1,44 @@ -import React from 'react' -import PrimaryButton from '~/driven/utils/components/buttons/primary-button/PrimaryButton' -import SimpleInput from '~/driven/utils/components/inputs/simple-input/SimpleInput' -import { staticMessages } from '~/driven/utils/constants/staticMessages' +import React from 'react'; +import PrimaryButton from '~/driven/utils/components/buttons/primary-button/PrimaryButton'; +import SimpleInput from '~/driven/utils/components/inputs/simple-input/SimpleInput'; +import { staticMessages } from '~/driven/utils/constants/staticMessages'; +import Notification from '~/driven/utils/components/Notification/Notification'; +import ICreateUserViewProps from './protocols'; -export default function CreateUserView() { +export default function CreateUserView(props: ICreateUserViewProps) { + const { onSubmit, inputNames, stateHandler, error, setError } = props; + const { inputStates, inputsSetStates } = stateHandler; + + const inputs = inputNames.map((inputName) => { + const title = staticMessages.global[inputName] as string; + return ( + + ); + }); return ( -
-
- - - -
+
+ {Boolean(error.message) && ( + setError({ message: '', type: 'error' })} + /> + )} +
{inputs}
- {}} title={staticMessages.global.submit} /> + null} title={staticMessages.global.submit} />
-
- ) + + ); } diff --git a/src/driving/application/core/create-user/view/protocols.ts b/src/driving/application/core/create-user/view/protocols.ts new file mode 100644 index 0000000..05cea73 --- /dev/null +++ b/src/driving/application/core/create-user/view/protocols.ts @@ -0,0 +1,13 @@ +import { INewUserData } from '~/business-logic/core/users/create-user/create-account/data/dto/protocols'; +import { SetStateInputMethod } from '~/driven/utils/components/inputs/simple-input/SimpleInput'; + +export default interface ICreateUserViewProps { + onSubmit: (e: React.FormEvent) => void; + stateHandler: { + inputStates: INewUserData; + inputsSetStates: SetStateInputMethod; + }; + inputNames: (keyof INewUserData)[]; + error: { message: string; type: 'error' | 'success' }; + setError: React.Dispatch>; +} diff --git a/src/driving/application/core/create-user/viewmodel/CreateUserVM.ts b/src/driving/application/core/create-user/viewmodel/CreateUserVM.ts new file mode 100644 index 0000000..f2e7785 --- /dev/null +++ b/src/driving/application/core/create-user/viewmodel/CreateUserVM.ts @@ -0,0 +1,59 @@ +import { useState } from 'react'; +import { checkPhoneNumberInput } from '~/driven/utils/helpers/globalHelpers'; +import { INewUserData } from '~/business-logic/core/users/create-user/create-account/data/dto/protocols'; +import { AxiosError } from 'axios'; +import { staticMessages } from '~/driven/utils/constants/staticMessages'; +import ICreateUserViewProps from '../view/protocols'; +import IUseCreateUserVm from './protocols'; + +const inputStateInitialValue: INewUserData = { + firstname: '', + lastname: '', + phonenumber: '', +}; + +const inputNames: (keyof INewUserData)[] = ['firstname', 'lastname', 'phonenumber']; + +const useCreateUserVM = (dependencies: IUseCreateUserVm): ICreateUserViewProps => { + const [error, setError] = useState<{ message: string; type: 'error' | 'success' }>({ message: '', type: 'error' }); + + const { handleSubmitForm } = dependencies; + const [inputsValue, setInputValues] = useState(inputStateInitialValue); + + const inputsSetStates = (name: keyof INewUserData, newValue: string) => { + if (name === 'phonenumber' && !checkPhoneNumberInput(newValue)) return; + setInputValues((prev) => ({ + ...prev, + [name]: newValue, + })); + }; + + const onSubmitCreateUserForm = async (e: React.FormEvent) => { + e.preventDefault(); + try { + await handleSubmitForm(inputsValue); + setError({ message: staticMessages.global.success.createUser, type: 'success' }); + } catch (errorExc) { + if (errorExc instanceof AxiosError) { + setError({ message: errorExc.response?.data?.description, type: 'error' }); + } else if (errorExc instanceof Error) { + setError({ message: errorExc.message, type: 'error' }); + } + } + }; + + const inputStates: INewUserData = { ...inputsValue }; + + return { + stateHandler: { + inputsSetStates, + inputStates, + }, + onSubmit: onSubmitCreateUserForm, + inputNames, + error, + setError, + }; +}; + +export default useCreateUserVM; diff --git a/src/driving/application/core/create-user/viewmodel/protocols.ts b/src/driving/application/core/create-user/viewmodel/protocols.ts new file mode 100644 index 0000000..d6033ac --- /dev/null +++ b/src/driving/application/core/create-user/viewmodel/protocols.ts @@ -0,0 +1,5 @@ +import { INewUserData } from '~/business-logic/core/users/create-user/create-account/data/dto/protocols'; + +export default interface IUseCreateUserVm { + handleSubmitForm: (newUserData: INewUserData) => void; +} diff --git a/src/driving/application/core/places-list/index.tsx b/src/driving/application/core/places-list/index.tsx index 2834742..63340e5 100644 --- a/src/driving/application/core/places-list/index.tsx +++ b/src/driving/application/core/places-list/index.tsx @@ -1,3 +1,3 @@ -import PlacesList from "./infra/PlacesList"; +import PlacesList from './infra/PlacesList'; -export default PlacesList; \ No newline at end of file +export default PlacesList; diff --git a/src/driving/application/core/places-list/infra/PlacesList.tsx b/src/driving/application/core/places-list/infra/PlacesList.tsx index 6ec5cf6..b3d0422 100644 --- a/src/driving/application/core/places-list/infra/PlacesList.tsx +++ b/src/driving/application/core/places-list/infra/PlacesList.tsx @@ -1,8 +1,37 @@ -import React from 'react' -import PlacesListView from '../view/PlacesListView' -import usePlacesListVM from '../viewmodel/placesListVM' +import React from 'react'; +import getPlaces from '~/business-logic/core/places/get-places'; +import getPlacesAdapter from '~/driven/adapters/get-places-adapter/getPlacesAdapter'; +import PlacesModel from '~/business-logic/core/places/common/model/placesModel'; +import { prepareStateManagementForVM } from '~/driven/utils/helpers/globalHelpers'; +import useGetNavigatorAndTokenUpdater from '~/driven/utils/helpers/hooks/getNavigatorAndAccessTokenUpdator'; +import AdminUserModel from '~/business-logic/generic/admin-user/common/data/model/adminUserModel'; +import PlacesListView from '../view/PlacesListView'; +import usePlacesListVM from '../viewmodel/placesListVM'; +import placesListModel from '../model/placesListModel'; -export default function PlacessList() { - const { selectedRowId, setSelectedRowId } = usePlacesListVM() - return +const prepareTheLogicForModel = () => { + const { accessTokenUpdateHandler, notLoginAuth, userData } = useGetNavigatorAndTokenUpdater(); + + const { user } = userData; + const gettingPlacesDrivenAdapter = getPlacesAdapter(user as AdminUserModel, accessTokenUpdateHandler, notLoginAuth); + const { url } = gettingPlacesDrivenAdapter; + const getingPlacesLogic = getPlaces(gettingPlacesDrivenAdapter); + return { getingPlacesLogic, url }; +}; + +export interface IPlacesListProps { + selectedRowId: string; + setSelectedRowId: React.Dispatch>; +} + +export default function PlacessList(props: IPlacesListProps) { + const { selectedRowId, setSelectedRowId } = props; + const { getingPlacesLogic, url } = prepareTheLogicForModel(); + const placesModel = async () => await placesListModel(getingPlacesLogic); + + const useGetPlacesList = prepareStateManagementForVM(url, placesModel); + const { placesData } = usePlacesListVM({ + useGetPlacesList, + }); + return ; } diff --git a/src/driving/application/core/places-list/infra/protocols.ts b/src/driving/application/core/places-list/infra/protocols.ts deleted file mode 100644 index 6abdb94..0000000 --- a/src/driving/application/core/places-list/infra/protocols.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface IPlacesListInfraProps { - -} \ No newline at end of file diff --git a/src/driving/application/core/places-list/model/placesListModel.ts b/src/driving/application/core/places-list/model/placesListModel.ts new file mode 100644 index 0000000..050e05d --- /dev/null +++ b/src/driving/application/core/places-list/model/placesListModel.ts @@ -0,0 +1,10 @@ +import { getPlacesModel } from './protocols'; + +const placesListModel: getPlacesModel = async (getPlaces) => { + // get the method for handling the logic + const places = await getPlaces(); + return places; + // handling the errors +}; + +export default placesListModel; diff --git a/src/driving/application/core/places-list/model/protocols.ts b/src/driving/application/core/places-list/model/protocols.ts new file mode 100644 index 0000000..cd68b37 --- /dev/null +++ b/src/driving/application/core/places-list/model/protocols.ts @@ -0,0 +1,4 @@ +import PlacesModel from '~/business-logic/core/places/common/model/placesModel'; +import { getPlacesReturnPort } from '~/business-logic/core/places/get-places/port'; + +export type getPlacesModel = (getPlaces: getPlacesReturnPort) => Promise; diff --git a/src/driving/application/core/places-list/view/PlacesListView.tsx b/src/driving/application/core/places-list/view/PlacesListView.tsx index 336da1d..8258417 100644 --- a/src/driving/application/core/places-list/view/PlacesListView.tsx +++ b/src/driving/application/core/places-list/view/PlacesListView.tsx @@ -1,72 +1,60 @@ -import React from 'react' -import RowItem from '../../common/table-row/view/table-row-item/view/RowItem' -import TableRow from '../../common/table-row' -import { IPlacesListProps } from './protocols' +import React, { useMemo } from 'react'; import { staticMessages } from '~/driven/utils/constants/staticMessages'; - +import Loading from '~/driven/utils/components/loading/Loading'; +import Places from '~/business-logic/core/places/common/entity/placeEntity'; +import TableRow from '../../common/table-row'; +import { IPlacesListProps } from './protocols'; export default function UsersListView(props: IPlacesListProps) { - const { selectedRowId, setSelectedRowId } = props; + const { selectedRowId, setSelectedRowId, placesList } = props; - const rows = () => { - const placesdata = [ - { - id: '1', - place_id: '6440020b89366fdcaf15a8c2', - title: 'flat demoplace ', - status: 'demo', - address: 'demoplace' - }, - { - id: '2', - place_id: '6440020b89366fdcaf15asdfa', - title: 'flat demoplace second ', - status: 'demo second', - address: 'demoplace second' - } - ] + const rows = useMemo(() => { + if (!placesList.data) return null; - const rowData = { - rowItemsTitle: placesdata.map(places => { - return [ - places.id, - places.title, - places.status, - places.address, - ] - }), - rowId: placesdata[0].id - } + return placesList.data.getData().map((places) => { + const rowData = { + rowItemsTitle: [places.name, places.placeType, places.qr], + rowId: places.id, + }; + return ( + + ); + }); + }, [placesList]); - return placesdata.map(places => { - const rowData = { - rowItemsTitle: [ - places.id, - places.title, - places.status, - places.address, - '' - ], - rowId: places.id, - } - return - }) - } + if (placesList.isLoading) + return ( +
+ +
+ ); + const tableTitles: Pick = { + name: staticMessages.global.title, + placeType: staticMessages.global.placeType, + qr: staticMessages.global.qrCode, + }; + const titles = Object.keys(tableTitles).map((titleKey) => { + const key = titleKey as keyof typeof tableTitles; + + const title = tableTitles[key]; + return ( + + {title} + + ); + }); return ( - - - - - - - - - - - - { rows() } - +
{staticMessages.global.place_id}{staticMessages.global.title}{staticMessages.global.status}{staticMessages.global.address}{staticMessages.global.qrCode}
+ + {titles} + + {rows}
- ) + ); } diff --git a/src/driving/application/core/places-list/view/protocols.ts b/src/driving/application/core/places-list/view/protocols.ts index 90a210f..79048e3 100644 --- a/src/driving/application/core/places-list/view/protocols.ts +++ b/src/driving/application/core/places-list/view/protocols.ts @@ -1,4 +1,11 @@ +import PlacesModel from '~/business-logic/core/places/common/model/placesModel'; + export interface IPlacesListProps { + placesList: { + data: PlacesModel | undefined; + isLoading: boolean; + error?: string | undefined; + }; selectedRowId: string; setSelectedRowId: React.Dispatch>; -} \ No newline at end of file +} diff --git a/src/driving/application/core/places-list/viewmodel/placesListVM.ts b/src/driving/application/core/places-list/viewmodel/placesListVM.ts index 9c197f8..c44eabf 100644 --- a/src/driving/application/core/places-list/viewmodel/placesListVM.ts +++ b/src/driving/application/core/places-list/viewmodel/placesListVM.ts @@ -1,13 +1,24 @@ -import { useState } from "react"; -import { placesListReturnType } from "./protocols"; +import { useState } from 'react'; +import PlacesModel from '~/business-logic/core/places/common/model/placesModel'; +import { placesListReturnType } from './protocols'; -const usePlacesListVM = (): placesListReturnType => { - const [ selectedRowId, setSelectedRowId ] = useState(''); +interface IPlacesListVM { + useGetPlacesList: () => { + data: PlacesModel | undefined; + isLoading: boolean; + error?: string | undefined; + }; +} +const usePlacesListVM = (dependencies: IPlacesListVM): placesListReturnType => { + const { useGetPlacesList } = dependencies; + const placesData = useGetPlacesList(); + const [selectedRowId, setSelectedRowId] = useState(''); return { selectedRowId, setSelectedRowId, - } -} + placesData, + }; +}; -export default usePlacesListVM; \ No newline at end of file +export default usePlacesListVM; diff --git a/src/driving/application/core/places-list/viewmodel/protocols.ts b/src/driving/application/core/places-list/viewmodel/protocols.ts index a84f296..cd4cf4d 100644 --- a/src/driving/application/core/places-list/viewmodel/protocols.ts +++ b/src/driving/application/core/places-list/viewmodel/protocols.ts @@ -1,9 +1,12 @@ -import React from "react"; - -export interface IPlacesListVM { -} +import React from 'react'; +import PlacesModel from '~/business-logic/core/places/common/model/placesModel'; export type placesListReturnType = { selectedRowId: string; setSelectedRowId: React.Dispatch>; -} \ No newline at end of file + placesData: { + data: PlacesModel | undefined; + isLoading: boolean; + error?: string | undefined; + }; +}; diff --git a/src/driving/application/core/users-list/index.tsx b/src/driving/application/core/users-list/index.tsx index b029efe..304d5e9 100644 --- a/src/driving/application/core/users-list/index.tsx +++ b/src/driving/application/core/users-list/index.tsx @@ -1,3 +1,3 @@ -import UsersList from "./infra/UsersList"; +import UsersList from './infra/UsersList'; -export default UsersList; \ No newline at end of file +export default UsersList; diff --git a/src/driving/application/core/users-list/infra/UsersList.tsx b/src/driving/application/core/users-list/infra/UsersList.tsx index 505975d..5b71f59 100644 --- a/src/driving/application/core/users-list/infra/UsersList.tsx +++ b/src/driving/application/core/users-list/infra/UsersList.tsx @@ -1,8 +1,35 @@ -import React from 'react' -import useUsersListVM from '../viewmodel/usersListVM' -import UsersListView from '../view/UsersListView' +import React from 'react'; +import getUsersAdapter from '~/driven/adapters/get-users-adapter/getUsersAdapter'; +import getUsers from '~/business-logic/core/users/get-users'; +import UsersModel from '~/business-logic/core/users/common/data/model/usersModel'; +import { prepareStateManagementForVM } from '~/driven/utils/helpers/globalHelpers'; +import useGetNavigatorAndTokenUpdater from '~/driven/utils/helpers/hooks/getNavigatorAndAccessTokenUpdator'; +import AdminUserModel from '~/business-logic/generic/admin-user/common/data/model/adminUserModel'; +import useUsersListVM from '../viewmodel/usersListVM'; +import UsersListView from '../view/UsersListView'; +import usersListModel from '../model/usersListModel'; -export default function UsersList() { - const { selectedRowId, setSelectedRowId } = useUsersListVM() - return +const usePrepareTheLogicForModel = () => { + const { accessTokenUpdateHandler, notLoginAuth, userData } = useGetNavigatorAndTokenUpdater(); + + const { user } = userData; + const gettingUsersDrivenAdapter = getUsersAdapter(user as AdminUserModel, accessTokenUpdateHandler, notLoginAuth); + const { url } = gettingUsersDrivenAdapter; + const getingusersLogic = getUsers(gettingUsersDrivenAdapter); + return { getingusersLogic, url }; +}; + +export interface IUsersListProps { + selectedRowId: string; + setSelectedRowId: React.Dispatch>; +} +export default function UsersList(props: IUsersListProps) { + const { selectedRowId, setSelectedRowId } = props; + const { getingusersLogic, url } = usePrepareTheLogicForModel(); + const usersModel = async () => await usersListModel(getingusersLogic); + const useGetusersList = prepareStateManagementForVM(url, usersModel); + const { usersData } = useUsersListVM({ + useGetusersList, + }); + return ; } diff --git a/src/driving/application/core/users-list/infra/protocols.ts b/src/driving/application/core/users-list/infra/protocols.ts deleted file mode 100644 index 88b4866..0000000 --- a/src/driving/application/core/users-list/infra/protocols.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface IUsersListInfraProps { - -} \ No newline at end of file diff --git a/src/driving/application/core/users-list/model/protocols.ts b/src/driving/application/core/users-list/model/protocols.ts new file mode 100644 index 0000000..45c4c6d --- /dev/null +++ b/src/driving/application/core/users-list/model/protocols.ts @@ -0,0 +1,4 @@ +import UsersModel from '~/business-logic/core/users/common/data/model/usersModel'; +import { getUsersReturnPort } from '~/business-logic/core/users/get-users/ports'; + +export type getUsersModel = (getUsers: getUsersReturnPort) => Promise; diff --git a/src/driving/application/core/users-list/model/usersListModel.ts b/src/driving/application/core/users-list/model/usersListModel.ts new file mode 100644 index 0000000..cc3cf69 --- /dev/null +++ b/src/driving/application/core/users-list/model/usersListModel.ts @@ -0,0 +1,10 @@ +import { getUsersModel } from './protocols'; + +const usersListModel: getUsersModel = async (getUsers) => { + // get the method for handling the logic + const users = await getUsers(); + return users; + // handling the errors +}; + +export default usersListModel; diff --git a/src/driving/application/core/users-list/view/UsersListView.tsx b/src/driving/application/core/users-list/view/UsersListView.tsx index ad8bb9c..18ef379 100644 --- a/src/driving/application/core/users-list/view/UsersListView.tsx +++ b/src/driving/application/core/users-list/view/UsersListView.tsx @@ -1,60 +1,60 @@ -import React from 'react' -import RowItem from '../../common/table-row/view/table-row-item/view/RowItem' -import TableRow from '../../common/table-row' -import { IUserListProps } from './protocols' +/* eslint-disable @typescript-eslint/no-unused-vars */ +import React, { useMemo } from 'react'; import { staticMessages } from '~/driven/utils/constants/staticMessages'; - +import Users from '~/business-logic/core/users/common/entity/entity'; +import Loading from '~/driven/utils/components/loading/Loading'; +import TableRow from '../../common/table-row'; +import { IUserListProps } from './protocols'; export default function UsersListView(props: IUserListProps) { - const { selectedRowId, setSelectedRowId } = props; + const { selectedRowId, setSelectedRowId, usersList } = props; + const rows = useMemo(() => { + if (!usersList.data) return null; - const rows = () => { - const userdata = [ - { - id: '1', - firstname: 'behnam', - lastname: 'rahimpour' - }, - { - id: '2', - firstname: 'Salar', - lastname: 'Sali' - } - ] + return usersList.data.getData().map((user) => { + const rowData = { + rowItemsTitle: [user.firstname, user.lastname], + rowId: user.accountId, + }; + return ( + + ); + }); + }, [usersList]); + const tableTitles: Pick = { + firstname: staticMessages.global.firstname, + lastname: staticMessages.global.lastname, + }; - const rowData = { - rowItemsTitle: userdata.map(user => { - return [ - user.firstname, - user.lastname - ] - }), - rowId: userdata[0].id - } + const titles = Object.keys(tableTitles).map((titleKey) => { + const key = titleKey as keyof typeof tableTitles; - return userdata.map(user => { - const rowData = { - rowItemsTitle: [ - user.firstname, - user.lastname - ], - rowId: user.id, - } - return - }) - } + const title = tableTitles[key]; + return ( + + {title} + + ); + }); + + if (usersList.isLoading) + return ( +
+ +
+ ); return ( - - - - - - - - - { rows() } - +
{staticMessages.global.fistname}{staticMessages.global.lastname}
+ + {titles} + + {rows}
- ) + ); } diff --git a/src/driving/application/core/users-list/view/protocols.ts b/src/driving/application/core/users-list/view/protocols.ts index 1690a6c..2064d84 100644 --- a/src/driving/application/core/users-list/view/protocols.ts +++ b/src/driving/application/core/users-list/view/protocols.ts @@ -1,4 +1,11 @@ +import UsersModel from '~/business-logic/core/users/common/data/model/usersModel'; + export interface IUserListProps { selectedRowId: string; setSelectedRowId: React.Dispatch>; -} \ No newline at end of file + usersList: { + data: UsersModel | undefined; + isLoading: boolean; + error?: string | undefined; + }; +} diff --git a/src/driving/application/core/users-list/viewmodel/protocols.ts b/src/driving/application/core/users-list/viewmodel/protocols.ts index 86df5aa..00963e1 100644 --- a/src/driving/application/core/users-list/viewmodel/protocols.ts +++ b/src/driving/application/core/users-list/viewmodel/protocols.ts @@ -1,9 +1,12 @@ -import React from "react"; - -export interface IUsersListVM { -} +import React from 'react'; +import UsersModel from '~/business-logic/core/users/common/data/model/usersModel'; export type userListReturnType = { selectedRowId: string; setSelectedRowId: React.Dispatch>; -} \ No newline at end of file + usersData: { + data: UsersModel | undefined; + isLoading: boolean; + error?: string | undefined; + }; +}; diff --git a/src/driving/application/core/users-list/viewmodel/usersListVM.ts b/src/driving/application/core/users-list/viewmodel/usersListVM.ts index 6cdbfb8..b8c78db 100644 --- a/src/driving/application/core/users-list/viewmodel/usersListVM.ts +++ b/src/driving/application/core/users-list/viewmodel/usersListVM.ts @@ -1,13 +1,24 @@ -import { useState } from "react"; -import { userListReturnType } from "./protocols"; +import { useState } from 'react'; +import UsersModel from '~/business-logic/core/users/common/data/model/usersModel'; +import { userListReturnType } from './protocols'; -const useUsersListVM = (): userListReturnType => { - const [ selectedRowId, setSelectedRowId ] = useState(''); +interface IUsersListVM { + useGetusersList: () => { + data: UsersModel | undefined; + isLoading: boolean; + error?: string | undefined; + }; +} +const useUsersListVM = (dependencies: IUsersListVM): userListReturnType => { + const { useGetusersList } = dependencies; + const [selectedRowId, setSelectedRowId] = useState(''); + const usersData = useGetusersList(); return { selectedRowId, setSelectedRowId, - } -} + usersData, + }; +}; -export default useUsersListVM; \ No newline at end of file +export default useUsersListVM; diff --git a/src/driving/application/generic/authentication/index.tsx b/src/driving/application/generic/authentication/index.tsx new file mode 100644 index 0000000..51151e3 --- /dev/null +++ b/src/driving/application/generic/authentication/index.tsx @@ -0,0 +1,3 @@ +import Authentication from './infra/Authentication'; + +export default Authentication; diff --git a/src/driving/application/generic/authentication/infra/Authentication.tsx b/src/driving/application/generic/authentication/infra/Authentication.tsx new file mode 100644 index 0000000..76ff8aa --- /dev/null +++ b/src/driving/application/generic/authentication/infra/Authentication.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import authAdminPhoneNumberDriven from '~/driven/adapters/auth-admin-phonenumber/authAdminPhoneNumberDriven'; +import phonenumberAuthInfra from '~/business-logic/generic/admin-user/authentication/phonnumber-auth'; +import authAdminLogin from '~/driven/adapters/auth-admin-login/authAdminLogin'; +import otpAuthInfra from '~/business-logic/generic/admin-user/authentication/otp-auth'; +import useGetNavigatorAndTokenUpdater from '~/driven/utils/helpers/hooks/getNavigatorAndAccessTokenUpdator'; +import AuthenticationView from '../view/AuthenticationView'; + +export default function Authentication() { + const { accessTokenUpdateHandler, notLoginAuth, userData } = useGetNavigatorAndTokenUpdater(); + const { user } = userData; + + const authPhonenumberDriven = authAdminPhoneNumberDriven(user, accessTokenUpdateHandler, notLoginAuth); + const authPhoneLogic = phonenumberAuthInfra(authPhonenumberDriven); + + const authLoginDriven = authAdminLogin(user, accessTokenUpdateHandler, notLoginAuth); + const otpAuthLogic = otpAuthInfra(authLoginDriven.httpHandler); + + return ; +} diff --git a/src/driving/application/generic/authentication/otp-code-inputs/index.tsx b/src/driving/application/generic/authentication/otp-code-inputs/index.tsx new file mode 100644 index 0000000..6162c1a --- /dev/null +++ b/src/driving/application/generic/authentication/otp-code-inputs/index.tsx @@ -0,0 +1,3 @@ +import OtpCode from './infra/OtpCode'; + +export default OtpCode; diff --git a/src/driving/application/generic/authentication/otp-code-inputs/infra/OtpCode.tsx b/src/driving/application/generic/authentication/otp-code-inputs/infra/OtpCode.tsx new file mode 100644 index 0000000..982d05d --- /dev/null +++ b/src/driving/application/generic/authentication/otp-code-inputs/infra/OtpCode.tsx @@ -0,0 +1,18 @@ +/* eslint-disable react/display-name */ +import React from 'react'; +import OtpCodeView from '../view/OtpCodeView'; +import useOtpCodeVm from '../viewmodel/OtpCodeVM'; + +const OtpCode = React.forwardRef((_props, otpCharRef) => { + const { eventHandlers } = useOtpCodeVm({ + otpChar: otpCharRef as unknown as React.MutableRefObject, + }); + return ( + } + /> + ); +}); + +export default OtpCode; diff --git a/src/driving/application/generic/authentication/otp-code-inputs/view/OtpCodeView.tsx b/src/driving/application/generic/authentication/otp-code-inputs/view/OtpCodeView.tsx new file mode 100644 index 0000000..0f7b7ec --- /dev/null +++ b/src/driving/application/generic/authentication/otp-code-inputs/view/OtpCodeView.tsx @@ -0,0 +1,38 @@ +/* eslint-disable consistent-return */ +/* eslint-disable no-return-assign */ +/* eslint-disable react/no-array-index-key */ +import React from 'react'; +import { staticMessages } from '~/driven/utils/constants/staticMessages'; + +export interface IOtpCodeView { + otpChar: React.MutableRefObject; + eventHandlers: { + handleFocusInput: (e: React.FocusEvent) => void; + handleKeyPressInput: (e: React.KeyboardEvent) => void; + }; +} + +export default function OtpCodeView(props: IOtpCodeView) { + const { eventHandlers, otpChar } = props; + const { handleFocusInput, handleKeyPressInput } = eventHandlers; + const otpInputs = Array.from({ length: 6 }).map((digit, i) => ( + (otpChar.current[i] = el)} + key={`otp_char_${i}`} + className='font-bold text-base inline-block w-5 md:w-6 lg:w-8 xl:w-10 bg-transparent text-center focus:outline-none' + maxLength={1} + defaultValue='_' + placeholder='_' + onClick={(e) => e.stopPropagation()} + onFocus={handleFocusInput} + onKeyDown={handleKeyPressInput} + /> + )); + return ( +
+
{staticMessages.global.enterOtpCode}
+
{otpInputs}
+
+ ); +} diff --git a/src/driving/application/generic/authentication/otp-code-inputs/viewmodel/OtpCodeVM.ts b/src/driving/application/generic/authentication/otp-code-inputs/viewmodel/OtpCodeVM.ts new file mode 100644 index 0000000..3d05da3 --- /dev/null +++ b/src/driving/application/generic/authentication/otp-code-inputs/viewmodel/OtpCodeVM.ts @@ -0,0 +1,114 @@ +import { useEffect } from 'react'; +import { IOtpCodeView } from '../view/OtpCodeView'; + +interface IOtpCodeVm { + otpChar: React.MutableRefObject; +} + +type useOtpCodeReturnType = Pick; + +const useOtpCodeVm = (dependencies: IOtpCodeVm): useOtpCodeReturnType => { + const { otpChar } = dependencies; + + function focusToInput(target: HTMLInputElement, inputTabIndex: number) { + const input = target.parentElement?.querySelector(`input[tabindex="${inputTabIndex}"]`) as + | HTMLInputElement + | undefined; + + if (!input) return; + + setTimeout(() => input.focus(), 150); + } + + const handleFocusFirstInput = () => { + if (!otpChar.current.length) return; + + otpChar.current[0].focus(); + }; + + useEffect(() => { + handleFocusFirstInput(); + }, []); + + const handleFocusInput = (e: React.FocusEvent) => { + const target = e.target as HTMLInputElement; + + // check previous inputs are not empty + const currentIndex = target.getAttribute('tabindex'); + + if (!currentIndex || +currentIndex === 1) { + target.select(); + return; + } + + // get first previous empty + let isFindEmptyInput = false; + const firstEmptyInput = otpChar.current.find((item) => { + const otpItemIndex = item.getAttribute('tabindex'); + + if (!otpItemIndex) return false; + + const isInputEmpty = !item.value.trim() || item.value.trim() === '_'; + + if (+otpItemIndex < +currentIndex && isInputEmpty && !isFindEmptyInput) { + isFindEmptyInput = true; + return true; + } + + return false; + }); + + if (firstEmptyInput) { + firstEmptyInput.select(); + return; + } + + // focus to it + target.select(); + }; + + function goToPreviousInput(target: HTMLInputElement, currentIndex: number) { + const preIndex = +currentIndex - 1; + + if (!preIndex) return; + + focusToInput(target, preIndex); + } + + function goToNextInput(target: HTMLInputElement, currentIndex: number) { + // get next index + const nextIndex = +currentIndex + 1; + + if (!nextIndex) return; + + focusToInput(target, nextIndex); + } + + const handleKeyPressInput = (e: React.KeyboardEvent) => { + // target + const target = e.target as HTMLInputElement; + + // get current index + const currentIndex = target.getAttribute('tabindex'); + + if (!currentIndex) return; + + const isRemoveChar = e.key.toLowerCase() === 'backspace'; + + if (isRemoveChar && +currentIndex !== 1) { + goToPreviousInput(target, +currentIndex); + return; + } + + goToNextInput(target, +currentIndex); + }; + + return { + eventHandlers: { + handleFocusInput, + handleKeyPressInput, + }, + }; +}; + +export default useOtpCodeVm; diff --git a/src/driving/application/generic/authentication/view/AuthenticationView.tsx b/src/driving/application/generic/authentication/view/AuthenticationView.tsx new file mode 100644 index 0000000..2b02379 --- /dev/null +++ b/src/driving/application/generic/authentication/view/AuthenticationView.tsx @@ -0,0 +1,85 @@ +import React, { useRef, useState } from 'react'; +import PrimaryButton from '~/driven/utils/components/buttons/primary-button/PrimaryButton'; +import { icons } from '~/driven/utils/constants/assertUrls'; +import { staticMessages } from '~/driven/utils/constants/staticMessages'; +import OtpAuthUsecase from '~/business-logic/generic/admin-user/authentication/otp-auth/usecase/otpAuthUsecase'; +import PhonenumberAuthUsecase from '~/business-logic/generic/admin-user/authentication/phonnumber-auth/usecase/phonenumberAuthUsecase'; +import StorageService from '~/driven/boundaries/storage-boundary'; +import { appConfig } from '~/driven/utils/configs/appConfig'; +import Notification from '~/driven/utils/components/Notification/Notification'; +import OtpAuthUsecaseException from '~/business-logic/generic/admin-user/authentication/otp-auth/usecase/otpException'; +import { AxiosError } from 'axios'; +import { useUser } from '~/driven/utils/helpers/contexts/userContext'; +import OtpCode from '../otp-code-inputs'; +import PhoneNumberAuth from './PhoneNumberAuth'; + +interface IAuthenticationView { + authPhone: PhonenumberAuthUsecase; + otpAuth: OtpAuthUsecase; +} + +/** + * @todo should seperate logic and ui logic from the view + */ +export default function AuthenticationView(props: IAuthenticationView) { + const { authPhone, otpAuth } = props; + const [phoneNumberValue, setPhoneNumberValue] = useState(''); + const [error, setError] = useState(''); + const { setUser } = useUser(); + const otpChar = useRef([]); + const statesName = { + phonenumber: , + otp: , + }; + const [authState, setAuthState] = useState('phonenumber'); + + const submitForm = async (e: React.FormEvent) => { + e.preventDefault(); + try { + if (authState === 'phonenumber') { + await authPhone.execute({ phonenumber: phoneNumberValue }); + setAuthState('otp'); + } + if (authState === 'otp') { + const otp = otpChar.current + .map((inputItem) => { + return inputItem.value; + }) + .join(''); + const userModel = await otpAuth.execute({ otp, phonenumber: phoneNumberValue }); + const storage = StorageService.localStorage(); + storage.setData(appConfig.adminUserStorageKey, userModel); + if (setUser) setUser(userModel); + } + } catch (errorException) { + if (errorException instanceof OtpAuthUsecaseException) { + setError(staticMessages.global.errors.otp); + } else if (errorException instanceof AxiosError) { + setError(errorException.response?.data?.description); + } else if (errorException instanceof Error) { + setError(errorException.message); + } + } + }; + return ( +
+ {Boolean(error) && setError('')} />} +
+
+ page icon +
+
{staticMessages.global.enterPanel}
+ {statesName[authState]} + null} + title={staticMessages.global.submit} + className='[background:var(--color-gradient-button)] hover:brightness-90 transition-all w-full h-11' + /> + +
+
+ ); +} diff --git a/src/driving/application/generic/authentication/view/PhoneNumberAuth.tsx b/src/driving/application/generic/authentication/view/PhoneNumberAuth.tsx new file mode 100644 index 0000000..aeac495 --- /dev/null +++ b/src/driving/application/generic/authentication/view/PhoneNumberAuth.tsx @@ -0,0 +1,32 @@ +import SimpleInput, { SetStateInputMethod } from '~/driven/utils/components/inputs/simple-input/SimpleInput'; +import { staticMessages } from '~/driven/utils/constants/staticMessages'; +import { checkPhoneNumberInput } from '~/driven/utils/helpers/globalHelpers'; + +interface IPhoneNumberAuth { + stateData: { + stateValue: string; + setState: React.Dispatch>; + }; +} +export default function PhoneNumberAuth(props: IPhoneNumberAuth) { + const { stateData } = props; + const { setState, stateValue } = stateData; + const onChangeInput: SetStateInputMethod = ( + _name: typeof staticMessages.global.phonenumber, + newValue: string, + ) => { + if (!checkPhoneNumberInput(newValue)) return; + setState(newValue); + }; + + return ( + <> +
{staticMessages.global.enterPhoneNumber}
+ + + ); +} diff --git a/src/driving/application/support/sidebar/index.tsx b/src/driving/application/support/sidebar/index.tsx index c6910ce..0004865 100644 --- a/src/driving/application/support/sidebar/index.tsx +++ b/src/driving/application/support/sidebar/index.tsx @@ -1,3 +1,3 @@ -import Sidebar from "./view/Sidebar"; +import Sidebar from './view/Sidebar'; -export default Sidebar; \ No newline at end of file +export default Sidebar; diff --git a/src/driving/application/support/sidebar/view/Sidebar.tsx b/src/driving/application/support/sidebar/view/Sidebar.tsx index 0af298d..f3af15d 100644 --- a/src/driving/application/support/sidebar/view/Sidebar.tsx +++ b/src/driving/application/support/sidebar/view/Sidebar.tsx @@ -1,29 +1,41 @@ -import React from 'react' -import { Link, useLocation } from 'react-router-dom' -import { routesData } from '~/driven/utils/configs/appConfig' -import { icons } from '~/driven/utils/constants/assertUrls' +import React from 'react'; +import { Link, useLocation, useNavigate } from 'react-router-dom'; +import { routesData } from '~/driven/utils/configs/appConfig'; +import { icons } from '~/driven/utils/constants/assertUrls'; +import { useUser } from '~/driven/utils/helpers/contexts/userContext'; +import { navigateToAuth } from '~/driven/utils/helpers/globalHelpers'; export default function Sidebar() { - const isCurrentPage = useLocation() - - const pages = Object.keys(routesData).map(routeKey => { - const key = routeKey as keyof typeof routesData + const isCurrentPage = useLocation(); + const userCTX = useUser(); + const navigator = useNavigate(); + const pages = Object.keys(routesData).map((routeKey) => { + const key = routeKey as keyof typeof routesData; return ( - - -
{routesData[key].title}
- - ) - }) + + page icon +
{routesData[key].title}
+ + ); + }); return ( - ) + ); } diff --git a/src/driving/main/App.tsx b/src/driving/main/App.tsx index 9ae6997..649fe0f 100644 --- a/src/driving/main/App.tsx +++ b/src/driving/main/App.tsx @@ -1,13 +1,21 @@ -import { BrowserRouter as RouterWrapper } from 'react-router-dom'; -import Router from './Router/Router'; +import { useUser } from '~/driven/utils/helpers/contexts/userContext'; import './style/App.css'; +import { useEffect } from 'react'; +import { appConfig } from '~/driven/utils/configs/appConfig'; +import AdminUserModel from '~/business-logic/generic/admin-user/common/data/model/adminUserModel'; +import StorageService from '~/driven/boundaries/storage-boundary'; +import Router from './Router/Router'; function App() { - return ( - - - - ); + const data = useUser(); + const { setUser } = data; + + useEffect(() => { + const storage = StorageService.localStorage(); + const currentUser = storage.getData(appConfig.adminUserStorageKey); + if (currentUser && currentUser.adminUserData.accessToken && setUser) setUser(currentUser); + }, []); + return ; } export default App; diff --git a/src/driving/main/Router/Router.tsx b/src/driving/main/Router/Router.tsx index c2506cc..d674476 100644 --- a/src/driving/main/Router/Router.tsx +++ b/src/driving/main/Router/Router.tsx @@ -3,16 +3,21 @@ import Home from '~/driving/main/pages'; import { routes } from '~/driven/utils/configs/appConfig'; import CreateUser from '../pages/CreateUser'; import MainPageLayout from '../pages/layouts/MainPageLayout'; +import AuthenticationPage from '../pages/Authentication'; +import UserLoginLayout from '../pages/layouts/UserLoginLayout'; export default function Router() { const location = useLocation(); return ( - } > + }> } /> } /> + }> + } /> + } /> ); diff --git a/src/driving/main/index.tsx b/src/driving/main/index.tsx index 49a87aa..240b5cf 100644 --- a/src/driving/main/index.tsx +++ b/src/driving/main/index.tsx @@ -1,10 +1,12 @@ import ReactDOM from 'react-dom/client'; -import { Provider } from 'react-redux'; +import { UserProvider } from '~/driven/utils/helpers/contexts/userContext'; +import { BrowserRouter as RouterWrapper } from 'react-router-dom'; import App from './App'; -import store from '~/driven/boundaries/state-management-boundary/store/store'; ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( - - - , + + + + + , ); diff --git a/src/driving/main/pages/Authentication.tsx b/src/driving/main/pages/Authentication.tsx new file mode 100644 index 0000000..c4dcb47 --- /dev/null +++ b/src/driving/main/pages/Authentication.tsx @@ -0,0 +1,6 @@ +import React from 'react'; +import Authentication from '~/driving/application/generic/authentication'; + +export default function AuthenticationPage() { + return ; +} diff --git a/src/driving/main/pages/CreateUser.tsx b/src/driving/main/pages/CreateUser.tsx index 62fcc0a..a984828 100644 --- a/src/driving/main/pages/CreateUser.tsx +++ b/src/driving/main/pages/CreateUser.tsx @@ -1,7 +1,7 @@ -import React from 'react' -import PageTitle from '~/driven/utils/components/page-title/pageTitle' -import { staticMessages } from '~/driven/utils/constants/staticMessages' -import CreateUser from '~/driving/application/core/create-user' +import React from 'react'; +import PageTitle from '~/driven/utils/components/page-title/pageTitle'; +import { staticMessages } from '~/driven/utils/constants/staticMessages'; +import CreateUser from '~/driving/application/core/create-user'; export default function CreateUserPage() { return ( @@ -9,5 +9,5 @@ export default function CreateUserPage() { - ) + ); } diff --git a/src/driving/main/pages/index.tsx b/src/driving/main/pages/index.tsx index 008ac54..8573c65 100644 --- a/src/driving/main/pages/index.tsx +++ b/src/driving/main/pages/index.tsx @@ -1,23 +1,86 @@ -import React from 'react'; +import { AxiosError } from 'axios'; +import React, { useEffect, useState } from 'react'; +import { HTTPPovider } from '~/driven/boundaries/http-boundary/httpBoundary'; +import { HttpOptionsType } from '~/driven/boundaries/http-boundary/protocols'; +import Notification from '~/driven/utils/components/Notification/Notification'; import PrimaryButton from '~/driven/utils/components/buttons/primary-button/PrimaryButton'; import PageTitle from '~/driven/utils/components/page-title/pageTitle'; +import { apiUrls } from '~/driven/utils/configs/appConfig'; import { staticMessages } from '~/driven/utils/constants/staticMessages'; +import useGetNavigatorAndTokenUpdater from '~/driven/utils/helpers/hooks/getNavigatorAndAccessTokenUpdator'; import PlacesList from '~/driving/application/core/places-list'; import UsersList from '~/driving/application/core/users-list'; -import Sidebar from '~/driving/application/support/sidebar'; export default function index() { + const [selectedUserRowId, setSelectedUserRowId] = useState(''); + const [selectedPlaceRowId, setSelectedPlaceRowId] = useState(''); + const { accessTokenUpdateHandler, notLoginAuth, userData } = useGetNavigatorAndTokenUpdater(); + const [error, setError] = useState<{ message: string; type: 'error' | 'success' }>({ message: '', type: 'error' }); + const [isSubmitDisabled, setIsSubmitDisabled] = useState(true); + const onSubmitMember = async (e: React.FormEvent) => { + e.preventDefault(); + try { + const url = apiUrls.core.createMember; + const data = { + place_id: selectedPlaceRowId, + account_id: selectedUserRowId, + }; + + const options: HttpOptionsType = { + url, + data, + headers: { + 'Content-Type': 'application/json', + }, + method: 'POST', + }; + + const userTokens = { + accessToken: userData.user?.adminUserData.accessToken || null, + refreshToken: userData.user?.adminUserData.refreshToken || null, + }; + const httpProvider = new HTTPPovider(userTokens, accessTokenUpdateHandler, notLoginAuth); + + await httpProvider.request(options); + setError({ message: staticMessages.global.success.createMember, type: 'success' }); + } catch (errorExc) { + if (errorExc instanceof AxiosError) { + setError({ message: errorExc.response?.data?.description, type: 'error' }); + } else if (errorExc instanceof Error) { + setError({ message: errorExc.message, type: 'error' }); + } + } + }; + + useEffect(() => { + if (selectedUserRowId && selectedPlaceRowId) setIsSubmitDisabled(false); + else setIsSubmitDisabled(true); + }, [selectedUserRowId, selectedPlaceRowId]); + return ( <> -
-
- { } } /> + {Boolean(error.message) && ( + setError({ message: '', type: 'error' })} + /> + )} + +
+
+ null} + /> + +
+ + +
-
- - -
-
- + ); } diff --git a/src/driving/main/pages/layouts/MainPageLayout.tsx b/src/driving/main/pages/layouts/MainPageLayout.tsx index 33fe105..9447647 100644 --- a/src/driving/main/pages/layouts/MainPageLayout.tsx +++ b/src/driving/main/pages/layouts/MainPageLayout.tsx @@ -1,14 +1,29 @@ -import React from 'react' -import { Outlet } from 'react-router-dom' -import Sidebar from '~/driving/application/support/sidebar' +import React, { useEffect } from 'react'; +import { Navigate, Outlet } from 'react-router-dom'; +import AdminUserModel from '~/business-logic/generic/admin-user/common/data/model/adminUserModel'; +import StorageService from '~/driven/boundaries/storage-boundary'; +import { appConfig, routes } from '~/driven/utils/configs/appConfig'; +import { useUser } from '~/driven/utils/helpers/contexts/userContext'; +import Sidebar from '~/driving/application/support/sidebar'; +/** + * @todo should move to the logic and seperate the ui from the main logics + */ export default function MainPageLayout() { + const { user, setUser } = useUser(); + useEffect(() => { + const storage = StorageService.localStorage(); + const currentUser = storage.getData(appConfig.adminUserStorageKey); + if (!currentUser && setUser) setUser(null); + }); + + if (!user || !user.adminUserData.accessToken) return ; return ( -
+
-
+
-
+
- ) + ); } diff --git a/src/driving/main/pages/layouts/UserLoginLayout.tsx b/src/driving/main/pages/layouts/UserLoginLayout.tsx new file mode 100644 index 0000000..dfeee28 --- /dev/null +++ b/src/driving/main/pages/layouts/UserLoginLayout.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { Navigate, Outlet } from 'react-router-dom'; +import { routes } from '~/driven/utils/configs/appConfig'; +import { useUser } from '~/driven/utils/helpers/contexts/userContext'; + +export default function UserLoginLayout() { + const { user } = useUser(); + if (user && user.adminUserData.accessToken) return ; + return ; +} diff --git a/src/driving/main/style/App.css b/src/driving/main/style/App.css index 3e72895..db02ae3 100644 --- a/src/driving/main/style/App.css +++ b/src/driving/main/style/App.css @@ -20,6 +20,7 @@ html,body { --color-primary-300: #0461B8; --color-primary-200: #80C8EF; --color-separated-border: #D4D4D4; + --color-gradient-button: linear-gradient(90deg, #00A6FE -10.23%, #D700FE 114.77%); } th, diff --git a/tailwind.config.cjs b/tailwind.config.cjs index a979fbd..4469893 100644 --- a/tailwind.config.cjs +++ b/tailwind.config.cjs @@ -27,6 +27,7 @@ module.exports = { }, gradient: { primary: 'var(--color-gradient-primary-dark)', + button: 'var(--color-gradient-button)' }, separated: { border: 'var(--color-separated-border)' diff --git a/tsconfig.json b/tsconfig.json index 54166f4..17621e1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,8 @@ "target": "esnext", "module": "esnext", "types": [ - "node" + "node", + "jest" ], "useDefineForClassFields": true, "lib": ["DOM", "DOM.Iterable", "ESNext"], diff --git a/vite.config.ts b/vite.config.ts index 09fa318..eaee4e3 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,16 +1,32 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import {defineConfig, loadEnv} from 'vite'; +import react from '@vitejs/plugin-react'; import path from 'path'; // https://vitejs.dev/config/ -export default defineConfig({ - plugins: [react()], - resolve: { - alias: { - '~': path.resolve(__dirname, './src'), +export default defineConfig(({mode}) => { + const env = loadEnv(mode, process.cwd()) + + // expose .env as process.env instead of import.meta since jest does not import meta yet + const envWithProcessPrefix = Object.entries(env).reduce( + (prev, [key, val]) => { + return { + ...prev, + [ key]: `${val}`, + } }, - }, - server: { - host: true, - }, -}) + {}, + ) + + return { + plugins: [react()], + resolve: { + alias: { + '~': path.resolve(__dirname, './src'), + }, + }, + server: { + host: true, + }, + define: {'process.env': {...envWithProcessPrefix}} + } +}); diff --git a/yarn.lock b/yarn.lock index 77e163e..ecc5a77 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22,11 +22,23 @@ dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.1", "@babel/compat-data@^7.20.5": +"@babel/code-frame@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" + integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== + dependencies: + "@babel/highlight" "^7.18.6" + +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5": version "7.21.0" resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz" integrity sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g== +"@babel/compat-data@^7.21.5": + version "7.21.7" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.7.tgz#61caffb60776e49a57ba61a88f02bedd8714f6bc" + integrity sha512-KYMqFYTaenzMK4yUtf4EW9wc4N9ef80FsbMtkwool5zpwl4YrT1SdWYSTRcT94KO4hannogdS+LxY7L+arP3gA== + "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.20.12": version "7.21.0" resolved "https://registry.npmjs.org/@babel/core/-/core-7.21.0.tgz" @@ -58,6 +70,16 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" +"@babel/generator@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.5.tgz#c0c0e5449504c7b7de8236d99338c3e2a340745f" + integrity sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w== + dependencies: + "@babel/types" "^7.21.5" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.16.0", "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz" @@ -73,7 +95,7 @@ "@babel/helper-explode-assignable-expression" "^7.18.6" "@babel/types" "^7.18.9" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.0", "@babel/helper-compilation-targets@^7.20.7": +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7": version "7.20.7" resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz" integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== @@ -84,6 +106,17 @@ lru-cache "^5.1.1" semver "^6.3.0" +"@babel/helper-compilation-targets@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.5.tgz#631e6cc784c7b660417421349aac304c94115366" + integrity sha512-1RkbFGUKex4lvsB9yhIfWltJM5cZKUftB2eNajaDv3dCMEp49iBG0K14uH8NnX9IPux2+mK7JGEOB0jn48/J6w== + dependencies: + "@babel/compat-data" "^7.21.5" + "@babel/helper-validator-option" "^7.21.0" + browserslist "^4.21.3" + lru-cache "^5.1.1" + semver "^6.3.0" + "@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0": version "7.21.0" resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz" @@ -123,6 +156,11 @@ resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== +"@babel/helper-environment-visitor@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz#c769afefd41d171836f7cb63e295bedf689d48ba" + integrity sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ== + "@babel/helper-explode-assignable-expression@^7.18.6": version "7.18.6" resolved "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz" @@ -159,7 +197,14 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.0", "@babel/helper-module-transforms@^7.21.2": +"@babel/helper-module-imports@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af" + integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== + dependencies: + "@babel/types" "^7.21.4" + +"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.0": version "7.21.2" resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz" integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== @@ -173,6 +218,20 @@ "@babel/traverse" "^7.21.2" "@babel/types" "^7.21.2" +"@babel/helper-module-transforms@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.5.tgz#d937c82e9af68d31ab49039136a222b17ac0b420" + integrity sha512-bI2Z9zBGY2q5yMHoBvJ2a9iX3ZOAzJPm7Q8Yz6YeoUjU/Cvhmi2G4QyTNyPBqqXSgTjUxRg3L0xV45HvkNWWBw== + dependencies: + "@babel/helper-environment-visitor" "^7.21.5" + "@babel/helper-module-imports" "^7.21.4" + "@babel/helper-simple-access" "^7.21.5" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.5" + "@babel/types" "^7.21.5" + "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz" @@ -185,6 +244,11 @@ resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz" integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== +"@babel/helper-plugin-utils@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz#345f2377d05a720a4e5ecfa39cbf4474a4daed56" + integrity sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg== + "@babel/helper-remap-async-to-generator@^7.18.9": version "7.18.9" resolved "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz" @@ -214,6 +278,13 @@ dependencies: "@babel/types" "^7.20.2" +"@babel/helper-simple-access@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz#d697a7971a5c39eac32c7e63c0921c06c8a249ee" + integrity sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg== + dependencies: + "@babel/types" "^7.21.5" + "@babel/helper-skip-transparent-expression-wrappers@^7.20.0": version "7.20.0" resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz" @@ -233,6 +304,11 @@ resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz" integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== +"@babel/helper-string-parser@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz#2b3eea65443c6bdc31c22d037c65f6d323b6b2bd" + integrity sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w== + "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": version "7.19.1" resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz" @@ -276,6 +352,11 @@ resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.21.2.tgz" integrity sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ== +"@babel/parser@^7.21.5": + version "7.21.8" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.8.tgz#642af7d0333eab9c0ad70b14ac5e76dbde7bfdf8" + integrity sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA== + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" resolved "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz" @@ -283,18 +364,18 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.18.9": +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.20.7": version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz#d9c85589258539a22a901033853101a6198d4ef1" integrity sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ== dependencies: "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/plugin-proposal-optional-chaining" "^7.20.7" -"@babel/plugin-proposal-async-generator-functions@^7.20.1": +"@babel/plugin-proposal-async-generator-functions@^7.20.7": version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326" integrity sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA== dependencies: "@babel/helper-environment-visitor" "^7.18.9" @@ -310,9 +391,9 @@ "@babel/helper-create-class-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-proposal-class-static-block@^7.18.6": +"@babel/plugin-proposal-class-static-block@^7.21.0": version "7.21.0" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz#77bdd66fb7b605f3a61302d224bdfacf5547977d" integrity sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw== dependencies: "@babel/helper-create-class-features-plugin" "^7.21.0" @@ -343,9 +424,9 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-json-strings" "^7.8.3" -"@babel/plugin-proposal-logical-assignment-operators@^7.18.9": +"@babel/plugin-proposal-logical-assignment-operators@^7.20.7": version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz#dfbcaa8f7b4d37b51e8bfb46d94a5aea2bb89d83" integrity sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug== dependencies: "@babel/helper-plugin-utils" "^7.20.2" @@ -367,9 +448,9 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-proposal-object-rest-spread@^7.20.2": +"@babel/plugin-proposal-object-rest-spread@^7.20.7": version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz#aa662940ef425779c75534a5c41e9d936edc390a" integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== dependencies: "@babel/compat-data" "^7.20.5" @@ -386,7 +467,7 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-proposal-optional-chaining@^7.18.9", "@babel/plugin-proposal-optional-chaining@^7.20.7": +"@babel/plugin-proposal-optional-chaining@^7.20.7", "@babel/plugin-proposal-optional-chaining@^7.21.0": version "7.21.0" resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz" integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== @@ -403,9 +484,9 @@ "@babel/helper-create-class-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-proposal-private-property-in-object@^7.18.6": +"@babel/plugin-proposal-private-property-in-object@^7.21.0": version "7.21.0" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz#19496bd9883dd83c23c7d7fc45dcd9ad02dfa1dc" integrity sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" @@ -470,7 +551,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.19.0" -"@babel/plugin-syntax-import-meta@^7.8.3": +"@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz" integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== @@ -491,6 +572,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-syntax-jsx@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz#f264ed7bf40ffc9ec239edabc17a50c4f5b6fea2" + integrity sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz" @@ -554,16 +642,16 @@ dependencies: "@babel/helper-plugin-utils" "^7.19.0" -"@babel/plugin-transform-arrow-functions@^7.18.6": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz" - integrity sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ== +"@babel/plugin-transform-arrow-functions@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.21.5.tgz#9bb42a53de447936a57ba256fbf537fc312b6929" + integrity sha512-wb1mhwGOCaXHDTcsRYMKF9e5bbMgqwxtqa2Y1ifH96dXJPwbuLX9qHy3clhrxVqgMz7nyNXs8VkxdH8UBcjKqA== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.21.5" -"@babel/plugin-transform-async-to-generator@^7.18.6": +"@babel/plugin-transform-async-to-generator@^7.20.7": version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz#dfee18623c8cb31deb796aa3ca84dda9cea94354" integrity sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q== dependencies: "@babel/helper-module-imports" "^7.18.6" @@ -577,16 +665,16 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-block-scoping@^7.20.2": +"@babel/plugin-transform-block-scoping@^7.21.0": version "7.21.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz#e737b91037e5186ee16b76e7ae093358a5634f02" integrity sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ== dependencies: "@babel/helper-plugin-utils" "^7.20.2" -"@babel/plugin-transform-classes@^7.20.2": +"@babel/plugin-transform-classes@^7.21.0": version "7.21.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz#f469d0b07a4c5a7dbb21afad9e27e57b47031665" integrity sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" @@ -599,18 +687,18 @@ "@babel/helper-split-export-declaration" "^7.18.6" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.18.9": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz" - integrity sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ== +"@babel/plugin-transform-computed-properties@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.21.5.tgz#3a2d8bb771cd2ef1cd736435f6552fe502e11b44" + integrity sha512-TR653Ki3pAwxBxUe8srfF3e4Pe3FTA46uaNHYyQwIoM4oWKSoOZiDNyHJ0oIoDIUPSRQbQG7jzgVBX3FPVne1Q== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.21.5" "@babel/template" "^7.20.7" -"@babel/plugin-transform-destructuring@^7.20.2": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz" - integrity sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA== +"@babel/plugin-transform-destructuring@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz#73b46d0fd11cd6ef57dea8a381b1215f4959d401" + integrity sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA== dependencies: "@babel/helper-plugin-utils" "^7.20.2" @@ -637,12 +725,12 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-for-of@^7.18.8": - version "7.21.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz" - integrity sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ== +"@babel/plugin-transform-for-of@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.5.tgz#e890032b535f5a2e237a18535f56a9fdaa7b83fc" + integrity sha512-nYWpjKW/7j/I/mZkGVgHJXh4bA1sfdFnJoOXwJuj4m3Q2EraO/8ZyrkCau9P5tbHQk01RMSt6KYLCsW7730SXQ== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.21.5" "@babel/plugin-transform-function-name@^7.18.9": version "7.18.9" @@ -667,26 +755,26 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-modules-amd@^7.19.6": +"@babel/plugin-transform-modules-amd@^7.20.11": version "7.20.11" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz#3daccca8e4cc309f03c3a0c4b41dc4b26f55214a" integrity sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g== dependencies: "@babel/helper-module-transforms" "^7.20.11" "@babel/helper-plugin-utils" "^7.20.2" -"@babel/plugin-transform-modules-commonjs@^7.19.6": - version "7.21.2" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz" - integrity sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA== +"@babel/plugin-transform-modules-commonjs@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.5.tgz#d69fb947eed51af91de82e4708f676864e5e47bc" + integrity sha512-OVryBEgKUbtqMoB7eG2rs6UFexJi6Zj6FDXx+esBLPTCxCNxAY9o+8Di7IsUGJ+AVhp5ncK0fxWUBd0/1gPhrQ== dependencies: - "@babel/helper-module-transforms" "^7.21.2" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-module-transforms" "^7.21.5" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-simple-access" "^7.21.5" -"@babel/plugin-transform-modules-systemjs@^7.19.6": +"@babel/plugin-transform-modules-systemjs@^7.20.11": version "7.20.11" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz#467ec6bba6b6a50634eea61c9c232654d8a4696e" integrity sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw== dependencies: "@babel/helper-hoist-variables" "^7.18.6" @@ -702,9 +790,9 @@ "@babel/helper-module-transforms" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-named-capturing-groups-regex@^7.19.1": +"@babel/plugin-transform-named-capturing-groups-regex@^7.20.5": version "7.20.5" - resolved "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz#626298dd62ea51d452c3be58b285d23195ba69a8" integrity sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.20.5" @@ -725,13 +813,20 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/helper-replace-supers" "^7.18.6" -"@babel/plugin-transform-parameters@^7.20.1", "@babel/plugin-transform-parameters@^7.20.7": +"@babel/plugin-transform-parameters@^7.20.7": version "7.20.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz" integrity sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA== dependencies: "@babel/helper-plugin-utils" "^7.20.2" +"@babel/plugin-transform-parameters@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz#18fc4e797cf6d6d972cb8c411dbe8a809fa157db" + integrity sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-transform-property-literals@^7.18.6": version "7.18.6" resolved "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz" @@ -786,12 +881,12 @@ "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-regenerator@^7.18.6": - version "7.20.5" - resolved "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz" - integrity sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ== +"@babel/plugin-transform-regenerator@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.21.5.tgz#576c62f9923f94bcb1c855adc53561fd7913724e" + integrity sha512-ZoYBKDb6LyMi5yCsByQ5jmXsHAQDDYeexT1Szvlmui+lADvfSecr5Dxd/PkrTC3pAD182Fcju1VQkB4oCp9M+w== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.21.5" regenerator-transform "^0.15.1" "@babel/plugin-transform-reserved-words@^7.18.6": @@ -808,9 +903,9 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-spread@^7.19.0": +"@babel/plugin-transform-spread@^7.20.7": version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz#c2d83e0b99d3bf83e07b11995ee24bf7ca09401e" integrity sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw== dependencies: "@babel/helper-plugin-utils" "^7.20.2" @@ -837,21 +932,22 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-typescript@^7.21.0": - version "7.21.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.0.tgz" - integrity sha512-xo///XTPp3mDzTtrqXoBlK9eiAYW3wv9JXglcn/u1bi60RW11dEUxIgA8cbnDhutS1zacjMRmAwxE0gMklLnZg== +"@babel/plugin-transform-typescript@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz#316c5be579856ea890a57ebc5116c5d064658f2b" + integrity sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw== dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-create-class-features-plugin" "^7.21.0" "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-typescript" "^7.20.0" -"@babel/plugin-transform-unicode-escapes@^7.18.10": - version "7.18.10" - resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz" - integrity sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ== +"@babel/plugin-transform-unicode-escapes@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.21.5.tgz#1e55ed6195259b0e9061d81f5ef45a9b009fb7f2" + integrity sha512-LYm/gTOwZqsYohlvFUe/8Tujz75LqqVC2w+2qPHLR+WyWHGCZPN1KBpJCJn+4Bk4gOkQy/IXKIge6az5MqwlOg== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.21.5" "@babel/plugin-transform-unicode-regex@^7.18.6": version "7.18.6" @@ -861,31 +957,31 @@ "@babel/helper-create-regexp-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/preset-env@^7.20.2": - version "7.20.2" - resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.20.2.tgz" - integrity sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg== +"@babel/preset-env@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.21.5.tgz#db2089d99efd2297716f018aeead815ac3decffb" + integrity sha512-wH00QnTTldTbf/IefEVyChtRdw5RJvODT/Vb4Vcxq1AZvtXj6T0YeX0cAcXhI6/BdGuiP3GcNIL4OQbI2DVNxg== dependencies: - "@babel/compat-data" "^7.20.1" - "@babel/helper-compilation-targets" "^7.20.0" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-validator-option" "^7.18.6" + "@babel/compat-data" "^7.21.5" + "@babel/helper-compilation-targets" "^7.21.5" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-validator-option" "^7.21.0" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.9" - "@babel/plugin-proposal-async-generator-functions" "^7.20.1" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.20.7" + "@babel/plugin-proposal-async-generator-functions" "^7.20.7" "@babel/plugin-proposal-class-properties" "^7.18.6" - "@babel/plugin-proposal-class-static-block" "^7.18.6" + "@babel/plugin-proposal-class-static-block" "^7.21.0" "@babel/plugin-proposal-dynamic-import" "^7.18.6" "@babel/plugin-proposal-export-namespace-from" "^7.18.9" "@babel/plugin-proposal-json-strings" "^7.18.6" - "@babel/plugin-proposal-logical-assignment-operators" "^7.18.9" + "@babel/plugin-proposal-logical-assignment-operators" "^7.20.7" "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" "@babel/plugin-proposal-numeric-separator" "^7.18.6" - "@babel/plugin-proposal-object-rest-spread" "^7.20.2" + "@babel/plugin-proposal-object-rest-spread" "^7.20.7" "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" - "@babel/plugin-proposal-optional-chaining" "^7.18.9" + "@babel/plugin-proposal-optional-chaining" "^7.21.0" "@babel/plugin-proposal-private-methods" "^7.18.6" - "@babel/plugin-proposal-private-property-in-object" "^7.18.6" + "@babel/plugin-proposal-private-property-in-object" "^7.21.0" "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-class-properties" "^7.12.13" @@ -893,6 +989,7 @@ "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" "@babel/plugin-syntax-import-assertions" "^7.20.0" + "@babel/plugin-syntax-import-meta" "^7.10.4" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" @@ -902,40 +999,40 @@ "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-transform-arrow-functions" "^7.18.6" - "@babel/plugin-transform-async-to-generator" "^7.18.6" + "@babel/plugin-transform-arrow-functions" "^7.21.5" + "@babel/plugin-transform-async-to-generator" "^7.20.7" "@babel/plugin-transform-block-scoped-functions" "^7.18.6" - "@babel/plugin-transform-block-scoping" "^7.20.2" - "@babel/plugin-transform-classes" "^7.20.2" - "@babel/plugin-transform-computed-properties" "^7.18.9" - "@babel/plugin-transform-destructuring" "^7.20.2" + "@babel/plugin-transform-block-scoping" "^7.21.0" + "@babel/plugin-transform-classes" "^7.21.0" + "@babel/plugin-transform-computed-properties" "^7.21.5" + "@babel/plugin-transform-destructuring" "^7.21.3" "@babel/plugin-transform-dotall-regex" "^7.18.6" "@babel/plugin-transform-duplicate-keys" "^7.18.9" "@babel/plugin-transform-exponentiation-operator" "^7.18.6" - "@babel/plugin-transform-for-of" "^7.18.8" + "@babel/plugin-transform-for-of" "^7.21.5" "@babel/plugin-transform-function-name" "^7.18.9" "@babel/plugin-transform-literals" "^7.18.9" "@babel/plugin-transform-member-expression-literals" "^7.18.6" - "@babel/plugin-transform-modules-amd" "^7.19.6" - "@babel/plugin-transform-modules-commonjs" "^7.19.6" - "@babel/plugin-transform-modules-systemjs" "^7.19.6" + "@babel/plugin-transform-modules-amd" "^7.20.11" + "@babel/plugin-transform-modules-commonjs" "^7.21.5" + "@babel/plugin-transform-modules-systemjs" "^7.20.11" "@babel/plugin-transform-modules-umd" "^7.18.6" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.19.1" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.20.5" "@babel/plugin-transform-new-target" "^7.18.6" "@babel/plugin-transform-object-super" "^7.18.6" - "@babel/plugin-transform-parameters" "^7.20.1" + "@babel/plugin-transform-parameters" "^7.21.3" "@babel/plugin-transform-property-literals" "^7.18.6" - "@babel/plugin-transform-regenerator" "^7.18.6" + "@babel/plugin-transform-regenerator" "^7.21.5" "@babel/plugin-transform-reserved-words" "^7.18.6" "@babel/plugin-transform-shorthand-properties" "^7.18.6" - "@babel/plugin-transform-spread" "^7.19.0" + "@babel/plugin-transform-spread" "^7.20.7" "@babel/plugin-transform-sticky-regex" "^7.18.6" "@babel/plugin-transform-template-literals" "^7.18.9" "@babel/plugin-transform-typeof-symbol" "^7.18.9" - "@babel/plugin-transform-unicode-escapes" "^7.18.10" + "@babel/plugin-transform-unicode-escapes" "^7.21.5" "@babel/plugin-transform-unicode-regex" "^7.18.6" "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.20.2" + "@babel/types" "^7.21.5" babel-plugin-polyfill-corejs2 "^0.3.3" babel-plugin-polyfill-corejs3 "^0.6.0" babel-plugin-polyfill-regenerator "^0.4.1" @@ -955,7 +1052,7 @@ "@babel/preset-react@^7.18.6": version "7.18.6" - resolved "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.18.6.tgz#979f76d6277048dc19094c217b507f3ad517dd2d" integrity sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg== dependencies: "@babel/helper-plugin-utils" "^7.18.6" @@ -965,14 +1062,16 @@ "@babel/plugin-transform-react-jsx-development" "^7.18.6" "@babel/plugin-transform-react-pure-annotations" "^7.18.6" -"@babel/preset-typescript@^7.21.0": - version "7.21.0" - resolved "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.0.tgz" - integrity sha512-myc9mpoVA5m1rF8K8DgLEatOYFDpwC+RkMkjZ0Du6uI62YvDe8uxIEYVs/VCdSJ097nlALiU/yBC7//3nI+hNg== +"@babel/preset-typescript@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.21.5.tgz#68292c884b0e26070b4d66b202072d391358395f" + integrity sha512-iqe3sETat5EOrORXiQ6rWfoOg2y68Cs75B9wNxdPW4kixJxh7aXQE1KPdWLDniC24T/6dSnguF33W9j/ZZQcmA== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.21.5" "@babel/helper-validator-option" "^7.21.0" - "@babel/plugin-transform-typescript" "^7.21.0" + "@babel/plugin-syntax-jsx" "^7.21.4" + "@babel/plugin-transform-modules-commonjs" "^7.21.5" + "@babel/plugin-transform-typescript" "^7.21.3" "@babel/regjsgen@^0.8.0": version "0.8.0" @@ -1011,6 +1110,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.5.tgz#ad22361d352a5154b498299d523cf72998a4b133" + integrity sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw== + dependencies: + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.5" + "@babel/helper-environment-visitor" "^7.21.5" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.21.5" + "@babel/types" "^7.21.5" + debug "^4.1.0" + globals "^11.1.0" + "@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.21.2" resolved "https://registry.npmjs.org/@babel/types/-/types-7.21.2.tgz" @@ -1020,6 +1135,15 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" +"@babel/types@^7.21.4", "@babel/types@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.5.tgz#18dfbd47c39d3904d5db3d3dc2cc80bedb60e5b6" + integrity sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q== + dependencies: + "@babel/helper-string-parser" "^7.21.5" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" @@ -1631,7 +1755,7 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@*", "@types/jest@^29.4.0": +"@types/jest@*": version "29.4.0" resolved "https://registry.npmjs.org/@types/jest/-/jest-29.4.0.tgz" integrity sha512-VaywcGQ9tPorCX/Jkkni7RWGFfI11whqzs8dvxF41P17Z+z872thvEvlIbznjPJ02kl1HMX3LmLOonsj2n7HeQ== @@ -1639,6 +1763,14 @@ expect "^29.0.0" pretty-format "^29.0.0" +"@types/jest@^29.5.1": + version "29.5.1" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.1.tgz#83c818aa9a87da27d6da85d3378e5a34d2f31a47" + integrity sha512-tEuVcHrpaixS36w7hpsfLBLpjtMRJUE09/MHXn923LOVojDwyC14cWcfc0rDs0VEfUyYmt/+iX1kxxp+gZMcaQ== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + "@types/jsdom@^20.0.0": version "20.0.1" resolved "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz" @@ -5244,6 +5376,13 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +swr@^2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/swr/-/swr-2.1.5.tgz#688effa719c03f6d35c66decbb0f8e79c7190399" + integrity sha512-/OhfZMcEpuz77KavXST5q6XE9nrOBOVcBLWjMT+oAE/kQHyE3PASrevXCtQDZ8aamntOfFkbVJp7Il9tNBQWrw== + dependencies: + use-sync-external-store "^1.2.0" + symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz" @@ -5457,7 +5596,7 @@ url-parse@^1.5.3: querystringify "^2.1.1" requires-port "^1.0.0" -use-sync-external-store@^1.0.0: +use-sync-external-store@^1.0.0, use-sync-external-store@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz" integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==