diff --git a/.husky/pre-push b/.husky/pre-push index fdc2655..e541df8 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -3,3 +3,5 @@ npm run test && npm run lint npm run test && npm run lint +npm run test && npm run lint +npm run test && npm run lint diff --git a/package.json b/package.json index fea4397..1186219 100644 --- a/package.json +++ b/package.json @@ -14,12 +14,14 @@ }, "dependencies": { "@reduxjs/toolkit": "^1.9.3", + "@types/react-select": "^5.0.1", "axios": "^1.3.4", "qrcode.react": "^3.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-redux": "^8.0.5", "react-router-dom": "^6.9.0", + "react-select": "^5.7.3", "swr": "^2.1.5" }, "devDependencies": { diff --git a/public/assets/icons/createplace.svg b/public/assets/icons/createplace.svg new file mode 100644 index 0000000..87f3c47 --- /dev/null +++ b/public/assets/icons/createplace.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/public/assets/icons/createuser.svg b/public/assets/icons/createuser.svg index 081c6c0..cf55856 100644 --- a/public/assets/icons/createuser.svg +++ b/public/assets/icons/createuser.svg @@ -1,7 +1,7 @@ - - - - - - - + + + + + + + \ No newline at end of file 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 index 1b63fe6..9364a07 100644 --- 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 @@ -7,7 +7,7 @@ const getPlacesRO = (placesResponse: GetPlacesResponse): GetPlacesRO => { id: placeResponse._id, placeType: placeResponse.place_type, name: placeResponse.name, - parentId: placeResponse.place_type, + parentId: placeResponse.parent_id, availableServices: placeResponse.available_services, createdAt: placeResponse.createdAt, updatedAt: placeResponse.updatedAt, diff --git a/src/driven/adapters/create-place-adapter/createPlaceAdapter.ts b/src/driven/adapters/create-place-adapter/createPlaceAdapter.ts new file mode 100644 index 0000000..401cd35 --- /dev/null +++ b/src/driven/adapters/create-place-adapter/createPlaceAdapter.ts @@ -0,0 +1,45 @@ +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'; +import { + ICreatePlaceLogicMaker, + createPlaceArguments, +} from '~/driving/application/core/places/create-place/model/submiCreatePlace'; + +const createPlacesDto = (newPlace: createPlaceArguments) => ({ + place_type: newPlace.placeType, + parent_id: newPlace.parentId || null, + name: newPlace.placeName, +}); + +const createPlaceAdapter = ( + userAdmin: AdminUserModel | null, + updateAccessToken: (newAccessToken: string) => void, + navigateToAuth: () => void, +): ICreatePlaceLogicMaker => { + const url = apiUrls.core.createPlace; + const httpHandler = async (newPlace: createPlaceArguments) => { + const httpProvider = new HTTPPovider( + { + accessToken: (userAdmin && userAdmin?.adminUserData.accessToken) || null, + refreshToken: (userAdmin && userAdmin?.adminUserData.refreshToken) || null, + }, + updateAccessToken, + navigateToAuth, + ); + const dto = createPlacesDto(newPlace); + const options: HttpOptionsType = { + url, + method: 'POST', + data: dto, + }; + + return await httpProvider.request(options); + }; + return { + httpHandler, + }; +}; + +export default createPlaceAdapter; diff --git a/src/driven/boundaries/http-boundary/httpBoundary.ts b/src/driven/boundaries/http-boundary/httpBoundary.ts index ae4acc2..435c7d5 100644 --- a/src/driven/boundaries/http-boundary/httpBoundary.ts +++ b/src/driven/boundaries/http-boundary/httpBoundary.ts @@ -46,7 +46,6 @@ export class HTTPPovider { 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]); diff --git a/src/driven/boundaries/state-management/swrBoundary.ts b/src/driven/boundaries/state-management/swrBoundary.ts index 3dc3958..d5d533a 100644 --- a/src/driven/boundaries/state-management/swrBoundary.ts +++ b/src/driven/boundaries/state-management/swrBoundary.ts @@ -10,6 +10,9 @@ export default class SwrBoundary implements StateManagementProvider { isLoading: boolean; error?: string | undefined; } { - return useSwr(key, httpHandler); + return useSwr(key, httpHandler, { + revalidateOnFocus: false, + errorRetryCount: 3, + }); } } diff --git a/src/driven/utils/components/inputs/input-wrapper/InputWrapper.tsx b/src/driven/utils/components/inputs/input-wrapper/InputWrapper.tsx new file mode 100644 index 0000000..bd3655e --- /dev/null +++ b/src/driven/utils/components/inputs/input-wrapper/InputWrapper.tsx @@ -0,0 +1,18 @@ +import React, { PropsWithChildren } from 'react'; + +export interface IInputWrapper { + title: string; + className?: string; +} + +export default function InputWrapper(props: PropsWithChildren) { + const { children, title, className } = props; + return ( +
+ + {children} +
+ ); +} diff --git a/src/driven/utils/components/inputs/select-box/InputBox.tsx b/src/driven/utils/components/inputs/select-box/InputBox.tsx new file mode 100644 index 0000000..434f27b --- /dev/null +++ b/src/driven/utils/components/inputs/select-box/InputBox.tsx @@ -0,0 +1,34 @@ +/* eslint-disable import/no-extraneous-dependencies */ +import React from 'react'; +import Select from 'react-select'; +import InputWrapper, { IInputWrapper } from '../input-wrapper/InputWrapper'; + +interface IInputBox extends IInputWrapper { + name: string; + state: { + value: { value: ValueType; label: string }; + options: { value: ValueType; label: string }[]; + setValue: (newValue: ValueType, label: string) => void; + isLoading?: boolean; + }; +} + +export default function InputBox(props: IInputBox) { + const { title, className, name, state } = props; + const { setValue, value, options } = state; + return ( + + (props: ISimpleInput) { className='bg-bg-gray h-11 rounded-lg focus:outline-0 px-2 text-txt-medium w-full' id={title} /> - + ); } diff --git a/src/driven/utils/configs/appConfig.ts b/src/driven/utils/configs/appConfig.ts index b4742ae..2fc1608 100644 --- a/src/driven/utils/configs/appConfig.ts +++ b/src/driven/utils/configs/appConfig.ts @@ -9,6 +9,7 @@ export const appConfig = { export const routes = { usersList: '/', createUser: '/create-user', + createPlace: '/create-place', authentication: '/auth', }; @@ -23,6 +24,11 @@ export const routesData = { icon: icons.createUser, title: staticMessages.global.createUser, }, + createPlace: { + path: routes.createPlace, + icon: icons.createPlace, + title: staticMessages.global.createPlace, + }, }; const baseApiUrl = ENVs.apiOrigin; @@ -34,6 +40,7 @@ export const apiUrls = { createUserAccount: `${baseApiUrl}${ENVs.apiCreateUserAccount}`, createUserProfile: `${baseApiUrl}${ENVs.apiCreateUserProfile}`, createMember: `${baseApiUrl}${ENVs.apiCreateMember}`, + createPlace: `${baseApiUrl}${ENVs.apiCreatePlace}`, }, generic: { authPhonenumber: `${ENVs.apiAuthOrigin}${ENVs.apiAuthPhonenumber}`, diff --git a/src/driven/utils/constants/assertUrls.ts b/src/driven/utils/constants/assertUrls.ts index 938b105..a22aa41 100644 --- a/src/driven/utils/constants/assertUrls.ts +++ b/src/driven/utils/constants/assertUrls.ts @@ -5,5 +5,6 @@ export const icons = { logoBlack: `${baseIconsUrl}logo-black.svg`, users: `${baseIconsUrl}users.svg`, createUser: `${baseIconsUrl}createuser.svg`, + createPlace: `${baseIconsUrl}createplace.svg`, qrcode: `${baseIconsUrl}qrcode.png`, }; diff --git a/src/driven/utils/constants/envs.ts b/src/driven/utils/constants/envs.ts index 428a48c..80f6d65 100644 --- a/src/driven/utils/constants/envs.ts +++ b/src/driven/utils/constants/envs.ts @@ -10,4 +10,5 @@ export const ENVs = { apiQr: process.env.VITE_API_QR, apiCreateUserProfile: process.env.VITE_API_USERS_PROFILE, apiCreateMember: process.env.VITE_API_CREATE_MEMBER, + apiCreatePlace: process.env.VITE_API_PLACES, }; diff --git a/src/driven/utils/constants/staticMessages.ts b/src/driven/utils/constants/staticMessages.ts index a666000..d61ef69 100644 --- a/src/driven/utils/constants/staticMessages.ts +++ b/src/driven/utils/constants/staticMessages.ts @@ -4,6 +4,7 @@ export const staticMessages = { input: 'please fill all inputs correctly', phonenumber: 'please fill the valid number', otp: 'please fill the otp fields correctly', + inputPlace: 'place type and name of place is necessary', }, users: 'Users', submit: 'Submit', @@ -13,9 +14,12 @@ export const staticMessages = { title: 'title', status: 'Status', placeType: 'Place Type', + placeParentId: 'Parent id', address: 'Address', qrCode: 'qrCode', createUser: 'Create user', + createPlace: 'Create place', + PlaceName: 'Place name', phonenumber: 'Phone Number', enterPanel: 'Enter to Panel', enterPhoneNumber: 'Enter your phone number', @@ -23,15 +27,29 @@ export const staticMessages = { success: { createUser: 'user created successfully', createMember: 'member created successfully', + createPlace: 'place created successfully', }, and: 'and', canUseFor: 'can use for', oneTime: 'one time', multipleTimes: 'multiple times', }, + placeTypes: { + continent: 'Continent', + country: 'Country', + state: 'State', + region: 'Region', + city: 'City', + district: 'District', + street: 'Street', + building: 'Building', + section: 'Section', + floor: 'Floor', + flat: 'Flat', + }, service: { errors: { - 500: 'server not respond please try again later!', + 500: 'there is a problem with connecting to the server please try again later!', 401: 'Authentication error!', }, }, diff --git a/src/driving/application/core/places/common/hooks/useGetPlaceList.ts b/src/driving/application/core/places/common/hooks/useGetPlaceList.ts new file mode 100644 index 0000000..ab5cb1d --- /dev/null +++ b/src/driving/application/core/places/common/hooks/useGetPlaceList.ts @@ -0,0 +1,79 @@ +/* eslint-disable consistent-return */ +/* eslint-disable no-underscore-dangle */ +import { QrPlace } from '~/business-logic/core/places/common/entity/placeEntity'; +import getPlaces from '~/business-logic/core/places/get-places'; +import AdminUserModel from '~/business-logic/generic/admin-user/common/data/model/adminUserModel'; +import getPlacesAdapter from '~/driven/adapters/get-places-adapter/getPlacesAdapter'; +import { HTTPPovider } from '~/driven/boundaries/http-boundary/httpBoundary'; +import { HttpOptionsType } from '~/driven/boundaries/http-boundary/protocols'; +import { apiUrls } from '~/driven/utils/configs/appConfig'; +import useGetNavigatorAndTokenUpdater from '~/driven/utils/helpers/hooks/getNavigatorAndAccessTokenUpdator'; +import { prepareStateManagementForVM } from '~/driven/utils/helpers/globalHelpers'; +import PlacesModel from '~/business-logic/core/places/common/model/placesModel'; +import placesListModel from '../../places-list/model/placesListModel'; + +type QrCodeResponse = { + one_time: false; + place_id: string; + user_id: string; + _id: string; +}[]; +const QrCodeRO = (response: QrCodeResponse): QrPlace[] => { + return response.map((qrCode) => ({ + id: qrCode._id, + oneTime: qrCode.one_time, + placeId: qrCode.place_id, + userId: qrCode.user_id, + })); +}; + +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 }; +}; + +const useGetPlaceList = () => { + const { accessTokenUpdateHandler, notLoginAuth, userData } = useGetNavigatorAndTokenUpdater(); + + const getQrCodes = async () => { + try { + // url + const apiUrl = apiUrls.core.getQrs; + // options + const apiOptions: HttpOptionsType = { + url: apiUrl, + }; + // request + const userToken = { + accessToken: userData.user?.adminUserData.accessToken || null, + refreshToken: userData.user?.adminUserData.refreshToken || null, + }; + const httpProvider = new HTTPPovider(userToken, accessTokenUpdateHandler, notLoginAuth); + const response = await httpProvider.request(apiOptions); + const qrCodeList = QrCodeRO(response); + return qrCodeList; + // update the query + } catch (error) { + console.log(error); + } + }; + + const { getingPlacesLogic, url } = prepareTheLogicForModel(); + const getPlacesWithQrLogic = async () => { + const placesList = await getingPlacesLogic(); + const qrCodesList = await getQrCodes(); + placesList.setQrFor(qrCodesList || []); + return placesList; + }; + const placesModel = async () => await placesListModel(getPlacesWithQrLogic); + const useGetPlacesList = prepareStateManagementForVM(url, placesModel); + + return useGetPlacesList; +}; + +export default useGetPlaceList; diff --git a/src/driving/application/core/places/create-place/index.tsx b/src/driving/application/core/places/create-place/index.tsx new file mode 100644 index 0000000..2d971b7 --- /dev/null +++ b/src/driving/application/core/places/create-place/index.tsx @@ -0,0 +1,3 @@ +import CreatePlace from './infra/CreatePlace'; + +export default CreatePlace; diff --git a/src/driving/application/core/places/create-place/infra/CreatePlace.tsx b/src/driving/application/core/places/create-place/infra/CreatePlace.tsx new file mode 100644 index 0000000..89d6050 --- /dev/null +++ b/src/driving/application/core/places/create-place/infra/CreatePlace.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import createPlaceAdapter from '~/driven/adapters/create-place-adapter/createPlaceAdapter'; +import useGetNavigatorAndTokenUpdater from '~/driven/utils/helpers/hooks/getNavigatorAndAccessTokenUpdator'; +import CreatePlaceView from '../view/CreatePlaceView'; +import useCreatePlaceVm from '../viewmodel/createPlaceVM'; +import createPlaceModel from '../model/createPlaceModel'; + +export default function CreatePlace() { + const { accessTokenUpdateHandler, notLoginAuth, userData } = useGetNavigatorAndTokenUpdater(); + const { user } = userData; + + const createPlaceDrivenAdapter = createPlaceAdapter(user, accessTokenUpdateHandler, notLoginAuth); + const { inputOptions, createPlace } = createPlaceModel(createPlaceDrivenAdapter); + const { formStateData, inputStateHandlers, selectBoxOptions, handleSubmitCreatePlaceForm } = useCreatePlaceVm({ + getInputOptions: inputOptions, + createPlaceModel: createPlace, + }); + return ( + + ); +} diff --git a/src/driving/application/core/places/create-place/model/createPlaceModel.ts b/src/driving/application/core/places/create-place/model/createPlaceModel.ts new file mode 100644 index 0000000..6c46b52 --- /dev/null +++ b/src/driving/application/core/places/create-place/model/createPlaceModel.ts @@ -0,0 +1,20 @@ +import { CreatePlaceType } from './protocols'; +import getValidOptionsOfSelectBox from './getValidOptionsOfSelectBox'; +import createPlaceLogicMaker, { ICreatePlaceLogicMaker } from './submiCreatePlace'; + +type ICreatePlaceModel = ICreatePlaceLogicMaker; + +const createPlaceModel = (depenedencies: ICreatePlaceModel): CreatePlaceType => { + const { getPlacesTypeOptionsOfSelectBox, getValidParentIdsOptionsOfSelectBox } = getValidOptionsOfSelectBox; + + const { createPlace } = createPlaceLogicMaker(depenedencies); + return { + createPlace, + inputOptions: { + getPlaceType: getPlacesTypeOptionsOfSelectBox, + getParentIds: getValidParentIdsOptionsOfSelectBox, + }, + }; +}; + +export default createPlaceModel; diff --git a/src/driving/application/core/places/create-place/model/getValidOptionsOfSelectBox.ts b/src/driving/application/core/places/create-place/model/getValidOptionsOfSelectBox.ts new file mode 100644 index 0000000..afca010 --- /dev/null +++ b/src/driving/application/core/places/create-place/model/getValidOptionsOfSelectBox.ts @@ -0,0 +1,66 @@ +import Places from '~/business-logic/core/places/common/entity/placeEntity'; +import PlacesModel from '~/business-logic/core/places/common/model/placesModel'; +import { staticMessages } from '~/driven/utils/constants/staticMessages'; +import { placeTypes } from '../view/protocol'; + +export const placeTypeTuple: ReadonlyArray<`${placeTypes}`> = [ + placeTypes.continent, + placeTypes.country, + placeTypes.state, + placeTypes.region, + placeTypes.city, + placeTypes.district, + placeTypes.street, + placeTypes.building, + placeTypes.section, + placeTypes.floor, + placeTypes.flat, +]; + +const makeOptionsOfSelectBoxForParentIds = (places: Places[], currentPlaceType: `${placeTypes}`) => { + const currentPlaceTypeIndex = placeTypeTuple.indexOf(currentPlaceType); + if (currentPlaceTypeIndex === -1) throw new Error(); + + const placesValidToShow = places.reduce((prevVal, currentVal) => { + const placeListItemIndex = placeTypeTuple.indexOf(currentVal.placeType as `${placeTypes}`); + + if (!currentVal.parentId) return prevVal; + + if (placeListItemIndex === -1) return prevVal; + if (placeListItemIndex > currentPlaceTypeIndex) return prevVal; + + const label = `${currentVal.placeType} ${currentVal.name}`; + return [...prevVal, { value: currentVal.parentId, label }]; + }, <{ value: string; label: string }[]>[]); + + return placesValidToShow; +}; +const getValidParentIdsOptionsOfSelectBox = ( + currentPlaceType: `${placeTypes}`, + placesList: PlacesModel, +): { value: string; label: string }[] => { + try { + if (!placesList) throw new Error(); + const places = placesList.getData(); + if (!places.length) throw new Error(); + + const placesOptionsValidToShow = makeOptionsOfSelectBoxForParentIds(places, currentPlaceType); + + return placesOptionsValidToShow; + } catch (_error) { + return []; + } +}; + +const getPlacesTypeOptionsOfSelectBox = () => { + return Object.keys(placeTypes).map((placeTypeKey) => { + const key = placeTypeKey as keyof typeof placeTypes; + + return { value: placeTypes[key], label: staticMessages.placeTypes[placeTypes[key]] }; + }); +}; + +export default { + getValidParentIdsOptionsOfSelectBox, + getPlacesTypeOptionsOfSelectBox, +}; diff --git a/src/driving/application/core/places/create-place/model/index.ts b/src/driving/application/core/places/create-place/model/index.ts new file mode 100644 index 0000000..3089503 --- /dev/null +++ b/src/driving/application/core/places/create-place/model/index.ts @@ -0,0 +1,3 @@ +import createPlaceModel from './createPlaceModel'; + +export default createPlaceModel; diff --git a/src/driving/application/core/places/create-place/model/protocols.ts b/src/driving/application/core/places/create-place/model/protocols.ts new file mode 100644 index 0000000..639bfae --- /dev/null +++ b/src/driving/application/core/places/create-place/model/protocols.ts @@ -0,0 +1,20 @@ +import PlacesModel from '~/business-logic/core/places/common/model/placesModel'; +import { placeTypes } from '../view/protocol'; +import { createPlaceArguments } from './submiCreatePlace'; + +export type CreatePlaceType = { + createPlace: (placeData: createPlaceArguments) => Promise; + inputOptions: { + getPlaceType: () => { + value: placeTypes; + label: string; + }[]; + getParentIds: ( + currentPlaceType: `${placeTypes}`, + placesList: PlacesModel, + ) => { + value: string; + label: string; + }[]; + }; +}; diff --git a/src/driving/application/core/places/create-place/model/submiCreatePlace.ts b/src/driving/application/core/places/create-place/model/submiCreatePlace.ts new file mode 100644 index 0000000..51012a4 --- /dev/null +++ b/src/driving/application/core/places/create-place/model/submiCreatePlace.ts @@ -0,0 +1,24 @@ +import { placeTypes } from '../view/protocol'; + +export type createPlaceArguments = { + placeType: `${placeTypes}`; + placeName: string; + parentId: string; +}; + +export interface ICreatePlaceLogicMaker { + httpHandler: (placeData: createPlaceArguments) => Promise; +} + +const createPlaceLogicMaker = (depenedencies: ICreatePlaceLogicMaker) => { + const { httpHandler } = depenedencies; + + const createPlace = async (placeData: createPlaceArguments) => { + return await httpHandler(placeData); + }; + return { + createPlace, + }; +}; + +export default createPlaceLogicMaker; diff --git a/src/driving/application/core/places/create-place/view/CreatePlaceView.tsx b/src/driving/application/core/places/create-place/view/CreatePlaceView.tsx new file mode 100644 index 0000000..4774e12 --- /dev/null +++ b/src/driving/application/core/places/create-place/view/CreatePlaceView.tsx @@ -0,0 +1,61 @@ +import React from 'react'; +import SimpleInput from '~/driven/utils/components/inputs/simple-input/SimpleInput'; +import InputBox from '~/driven/utils/components/inputs/select-box/InputBox'; +import { staticMessages } from '~/driven/utils/constants/staticMessages'; +import PrimaryButton from '~/driven/utils/components/buttons/primary-button/PrimaryButton'; +import Notification from '~/driven/utils/components/Notification/Notification'; +import { ICreatePlaceProps } from './protocol'; + +export default function CreatePlaceView(props: ICreatePlaceProps) { + const { formStateData, inputStateHandlers, inputOptions, handleSubmitCreatePlaceForm } = props; + const { formState, error, setError } = formStateData; + + return ( + <> + {Boolean(error.message) && ( + setError({ message: '', type: 'error' })} + /> + )} +
+ + + +
+ null} title={staticMessages.global.submit} /> +
+ + + ); +} diff --git a/src/driving/application/core/places/create-place/view/protocol.ts b/src/driving/application/core/places/create-place/view/protocol.ts new file mode 100644 index 0000000..ac3b76c --- /dev/null +++ b/src/driving/application/core/places/create-place/view/protocol.ts @@ -0,0 +1,53 @@ +export enum placeTypes { + continent = 'continent', + country = 'country', + state = 'state', + region = 'region', + city = 'city', + district = 'district', + street = 'street', + building = 'building', + section = 'section', + floor = 'floor', + flat = 'flat', +} + +export type PlaceFormState = { + placeName: string; + placeTypes: `${placeTypes}`; + parentId: { value: string; label: string }; +}; + +export interface ICreatePlaceProps { + handleSubmitCreatePlaceForm: (e: React.FormEvent) => Promise; + formStateData: { + formState: PlaceFormState; + error: { + message: string; + type: 'error' | 'success'; + }; + setError: React.Dispatch< + React.SetStateAction<{ + message: string; + type: 'error' | 'success'; + }> + >; + setFormState: React.Dispatch>; + }; + inputStateHandlers: { + name: (_name: string, newValue: string) => void; + placeType: (newValue: `${placeTypes}`) => void; + parentId: (newValue: string, label: string) => void; + }; + inputOptions: { + placeType: { + value: placeTypes; + label: string; + }[]; + parentId: { + value: string; + label: string; + }[]; + isParentIdLoading: boolean; + }; +} diff --git a/src/driving/application/core/places/create-place/viewmodel/createPlaceVM.ts b/src/driving/application/core/places/create-place/viewmodel/createPlaceVM.ts new file mode 100644 index 0000000..a6386c8 --- /dev/null +++ b/src/driving/application/core/places/create-place/viewmodel/createPlaceVM.ts @@ -0,0 +1,97 @@ +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { AxiosError } from 'axios'; +import { staticMessages } from '~/driven/utils/constants/staticMessages'; +import useGetPlaceList from '../../common/hooks/useGetPlaceList'; +import { PlaceFormState, placeTypes } from '../view/protocol'; +import { createPlaceArguments } from '../model/submiCreatePlace'; +import { ICreatePlaceVm, initialPlaceFormState } from './protocols'; + +const onChangeInputStateHandlers = (setFormState: React.Dispatch>) => { + const handleNameSetState = (_name: string, newValue: string) => + setFormState((prev) => ({ ...prev, placeName: newValue })); + + const handlePlaceTypeSetState = (newValue: `${placeTypes}`) => + setFormState((prev) => ({ ...prev, placeTypes: newValue })); + + const handleParentIdSetState = (newValue: string, label: string) => + setFormState((prev) => ({ ...prev, parentId: { value: newValue, label } })); + + return { + handleNameSetState, + handlePlaceTypeSetState, + handleParentIdSetState, + }; +}; + +const resetParentIdOnChangePlaceType = (setFormState: React.Dispatch>) => { + setFormState((prev) => ({ ...prev, parentId: { label: '', value: '' } })); +}; + +const useCreatePlaceVm = (dependencies: ICreatePlaceVm) => { + const { getInputOptions, createPlaceModel } = dependencies; + const { getParentIds, getPlaceType } = getInputOptions; + const [formState, setFormState] = useState(initialPlaceFormState); + const [error, setError] = useState<{ message: string; type: 'error' | 'success' }>({ message: '', type: 'error' }); + + const { handleNameSetState, handleParentIdSetState, handlePlaceTypeSetState } = useMemo( + () => onChangeInputStateHandlers(setFormState), + [], + ); + + const handleSubmitCreatePlaceForm = async (e: React.FormEvent) => { + e.preventDefault(); + try { + const { parentId, placeName, placeTypes: placeType } = formState; + if (!placeName || !placeName) throw new Error(staticMessages.global.errors.inputPlace); + const newPlaceData: createPlaceArguments = { + parentId: parentId.value, + placeName, + placeType, + }; + const response = await createPlaceModel(newPlaceData); + if (!response) throw new Error(staticMessages.service.errors[500]); + setError({ + message: staticMessages.global.success.createPlace, + 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(() => resetParentIdOnChangePlaceType(setFormState), [formState.placeTypes]); + + const getPlaceList = useCallback(() => useGetPlaceList()(), []); + const places = getPlaceList(); + + const placeTypesOptions = useMemo(() => getPlaceType(), []); + const parentIdsOptions = useMemo(() => { + if (!places.data) return []; + return getParentIds(formState.placeTypes, places.data); + }, [formState.placeTypes, places]); + + return { + handleSubmitCreatePlaceForm, + formStateData: { + formState, + error, + setError, + setFormState, + }, + inputStateHandlers: { + name: handleNameSetState, + parentId: handleParentIdSetState, + placeType: handlePlaceTypeSetState, + }, + selectBoxOptions: { + placeTypesOptions, + parentIdsOptions, + isParentIdLoading: places.isLoading, + }, + }; +}; + +export default useCreatePlaceVm; diff --git a/src/driving/application/core/places/create-place/viewmodel/protocols.ts b/src/driving/application/core/places/create-place/viewmodel/protocols.ts new file mode 100644 index 0000000..7dd5057 --- /dev/null +++ b/src/driving/application/core/places/create-place/viewmodel/protocols.ts @@ -0,0 +1,14 @@ +import { CreatePlaceType } from '../model/protocols'; +import { createPlaceArguments } from '../model/submiCreatePlace'; +import { PlaceFormState, placeTypes } from '../view/protocol'; + +export const initialPlaceFormState: PlaceFormState = { + placeTypes: placeTypes.continent, + placeName: '', + parentId: { value: '', label: '' }, +}; + +export interface ICreatePlaceVm { + getInputOptions: CreatePlaceType['inputOptions']; + createPlaceModel: (placeData: createPlaceArguments) => Promise; +} diff --git a/src/driving/application/core/places/places-list/infra/PlacesList.tsx b/src/driving/application/core/places/places-list/infra/PlacesList.tsx index 571b93f..1fe1e19 100644 --- a/src/driving/application/core/places/places-list/infra/PlacesList.tsx +++ b/src/driving/application/core/places/places-list/infra/PlacesList.tsx @@ -1,44 +1,9 @@ /* eslint-disable consistent-return */ /* eslint-disable no-underscore-dangle */ 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 { apiUrls } from '~/driven/utils/configs/appConfig'; -import { HttpOptionsType } from '~/driven/boundaries/http-boundary/protocols'; -import { HTTPPovider } from '~/driven/boundaries/http-boundary/httpBoundary'; -import { QrPlace } from '~/business-logic/core/places/common/entity/placeEntity'; import PlacesListView from '../view/PlacesListView'; import usePlacesListVM from '../viewmodel/placesListVM'; -import placesListModel from '../model/placesListModel'; - -type QrCodeResponse = { - one_time: false; - place_id: string; - user_id: string; - _id: string; -}[]; -const QrCodeRO = (response: QrCodeResponse): QrPlace[] => { - return response.map((qrCode) => ({ - id: qrCode._id, - oneTime: qrCode.one_time, - placeId: qrCode.place_id, - userId: qrCode.user_id, - })); -}; - -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 }; -}; +import useGetPlaceList from '../../common/hooks/useGetPlaceList'; export interface IPlacesListProps { selectedRowId: string; @@ -47,40 +12,8 @@ export interface IPlacesListProps { export default function PlacessList(props: IPlacesListProps) { const { selectedRowId, setSelectedRowId } = props; - const { accessTokenUpdateHandler, notLoginAuth, userData } = useGetNavigatorAndTokenUpdater(); - const getQrCodes = async () => { - try { - // url - const apiUrl = apiUrls.core.getQrs; - // options - const apiOptions: HttpOptionsType = { - url: apiUrl, - }; - // request - const userToken = { - accessToken: userData.user?.adminUserData.accessToken || null, - refreshToken: userData.user?.adminUserData.refreshToken || null, - }; - const httpProvider = new HTTPPovider(userToken, accessTokenUpdateHandler, notLoginAuth); - const response = await httpProvider.request(apiOptions); - const qrCodeList = QrCodeRO(response); - return qrCodeList; - // update the query - } catch (error) { - console.log(error); - } - }; - - const { getingPlacesLogic, url } = prepareTheLogicForModel(); - const getPlacesWithQrLogic = async () => { - const placesList = await getingPlacesLogic(); - const qrCodesList = await getQrCodes(); - placesList.setQrFor(qrCodesList || []); - return placesList; - }; - const placesModel = async () => await placesListModel(getPlacesWithQrLogic); - const useGetPlacesList = prepareStateManagementForVM(url, placesModel); + const useGetPlacesList = useGetPlaceList(); const { placesData } = usePlacesListVM({ useGetPlacesList, }); diff --git a/src/driving/application/core/users/create-user/view/protocols.ts b/src/driving/application/core/users/create-user/view/protocols.ts index 05cea73..fd01eee 100644 --- a/src/driving/application/core/users/create-user/view/protocols.ts +++ b/src/driving/application/core/users/create-user/view/protocols.ts @@ -2,7 +2,7 @@ import { INewUserData } from '~/business-logic/core/users/create-user/create-acc import { SetStateInputMethod } from '~/driven/utils/components/inputs/simple-input/SimpleInput'; export default interface ICreateUserViewProps { - onSubmit: (e: React.FormEvent) => void; + onSubmit: (e: React.FormEvent) => Promise; stateHandler: { inputStates: INewUserData; inputsSetStates: SetStateInputMethod; diff --git a/src/driving/application/support/sidebar/view/Sidebar.tsx b/src/driving/application/support/sidebar/view/Sidebar.tsx index f3af15d..9667881 100644 --- a/src/driving/application/support/sidebar/view/Sidebar.tsx +++ b/src/driving/application/support/sidebar/view/Sidebar.tsx @@ -19,7 +19,7 @@ export default function Sidebar() { isCurrentPage.pathname === routesData[key].path ? 'bg-primary-300' : '' }`} > - page icon + page icon
{routesData[key].title}
); diff --git a/src/driving/main/Router/Router.tsx b/src/driving/main/Router/Router.tsx index d674476..76bd96e 100644 --- a/src/driving/main/Router/Router.tsx +++ b/src/driving/main/Router/Router.tsx @@ -5,6 +5,7 @@ import CreateUser from '../pages/CreateUser'; import MainPageLayout from '../pages/layouts/MainPageLayout'; import AuthenticationPage from '../pages/Authentication'; import UserLoginLayout from '../pages/layouts/UserLoginLayout'; +import CreatePlacePage from '../pages/CreatePlacePage'; export default function Router() { const location = useLocation(); @@ -14,6 +15,7 @@ export default function Router() { }> } /> } /> + } /> }> } /> diff --git a/src/driving/main/pages/CreatePlacePage.tsx b/src/driving/main/pages/CreatePlacePage.tsx new file mode 100644 index 0000000..6187e30 --- /dev/null +++ b/src/driving/main/pages/CreatePlacePage.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import PageTitle from '~/driven/utils/components/page-title/pageTitle'; +import { staticMessages } from '~/driven/utils/constants/staticMessages'; +import CreatePlace from '~/driving/application/core/places/create-place'; + +export default function CreatePlacePage() { + return ( + <> + + + + ); +} diff --git a/yarn.lock b/yarn.lock index ecc5a77..db7e3a8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -197,7 +197,7 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-module-imports@^7.21.4": +"@babel/helper-module-imports@^7.16.7", "@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== @@ -1078,6 +1078,13 @@ resolved "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== +"@babel/runtime@^7.12.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.3.tgz#0a7fce51d43adbf0f7b517a71f4c3aaca92ebcbb" + integrity sha512-XsDuspWKLUsxwCp6r7EhsExHtYfbe5oAGQ19kqngTdCPUoPQzOPdUbD/pB9PJiwb2ptYKQDjSJT3R6dC+EPqfQ== + dependencies: + regenerator-runtime "^0.13.11" + "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.20.7", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": version "7.21.0" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz" @@ -1149,6 +1156,39 @@ resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@emotion/babel-plugin@^11.11.0": + version "11.11.0" + resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz#c2d872b6a7767a9d176d007f5b31f7d504bb5d6c" + integrity sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ== + dependencies: + "@babel/helper-module-imports" "^7.16.7" + "@babel/runtime" "^7.18.3" + "@emotion/hash" "^0.9.1" + "@emotion/memoize" "^0.8.1" + "@emotion/serialize" "^1.1.2" + babel-plugin-macros "^3.1.0" + convert-source-map "^1.5.0" + escape-string-regexp "^4.0.0" + find-root "^1.1.0" + source-map "^0.5.7" + stylis "4.2.0" + +"@emotion/cache@^11.11.0", "@emotion/cache@^11.4.0": + version "11.11.0" + resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.11.0.tgz#809b33ee6b1cb1a625fef7a45bc568ccd9b8f3ff" + integrity sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ== + dependencies: + "@emotion/memoize" "^0.8.1" + "@emotion/sheet" "^1.2.2" + "@emotion/utils" "^1.2.1" + "@emotion/weak-memoize" "^0.3.1" + stylis "4.2.0" + +"@emotion/hash@^0.9.1": + version "0.9.1" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.1.tgz#4ffb0055f7ef676ebc3a5a91fb621393294e2f43" + integrity sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ== + "@emotion/is-prop-valid@^1.1.0": version "1.2.0" resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz" @@ -1161,6 +1201,41 @@ resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz" integrity sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA== +"@emotion/memoize@^0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.1.tgz#c1ddb040429c6d21d38cc945fe75c818cfb68e17" + integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA== + +"@emotion/react@^11.8.1": + version "11.11.0" + resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.11.0.tgz#408196b7ef8729d8ad08fc061b03b046d1460e02" + integrity sha512-ZSK3ZJsNkwfjT3JpDAWJZlrGD81Z3ytNDsxw1LKq1o+xkmO5pnWfr6gmCC8gHEFf3nSSX/09YrG67jybNPxSUw== + dependencies: + "@babel/runtime" "^7.18.3" + "@emotion/babel-plugin" "^11.11.0" + "@emotion/cache" "^11.11.0" + "@emotion/serialize" "^1.1.2" + "@emotion/use-insertion-effect-with-fallbacks" "^1.0.1" + "@emotion/utils" "^1.2.1" + "@emotion/weak-memoize" "^0.3.1" + hoist-non-react-statics "^3.3.1" + +"@emotion/serialize@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.2.tgz#017a6e4c9b8a803bd576ff3d52a0ea6fa5a62b51" + integrity sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA== + dependencies: + "@emotion/hash" "^0.9.1" + "@emotion/memoize" "^0.8.1" + "@emotion/unitless" "^0.8.1" + "@emotion/utils" "^1.2.1" + csstype "^3.0.2" + +"@emotion/sheet@^1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.2.tgz#d58e788ee27267a14342303e1abb3d508b6d0fec" + integrity sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA== + "@emotion/stylis@^0.8.4": version "0.8.5" resolved "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz" @@ -1171,6 +1246,26 @@ resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz" integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== +"@emotion/unitless@^0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.1.tgz#182b5a4704ef8ad91bde93f7a860a88fd92c79a3" + integrity sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ== + +"@emotion/use-insertion-effect-with-fallbacks@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz#08de79f54eb3406f9daaf77c76e35313da963963" + integrity sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw== + +"@emotion/utils@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.1.tgz#bbab58465738d31ae4cb3dbb6fc00a5991f755e4" + integrity sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg== + +"@emotion/weak-memoize@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz#d0fce5d07b0620caa282b5131c297bb60f9d87e6" + integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww== + "@esbuild/android-arm64@0.16.17": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz#cf91e86df127aa3d141744edafcba0abdc577d23" @@ -1313,6 +1408,18 @@ resolved "https://registry.npmjs.org/@eslint/js/-/js-8.36.0.tgz" integrity sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg== +"@floating-ui/core@^1.2.6": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.2.6.tgz#d21ace437cc919cdd8f1640302fa8851e65e75c0" + integrity sha512-EvYTiXet5XqweYGClEmpu3BoxmsQ4hkj3QaYA6qEnigCWffTP3vNRwBReTdrwDwo7OoJ3wM8Uoe9Uk4n+d4hfg== + +"@floating-ui/dom@^1.0.1": + version "1.2.9" + resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.2.9.tgz#b9ed1c15d30963419a6736f1b7feb350dd49c603" + integrity sha512-sosQxsqgxMNkV3C+3UqTS6LxP7isRLwX8WMepp843Rb3/b0Wz8+MdUkxJksByip3C2WwLugLHN1b4ibn//zKwQ== + dependencies: + "@floating-ui/core" "^1.2.6" + "@humanwhocodes/config-array@^0.11.8": version "0.11.8" resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz" @@ -1795,6 +1902,11 @@ resolved "https://registry.npmjs.org/@types/node/-/node-18.15.1.tgz" integrity sha512-U2TWca8AeHSmbpi314QBESRk7oPjSZjDsR+c+H4ECC1l+kFgpZf8Ydhv3SJpPy51VyZHHqxlb6mTTqYNNRVAIw== +"@types/parse-json@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== + "@types/prettier@^2.1.5": version "2.7.2" resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz" @@ -1812,6 +1924,20 @@ dependencies: "@types/react" "*" +"@types/react-select@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@types/react-select/-/react-select-5.0.1.tgz#04fc85edd34a72675a0ab56ad4c30428aab0e444" + integrity sha512-h5Im0AP0dr4AVeHtrcvQrLV+gmPa7SA0AGdxl2jOhtwiE6KgXBFSogWw8az32/nusE6AQHlCOHQWjP1S/+oMWA== + dependencies: + react-select "*" + +"@types/react-transition-group@^4.4.0": + version "4.4.6" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.6.tgz#18187bcda5281f8e10dfc48f0943e2fdf4f75e2e" + integrity sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew== + dependencies: + "@types/react" "*" + "@types/react@*", "@types/react@^18.0.27": version "18.0.28" resolved "https://registry.npmjs.org/@types/react/-/react-18.0.28.tgz" @@ -2224,6 +2350,15 @@ babel-plugin-jest-hoist@^29.5.0: "@types/babel__core" "^7.1.14" "@types/babel__traverse" "^7.0.6" +babel-plugin-macros@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" + integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg== + dependencies: + "@babel/runtime" "^7.12.5" + cosmiconfig "^7.0.0" + resolve "^1.19.0" + babel-plugin-polyfill-corejs2@^0.3.3: version "0.3.3" resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz" @@ -2498,7 +2633,7 @@ confusing-browser-globals@^1.0.10: resolved "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz" integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA== -convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@^1.5.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.9.0" resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== @@ -2515,6 +2650,17 @@ core-js-compat@^3.25.1: dependencies: browserslist "^4.21.5" +cosmiconfig@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" + integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" @@ -2714,6 +2860,14 @@ dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9: resolved "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz" integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== +dom-helpers@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" + integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== + dependencies: + "@babel/runtime" "^7.8.7" + csstype "^3.0.2" + domexception@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz" @@ -3228,6 +3382,11 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +find-root@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" + integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== + find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" @@ -3470,7 +3629,7 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" -hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: +hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -4421,6 +4580,11 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" +memoize-one@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045" + integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw== + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" @@ -4676,7 +4840,7 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse-json@^5.2.0: +parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -4846,7 +5010,7 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.8.1: +prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -4875,6 +5039,11 @@ pure-rand@^6.0.0: resolved "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.1.tgz" integrity sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg== +qrcode.react@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/qrcode.react/-/qrcode.react-3.1.0.tgz#5c91ddc0340f768316fbdb8fff2765134c2aecd8" + integrity sha512-oyF+Urr3oAMUG/OiOuONL3HXM+53wvuH3mtIWQrYmsXoAq0DkvZp2RYUWFSMFtbdOpuS++9v+WAkzNVkMlNW6Q== + querystringify@^2.1.1: version "2.2.0" resolved "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz" @@ -4945,6 +5114,31 @@ react-router@6.9.0: dependencies: "@remix-run/router" "1.4.0" +react-select@*, react-select@^5.7.3: + version "5.7.3" + resolved "https://registry.yarnpkg.com/react-select/-/react-select-5.7.3.tgz#fa0dc9a23cad6ff3871ad3829f6083a4b54961a2" + integrity sha512-z8i3NCuFFWL3w27xq92rBkVI2onT0jzIIPe480HlBjXJ3b5o6Q+Clp4ydyeKrj9DZZ3lrjawwLC5NGl0FSvUDg== + dependencies: + "@babel/runtime" "^7.12.0" + "@emotion/cache" "^11.4.0" + "@emotion/react" "^11.8.1" + "@floating-ui/dom" "^1.0.1" + "@types/react-transition-group" "^4.4.0" + memoize-one "^6.0.0" + prop-types "^15.6.0" + react-transition-group "^4.3.0" + use-isomorphic-layout-effect "^1.1.2" + +react-transition-group@^4.3.0: + version "4.4.5" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" + integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== + dependencies: + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react@^18.2.0: version "18.2.0" resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz" @@ -5089,6 +5283,15 @@ resolve@^1.1.7, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.1: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.19.0: + version "1.22.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== + dependencies: + is-core-module "^2.11.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^2.0.0-next.4: version "2.0.0-next.4" resolved "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz" @@ -5218,6 +5421,11 @@ source-map-support@0.5.13: buffer-from "^1.0.0" source-map "^0.6.0" +source-map@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== + source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" @@ -5350,6 +5558,11 @@ styled-components@^5.3.8: shallowequal "^1.1.0" supports-color "^5.5.0" +stylis@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" + integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw== + supports-color@^5.3.0, supports-color@^5.5.0: version "5.5.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" @@ -5596,6 +5809,11 @@ url-parse@^1.5.3: querystringify "^2.1.1" requires-port "^1.0.0" +use-isomorphic-layout-effect@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz#497cefb13d863d687b08477d9e5a164ad8c1a6fb" + integrity sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA== + 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" @@ -5768,7 +5986,7 @@ yallist@^4.0.0: resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.10.2: +yaml@^1.10.0, yaml@^1.10.2: version "1.10.2" resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==