feature/create-place #8

Merged
behnam merged 5 commits from feature/create-place into develop 2023-05-30 13:07:57 +00:00
14 changed files with 191 additions and 45 deletions
Showing only changes of commit 6da3051038 - Show all commits

View File

@ -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<string>(options);
};
return {
httpHandler,
};
};
export default createPlaceAdapter;

View File

@ -46,7 +46,6 @@ export class HTTPPovider {
async request<R>(customOptions: HttpOptionsType) { async request<R>(customOptions: HttpOptionsType) {
const axiosInstance = this.handleRequestInterceptor(); const axiosInstance = this.handleRequestInterceptor();
this.responseIncepter(axiosInstance); this.responseIncepter(axiosInstance);
const response = await axiosInstance<ApiGlobalResponseObject<R>>(customOptions); const response = await axiosInstance<ApiGlobalResponseObject<R>>(customOptions);
if (!response) throw new Error(staticMessages.service.errors[500]); if (!response) throw new Error(staticMessages.service.errors[500]);

View File

@ -40,6 +40,7 @@ export const apiUrls = {
createUserAccount: `${baseApiUrl}${ENVs.apiCreateUserAccount}`, createUserAccount: `${baseApiUrl}${ENVs.apiCreateUserAccount}`,
createUserProfile: `${baseApiUrl}${ENVs.apiCreateUserProfile}`, createUserProfile: `${baseApiUrl}${ENVs.apiCreateUserProfile}`,
createMember: `${baseApiUrl}${ENVs.apiCreateMember}`, createMember: `${baseApiUrl}${ENVs.apiCreateMember}`,
createPlace: `${baseApiUrl}${ENVs.apiCreatePlace}`,
}, },
generic: { generic: {
authPhonenumber: `${ENVs.apiAuthOrigin}${ENVs.apiAuthPhonenumber}`, authPhonenumber: `${ENVs.apiAuthOrigin}${ENVs.apiAuthPhonenumber}`,

View File

@ -10,4 +10,5 @@ export const ENVs = {
apiQr: process.env.VITE_API_QR, apiQr: process.env.VITE_API_QR,
apiCreateUserProfile: process.env.VITE_API_USERS_PROFILE, apiCreateUserProfile: process.env.VITE_API_USERS_PROFILE,
apiCreateMember: process.env.VITE_API_CREATE_MEMBER, apiCreateMember: process.env.VITE_API_CREATE_MEMBER,
apiCreatePlace: process.env.VITE_API_PLACES,
}; };

View File

@ -4,6 +4,7 @@ export const staticMessages = {
input: 'please fill all inputs correctly', input: 'please fill all inputs correctly',
phonenumber: 'please fill the valid number', phonenumber: 'please fill the valid number',
otp: 'please fill the otp fields correctly', otp: 'please fill the otp fields correctly',
inputPlace: 'place type and name of place is necessary',
}, },
users: 'Users', users: 'Users',
submit: 'Submit', submit: 'Submit',
@ -26,6 +27,7 @@ export const staticMessages = {
success: { success: {
createUser: 'user created successfully', createUser: 'user created successfully',
createMember: 'member created successfully', createMember: 'member created successfully',
createPlace: 'place created successfully',
}, },
and: 'and', and: 'and',
canUseFor: 'can use for', canUseFor: 'can use for',
@ -47,7 +49,7 @@ export const staticMessages = {
}, },
service: { service: {
errors: { 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!', 401: 'Authentication error!',
}, },
}, },

View File

@ -1,15 +1,23 @@
import React from 'react'; 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 CreatePlaceView from '../view/CreatePlaceView';
import useCreatePlaceVm from '../viewmodel/createPlaceVM'; import useCreatePlaceVm from '../viewmodel/createPlaceVM';
import createPlaceModel from '../model/createPlaceModel'; import createPlaceModel from '../model/createPlaceModel';
export default function CreatePlace() { export default function CreatePlace() {
const { inputOptions } = createPlaceModel(); const { accessTokenUpdateHandler, notLoginAuth, userData } = useGetNavigatorAndTokenUpdater();
const { formStateData, inputStateHandlers, selectBoxOptions } = useCreatePlaceVm({ const { user } = userData;
const createPlaceDrivenAdapter = createPlaceAdapter(user, accessTokenUpdateHandler, notLoginAuth);
const { inputOptions, createPlace } = createPlaceModel(createPlaceDrivenAdapter);
const { formStateData, inputStateHandlers, selectBoxOptions, handleSubmitCreatePlaceForm } = useCreatePlaceVm({
getInputOptions: inputOptions, getInputOptions: inputOptions,
createPlaceModel: createPlace,
}); });
return ( return (
<CreatePlaceView <CreatePlaceView
handleSubmitCreatePlaceForm={handleSubmitCreatePlaceForm}
inputOptions={{ inputOptions={{
parentId: selectBoxOptions.parentIdsOptions, parentId: selectBoxOptions.parentIdsOptions,
placeType: selectBoxOptions.placeTypesOptions, placeType: selectBoxOptions.placeTypesOptions,

View File

@ -1,9 +1,15 @@
import { CreatePlaceType } from './protocols'; import { CreatePlaceType } from './protocols';
import getValidOptionsOfSelectBox from './getValidOptionsOfSelectBox'; import getValidOptionsOfSelectBox from './getValidOptionsOfSelectBox';
import createPlaceLogicMaker, { ICreatePlaceLogicMaker } from './submiCreatePlace';
const createPlaceModel = (): CreatePlaceType => { type ICreatePlaceModel = ICreatePlaceLogicMaker;
const createPlaceModel = (depenedencies: ICreatePlaceModel): CreatePlaceType => {
const { getPlacesTypeOptionsOfSelectBox, getValidParentIdsOptionsOfSelectBox } = getValidOptionsOfSelectBox; const { getPlacesTypeOptionsOfSelectBox, getValidParentIdsOptionsOfSelectBox } = getValidOptionsOfSelectBox;
const { createPlace } = createPlaceLogicMaker(depenedencies);
return { return {
createPlace,
inputOptions: { inputOptions: {
getPlaceType: getPlacesTypeOptionsOfSelectBox, getPlaceType: getPlacesTypeOptionsOfSelectBox,
getParentIds: getValidParentIdsOptionsOfSelectBox, getParentIds: getValidParentIdsOptionsOfSelectBox,

View File

@ -0,0 +1,3 @@
import createPlaceModel from './createPlaceModel';
export default createPlaceModel;

View File

@ -1,7 +1,9 @@
import PlacesModel from '~/business-logic/core/places/common/model/placesModel'; import PlacesModel from '~/business-logic/core/places/common/model/placesModel';
import { placeTypes } from '../view/protocol'; import { placeTypes } from '../view/protocol';
import { createPlaceArguments } from './submiCreatePlace';
export type CreatePlaceType = { export type CreatePlaceType = {
createPlace: (placeData: createPlaceArguments) => Promise<string>;
inputOptions: { inputOptions: {
getPlaceType: () => { getPlaceType: () => {
value: placeTypes; value: placeTypes;

View File

@ -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<string>;
}
const createPlaceLogicMaker = (depenedencies: ICreatePlaceLogicMaker) => {
const { httpHandler } = depenedencies;
const createPlace = async (placeData: createPlaceArguments) => {
return await httpHandler(placeData);
};
return {
createPlace,
};
};
export default createPlaceLogicMaker;

View File

@ -3,49 +3,59 @@ import SimpleInput from '~/driven/utils/components/inputs/simple-input/SimpleInp
import InputBox from '~/driven/utils/components/inputs/select-box/InputBox'; import InputBox from '~/driven/utils/components/inputs/select-box/InputBox';
import { staticMessages } from '~/driven/utils/constants/staticMessages'; import { staticMessages } from '~/driven/utils/constants/staticMessages';
import PrimaryButton from '~/driven/utils/components/buttons/primary-button/PrimaryButton'; import PrimaryButton from '~/driven/utils/components/buttons/primary-button/PrimaryButton';
import Notification from '~/driven/utils/components/Notification/Notification';
import { ICreatePlaceProps } from './protocol'; import { ICreatePlaceProps } from './protocol';
export default function CreatePlaceView(props: ICreatePlaceProps) { export default function CreatePlaceView(props: ICreatePlaceProps) {
const { formStateData, inputStateHandlers, inputOptions } = props; const { formStateData, inputStateHandlers, inputOptions, handleSubmitCreatePlaceForm } = props;
const { formState } = formStateData; const { formState, error, setError } = formStateData;
return ( return (
<form className='p-4 py-6 flex flex-wrap w-full gap-4'> <>
<SimpleInput {Boolean(error.message) && (
inputData={{ <Notification
name: staticMessages.global.PlaceName, message={error.message}
title: staticMessages.global.PlaceName, type={error.type}
}} onCloseCallback={() => setError({ message: '', type: 'error' })}
stateHanlder={{ />
state: formState.placeName, )}
setState: inputStateHandlers.name, <form onSubmit={handleSubmitCreatePlaceForm} className='p-4 py-6 flex flex-wrap w-full gap-4'>
}} <SimpleInput
className='mb-4 w-[48%] px-2' inputData={{
/> name: staticMessages.global.PlaceName,
<InputBox title: staticMessages.global.PlaceName,
name={staticMessages.global.placeType} }}
state={{ stateHanlder={{
value: { value: formState.placeTypes, label: staticMessages.placeTypes[formState.placeTypes] }, state: formState.placeName,
setValue: inputStateHandlers.placeType, setState: inputStateHandlers.name,
options: inputOptions.placeType, }}
}} className='mb-4 w-[48%] px-2'
title={staticMessages.global.placeType} />
className='mb-4 w-[48%] px-2 border-1 border-txt-medium text-txt-medium' <InputBox
/> name={staticMessages.global.placeType}
<InputBox state={{
name={staticMessages.global.placeParentId} value: { value: formState.placeTypes, label: staticMessages.placeTypes[formState.placeTypes] },
state={{ setValue: inputStateHandlers.placeType,
value: { value: formState.parentId.value, label: formState.parentId.label }, options: inputOptions.placeType,
setValue: inputStateHandlers.parentId, }}
options: inputOptions.parentId, title={staticMessages.global.placeType}
isLoading: inputOptions.isParentIdLoading, className='mb-4 w-[48%] px-2 border-1 border-txt-medium text-txt-medium'
}} />
title={staticMessages.global.placeParentId} <InputBox
className='mb-4 w-[48%] px-2 border-1 border-txt-medium text-txt-medium' name={staticMessages.global.placeParentId}
/> state={{
<div className='w-full'> value: { value: formState.parentId.value, label: formState.parentId.label },
<PrimaryButton onClick={() => null} title={staticMessages.global.submit} /> setValue: inputStateHandlers.parentId,
</div> options: inputOptions.parentId,
</form> isLoading: inputOptions.isParentIdLoading,
}}
title={staticMessages.global.placeParentId}
className='mb-4 w-[48%] px-2 border-1 border-txt-medium text-txt-medium'
/>
<div className='w-full'>
<PrimaryButton onClick={() => null} title={staticMessages.global.submit} />
</div>
</form>
</>
); );
} }

View File

@ -19,8 +19,19 @@ export type PlaceFormState = {
}; };
export interface ICreatePlaceProps { export interface ICreatePlaceProps {
handleSubmitCreatePlaceForm: (e: React.FormEvent) => Promise<void>;
formStateData: { formStateData: {
formState: PlaceFormState; formState: PlaceFormState;
error: {
message: string;
type: 'error' | 'success';
};
setError: React.Dispatch<
React.SetStateAction<{
message: string;
type: 'error' | 'success';
}>
>;
setFormState: React.Dispatch<React.SetStateAction<PlaceFormState>>; setFormState: React.Dispatch<React.SetStateAction<PlaceFormState>>;
}; };
inputStateHandlers: { inputStateHandlers: {

View File

@ -1,6 +1,9 @@
import { useCallback, useEffect, useMemo, useState } from 'react'; 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 useGetPlaceList from '../../common/hooks/useGetPlaceList';
import { PlaceFormState, placeTypes } from '../view/protocol'; import { PlaceFormState, placeTypes } from '../view/protocol';
import { createPlaceArguments } from '../model/submiCreatePlace';
import { ICreatePlaceVm, initialPlaceFormState } from './protocols'; import { ICreatePlaceVm, initialPlaceFormState } from './protocols';
const onChangeInputStateHandlers = (setFormState: React.Dispatch<React.SetStateAction<PlaceFormState>>) => { const onChangeInputStateHandlers = (setFormState: React.Dispatch<React.SetStateAction<PlaceFormState>>) => {
@ -25,15 +28,41 @@ const resetParentIdOnChangePlaceType = (setFormState: React.Dispatch<React.SetSt
}; };
const useCreatePlaceVm = (dependencies: ICreatePlaceVm) => { const useCreatePlaceVm = (dependencies: ICreatePlaceVm) => {
const { getInputOptions } = dependencies; const { getInputOptions, createPlaceModel } = dependencies;
const { getParentIds, getPlaceType } = getInputOptions; const { getParentIds, getPlaceType } = getInputOptions;
const [formState, setFormState] = useState<PlaceFormState>(initialPlaceFormState); const [formState, setFormState] = useState<PlaceFormState>(initialPlaceFormState);
const [error, setError] = useState<{ message: string; type: 'error' | 'success' }>({ message: '', type: 'error' });
const { handleNameSetState, handleParentIdSetState, handlePlaceTypeSetState } = useMemo( const { handleNameSetState, handleParentIdSetState, handlePlaceTypeSetState } = useMemo(
() => onChangeInputStateHandlers(setFormState), () => onChangeInputStateHandlers(setFormState),
[], [],
); );
const handleSubmitCreatePlaceForm = async (e: React.FormEvent) => {
e.preventDefault();
try {
const { parentId, placeName, placeTypes: placeType } = formState;
console.log(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]); useEffect(() => resetParentIdOnChangePlaceType(setFormState), [formState.placeTypes]);
const getPlaceList = useCallback(() => useGetPlaceList()(), []); const getPlaceList = useCallback(() => useGetPlaceList()(), []);
@ -46,8 +75,11 @@ const useCreatePlaceVm = (dependencies: ICreatePlaceVm) => {
}, [formState.placeTypes, places]); }, [formState.placeTypes, places]);
return { return {
handleSubmitCreatePlaceForm,
formStateData: { formStateData: {
formState, formState,
error,
setError,
setFormState, setFormState,
}, },
inputStateHandlers: { inputStateHandlers: {

View File

@ -1,4 +1,5 @@
import { CreatePlaceType } from '../model/protocols'; import { CreatePlaceType } from '../model/protocols';
import { createPlaceArguments } from '../model/submiCreatePlace';
import { PlaceFormState, placeTypes } from '../view/protocol'; import { PlaceFormState, placeTypes } from '../view/protocol';
export const initialPlaceFormState: PlaceFormState = { export const initialPlaceFormState: PlaceFormState = {
@ -9,4 +10,5 @@ export const initialPlaceFormState: PlaceFormState = {
export interface ICreatePlaceVm { export interface ICreatePlaceVm {
getInputOptions: CreatePlaceType['inputOptions']; getInputOptions: CreatePlaceType['inputOptions'];
createPlaceModel: (placeData: createPlaceArguments) => Promise<string>;
} }