Compare commits

...

6 Commits

50 changed files with 553 additions and 109 deletions

View File

@ -1,3 +1,5 @@
VITE_API_ORIGIN = http://176.53.196.42:6001/api/v1
VITE_API_PLACES = /place
VITE_API_USERS = /profile
VITE_API_USERS = /profile
VITE_API_USERS_ACCOUNT = /account
VITE_API_USERS_PROFILE = /profile

View File

@ -66,11 +66,13 @@
],
"prettier/prettier": [
"error",
{
{
"usePrettierrc": true,
"singleQuote": true
},
{}
"jsxSingleQuote": true,
"singleQuote": true,
"endOfLine": "auto"
}
],
"jest/valid-title": "off",
"react/button-has-type": "off",

View File

@ -1,7 +1,7 @@
type Places = {
placeType: string;
name: string;
qr: null;
qr: null | string;
id: string;
parentId: string | null;
};

View File

@ -16,6 +16,11 @@ class UsersModel {
getTitle(): string {
return this.modelTitle;
}
addUser(user: Users): Users[] {
this.usersList.push(user);
return this.usersList;
}
}
export default UsersModel;

View File

@ -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;

View File

@ -0,0 +1,12 @@
export interface INewUserData {
firstname: string;
lastname: string;
phonenumber: string;
}
export type CreateAccountDTOReturnType = {
username: string;
firstName: string;
lastName: string;
enabled: true;
};

View File

@ -0,0 +1,8 @@
import { INewUserData } from '../dto/protocols';
import { CreateAccountROReturnType } from '../response-object/protocols';
interface ICreateAcountRepo {
execute: (newUser: INewUserData) => Promise<CreateAccountROReturnType>;
}
export default ICreateAcountRepo;

View File

@ -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<CreateAccountROReturnType> = 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;
};
}

View File

@ -0,0 +1,4 @@
import { INewUserData } from '../dto/protocols';
import { CreateAcountResponseApi } from '../response-object/protocols';
export type HttpHandler = (newUser: INewUserData) => Promise<CreateAcountResponseApi>;

View File

@ -0,0 +1,8 @@
import { CreateAccountROReturnType, CreateAcountResponseApi } from './protocols';
const createAcountRO = (apiResponse: CreateAcountResponseApi): CreateAccountROReturnType => ({
accountId: apiResponse.id,
phonenumber: apiResponse.username,
});
export default createAcountRO;

View File

@ -0,0 +1,10 @@
export type CreateAcountResponseApi = {
nextRequestTimestamp: number;
username: string;
id: string;
};
export type CreateAccountROReturnType = {
phonenumber: string;
accountId: string;
};

View File

@ -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;

View File

@ -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;

View File

@ -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<CreateProfileDtoReturnType, string>
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);
}
}

View File

@ -0,0 +1,5 @@
import { ICreateNewProfileData } from '../dto/protocols';
export default interface ICreateProfileRepo {
execute: (accountData: ICreateNewProfileData) => Promise<string>;
}

View File

@ -0,0 +1,3 @@
import { CreateProfileDtoReturnType } from '../dto/protocols';
export type HttpHandler = (newUser: CreateProfileDtoReturnType) => Promise<string>;

View File

@ -0,0 +1,3 @@
import CreateUserInfra from './infra/createUserInfra';
export default CreateUserInfra;

View File

@ -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;

View File

@ -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;

View File

@ -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;
}
}

View File

@ -0,0 +1,30 @@
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 { HTTPPovider } from '~/driven/boundaries/http-boundary/httpBoundary';
import { HttpOptionsType } from '~/driven/boundaries/http-boundary/protocols';
import { apiUrls } from '~/driven/utils/configs/appConfig';
const createAccountAdapter = (): createUserPort['httpAccountHandler'] => {
// make url
const url = apiUrls.core.createUserAccount;
// call http provider
const httpProvider = new HTTPPovider();
const httpHandler = (newUserData: INewUserData) => {
// api options
const httpOptions: HttpOptionsType = {
url,
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: newUserData,
};
return httpProvider.request<CreateAcountResponseApi>(httpOptions);
};
return httpHandler;
};
export default createAccountAdapter;

View File

@ -0,0 +1,29 @@
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 { HTTPPovider } from '~/driven/boundaries/http-boundary/httpBoundary';
import { HttpOptionsType } from '~/driven/boundaries/http-boundary/protocols';
import { apiUrls } from '~/driven/utils/configs/appConfig';
const createProfileAdapter = (): createUserPort['httpProfileHandler'] => {
// make url
const url = apiUrls.core.createUserProfile;
// call http provider
const httpProvider = new HTTPPovider();
const httpHandler = (newAccountData: CreateProfileDtoReturnType) => {
// api options
const httpOptions: HttpOptionsType = {
url,
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: newAccountData,
};
return httpProvider.request<string>(httpOptions);
};
return httpHandler;
};
export default createProfileAdapter;

View File

@ -16,8 +16,7 @@ const getPlacesAdapter = (): IGetPlacesPort & getPlacesAdapterReturnType => {
// make the httpHandler
const httpProvider = new HTTPPovider();
const httpHandler = async () =>
httpProvider.request<GetPlacesResponse>(options);
const httpHandler = async () => httpProvider.request<GetPlacesResponse>(options);
// return the method
return {

View File

@ -1,6 +1,6 @@
export default abstract class StateManagementProvider {
abstract useGetQuery<DataType>(
key: string,
httpHandler: () => Promise<DataType>
httpHandler: () => Promise<DataType>,
): { data: DataType | undefined; isLoading: boolean; error?: string };
}

View File

@ -15,7 +15,7 @@ export default class StateManagementService implements StateManagementProvider {
useGetQuery<DataType>(
key: string,
httpHandler: () => Promise<DataType>
httpHandler: () => Promise<DataType>,
): {
data: DataType | undefined;
isLoading: boolean;

View File

@ -4,7 +4,7 @@ import StateManagementProvider from './stateManagementProvider';
export default class SwrBoundary implements StateManagementProvider {
useGetQuery<DataType>(
key: string,
httpHandler: () => Promise<DataType>
httpHandler: () => Promise<DataType>,
): {
data: DataType | undefined;
isLoading: boolean;

View File

@ -1,18 +1,41 @@
import React from 'react';
interface ISimpleInput {
title: string;
export type SetStateInputMethod<NameType> = (name: NameType, newValue: string) => void;
interface ISimpleInput<NameType> {
inputData: {
title: string;
name: string;
};
className?: string;
stateHanlder: {
state: string;
setState: SetStateInputMethod<NameType>;
};
}
export default function SimpleInput(props: ISimpleInput) {
const { title, className } = props;
export default function SimpleInput<NameType>(props: ISimpleInput<NameType>) {
const { className, inputData, stateHanlder } = props;
const { name, title } = inputData;
const { setState, state } = stateHanlder;
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { value, name: inputName } = e.target;
setState(inputName as NameType, value);
};
return (
<div className={`flex flex-col ${className}`}>
<label className='mb-1 text-txt-second text-xs' htmlFor={title}>
{title}
</label>
<input className='bg-bg-gray h-11 rounded-lg focus:outline-0 px-2 text-txt-medium' id={title} />
<input
value={state}
onChange={handleInputChange}
name={name}
className='bg-bg-gray h-11 rounded-lg focus:outline-0 px-2 text-txt-medium'
id={title}
/>
</div>
);
}

View File

@ -7,5 +7,11 @@ interface IPageTitleProps {
export default function PageTitle(props: IPageTitleProps) {
const { title, className } = props;
return <div className={`w-full shadow-sm shadow-txt-light font-semibold ${className}`}>{title}</div>;
return (
<div
className={`w-full shadow-sm shadow-txt-light font-semibold ${className}`}
>
{title}
</div>
);
}

View File

@ -27,5 +27,7 @@ export const apiUrls = {
core: {
getPlaces: `${baseApiUrl}${ENVs.apiGetPlaces}`,
getUsers: `${baseApiUrl}${ENVs.apiGetUsers}`,
createUserAccount: `${baseApiUrl}${ENVs.apiCreateUserAccount}`,
createUserProfile: `${baseApiUrl}${ENVs.apiCreateUserProfile}`,
},
};

View File

@ -2,4 +2,6 @@ export const ENVs = {
apiOrigin: process.env.VITE_API_ORIGIN,
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,
};

View File

@ -5,15 +5,16 @@ export const staticMessages = {
},
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',
},
service: {
errors: {

View File

@ -1,8 +1,5 @@
import StateManagementService from '~/driven/boundaries/state-management';
import {
errorHandlingStateTypes,
UIErrorHandling,
} from './protocols/globalHelpersProtocols';
import { errorHandlingStateTypes, UIErrorHandling } from './protocols/globalHelpersProtocols';
export const UIErrorHandlingFactory = <DATA_RESPONSE>({
state,
@ -18,13 +15,14 @@ export const UIErrorHandlingFactory = <DATA_RESPONSE>({
state,
});
export const prepareStateManagementForVM = <ReturnType>(
apiUrl: string,
model: () => Promise<ReturnType>
) => {
export const prepareStateManagementForVM = <ReturnType>(apiUrl: string, model: () => Promise<ReturnType>) => {
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;
};

View File

@ -0,0 +1,9 @@
type HttpHandler<NewDataToAdd, ResponseType> = (newUser: NewDataToAdd) => Promise<ResponseType>;
export default class RepositoryHandler<NewDataToAdd, ResponseType> {
protected httpHandler: HttpHandler<NewDataToAdd, ResponseType>;
constructor(httpHandler: HttpHandler<NewDataToAdd, ResponseType>) {
this.httpHandler = httpHandler;
}
}

View File

@ -9,5 +9,11 @@ export default function TableRow(props: ITableRowInfra) {
const { isRowSelected } = useTableRowVM({ selectedRowId, rowId });
return <TableRowView isSelected={isRowSelected} rowData={rowData} setSelectedRowId={setSelectedRowId} />;
return (
<TableRowView
isSelected={isRowSelected}
rowData={rowData}
setSelectedRowId={setSelectedRowId}
/>
);
}

View File

@ -1,7 +1,7 @@
export interface ITableRowInfra {
selectedRowId: string;
rowData: {
rowItemsTitle: string[];
rowItemsTitle: (string | null)[];
rowId: string;
};
setSelectedRowId: React.Dispatch<React.SetStateAction<string>>;

View File

@ -6,7 +6,14 @@ export default function TableRowView(props: ITableRowProps) {
const { isSelected, setSelectedRowId, rowData } = props;
const { rowId, rowItemsTitle } = rowData;
const columns = rowItemsTitle.map((rowItemTitle, index) => {
return <RowItem key={rowItemTitle} hasCheckbox={index === 0} isSelected={isSelected} title={rowItemTitle} />;
return (
<RowItem
key={rowItemTitle}
hasCheckbox={index === 0}
isSelected={isSelected}
title={rowItemTitle}
/>
);
});
return <tr onClick={() => setSelectedRowId(rowId)}>{columns}</tr>;

View File

@ -1,7 +1,7 @@
export interface ITableRowProps {
isSelected: boolean;
rowData: {
rowItemsTitle: string[];
rowItemsTitle: (string | null)[];
rowId: string;
};
setSelectedRowId: React.Dispatch<React.SetStateAction<string>>;

View File

@ -1,7 +1,7 @@
import React from 'react';
interface IRowItemProp {
title: string;
title: string | null;
hasCheckbox: boolean;
isSelected: boolean;
}
@ -17,7 +17,11 @@ export default function RowItem(props: IRowItemProp) {
isSelected ? 'opacity-100' : 'opacity-0'
}`}
>
<span className={`${isSelected ? 'visible' : 'hidden'} transition-all`}>&#10003;</span>
<span
className={`${isSelected ? 'visible' : 'hidden'} transition-all`}
>
&#10003;
</span>
</span>
)}
{title}

View File

@ -1,6 +1,22 @@
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 CreateUserView from '../view/CreateUserView';
import useCreateUserVM from '../viewmodel/CreateUserVM';
import createUserModel from '../model/createUserModel';
export default function CreateUser() {
return <CreateUserView />;
// get adapters from driven layer
const createAccountDrivenAdapter = createAccountAdapter();
const createProfileDrivenAdapter = createProfileAdapter();
// 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 } = useCreateUserVM({ handleSubmitForm });
// get all of the needed information to the user to show
return <CreateUserView stateHandler={stateHandler} inputNames={inputNames} onSubmit={onSubmit} />;
}

View File

@ -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;

View File

@ -2,18 +2,35 @@ 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 ICreateUserViewProps from './protocols';
export default function CreateUserView() {
export default function CreateUserView(props: ICreateUserViewProps) {
const { onSubmit, inputNames, stateHandler } = props;
const { inputStates, inputsSetStates } = stateHandler;
const inputs = inputNames.map((inputName) => {
const title = staticMessages.global[inputName] as string;
return (
<SimpleInput
inputData={{
title,
name: inputName,
}}
stateHanlder={{
setState: inputsSetStates,
state: inputStates[inputName],
}}
key={inputName}
className='mb-4 w-[48%]'
/>
);
});
return (
<div className='px-4 my-8'>
<div className='flex flex-wrap w-full gap-4'>
<SimpleInput title={staticMessages.global.fistname} className='mb-4 w-[48%]' />
<SimpleInput title={staticMessages.global.lastname} className='mb-4 w-[48%]' />
<SimpleInput title={staticMessages.global.phoneNumber} className='mb-4 w-[48%]' />
</div>
<form onSubmit={onSubmit} className='px-4 my-8'>
<div className='flex flex-wrap w-full gap-4'>{inputs}</div>
<div className='flex'>
<PrimaryButton onClick={() => null} title={staticMessages.global.submit} />
</div>
</div>
</form>
);
}

View File

@ -0,0 +1,11 @@
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<keyof INewUserData>;
};
inputNames: (keyof INewUserData)[];
}

View File

@ -0,0 +1,45 @@
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 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 { handleSubmitForm } = dependencies;
const [inputsValue, setInputValues] = useState<INewUserData>(inputStateInitialValue);
const inputsSetStates = (name: keyof INewUserData, newValue: string) => {
if (name === 'phonenumber' && !checkPhoneNumberInput(newValue)) return;
setInputValues((prev) => ({
...prev,
[name]: newValue,
}));
};
const onSubmitCreateUserForm = (e: React.FormEvent) => {
e.preventDefault();
console.log('submit user', inputsValue);
handleSubmitForm(inputsValue);
};
const inputStates: INewUserData = { ...inputsValue };
return {
stateHandler: {
inputsSetStates,
inputStates,
},
onSubmit: onSubmitCreateUserForm,
inputNames,
};
};
export default useCreateUserVM;

View File

@ -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;
}

View File

@ -1,39 +1,19 @@
import React from 'react';
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, placesList } = props;
console.log(placesList.data);
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',
},
];
return placesdata.map((places) => {
const rows = useMemo(() => {
if (!placesList.data) return null;
return placesList.data.getData().map((places) => {
const rowData = {
rowItemsTitle: [
places.id,
places.title,
places.status,
places.address,
'',
],
rowItemsTitle: [places.name, places.placeType, places.qr],
rowId: places.id,
};
return (
@ -45,27 +25,36 @@ export default function UsersListView(props: IPlacesListProps) {
/>
);
});
};
}, [placesList]);
if (placesList.isLoading)
return (
<div className="flex justify-center items-center">
<div className='flex justify-center items-center'>
<Loading />
</div>
);
const tableTitles: Pick<Places, 'name' | 'placeType' | 'qr'> = {
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 (
<th key={key} className='py-3'>
{title}
</th>
);
});
return (
<table className="table-auto rounded-md w-full text-sm">
<thead className="text-txt-medium font-bold">
<tr>
<th className="py-3">{staticMessages.global.place_id}</th>
<th className="py-3">{staticMessages.global.title}</th>
<th className="py-3">{staticMessages.global.status}</th>
<th className="py-3">{staticMessages.global.address}</th>
<th className="py-3">{staticMessages.global.qrCode}</th>
</tr>
<table className='table-auto rounded-md w-full text-sm h-fit'>
<thead className='text-txt-medium font-bold'>
<tr>{titles}</tr>
</thead>
<tbody>{rows()}</tbody>
<tbody>{rows}</tbody>
</table>
);
}

View File

@ -1,29 +1,20 @@
import React from 'react';
/* 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, usersList } = props;
console.log(usersList.data);
const rows = () => {
const userdata = [
{
id: '1',
firstname: 'behnam',
lastname: 'rahimpour',
},
{
id: '2',
firstname: 'Salar',
lastname: 'Sali',
},
];
const rows = useMemo(() => {
if (!usersList.data) return null;
return userdata.map((user) => {
return usersList.data.getData().map((user) => {
const rowData = {
rowItemsTitle: [user.firstname, user.lastname],
rowId: user.id,
rowId: user.accountId,
};
return (
<TableRow
@ -34,17 +25,36 @@ export default function UsersListView(props: IUserListProps) {
/>
);
});
}, [usersList]);
const tableTitles: Pick<Users, 'firstname' | 'lastname'> = {
firstname: staticMessages.global.firstname,
lastname: staticMessages.global.lastname,
};
const titles = Object.keys(tableTitles).map((titleKey) => {
const key = titleKey as keyof typeof tableTitles;
const title = tableTitles[key];
return (
<th key={key} className='py-3'>
{title}
</th>
);
});
if (usersList.isLoading)
return (
<div className='flex justify-center items-center'>
<Loading />
</div>
);
return (
<table className="table-auto rounded-md w-full text-sm">
<thead className="text-txt-medium font-bold">
<tr>
<th className="py-3">{staticMessages.global.fistname}</th>
<th className="py-3">{staticMessages.global.lastname}</th>
</tr>
<table className='table-auto rounded-md w-full text-sm h-fit'>
<thead className='text-txt-medium font-bold'>
<tr>{titles}</tr>
</thead>
<tbody>{rows()}</tbody>
<tbody>{rows}</tbody>
</table>
);
}

View File

@ -13,7 +13,9 @@ export default function Sidebar() {
key={key}
to={routesData[key].path}
className={`flex text-white mb-6 text-sm w-full py-2 pl-2 rounded-lg ${
isCurrentPage.pathname === routesData[key].path ? 'bg-primary-300' : ''
isCurrentPage.pathname === routesData[key].path
? 'bg-primary-300'
: ''
}`}
>
<img src={routesData[key].icon} alt='page icon' className='mr-2' />

View File

@ -6,7 +6,10 @@ import CreateUser from '~/driving/application/core/create-user';
export default function CreateUserPage() {
return (
<>
<PageTitle className='px-4 py-5' title={staticMessages.global.createUser} />
<PageTitle
className='px-4 py-5'
title={staticMessages.global.createUser}
/>
<CreateUser />
</>
);

View File

@ -11,7 +11,11 @@ export default function index() {
<PageTitle className='px-4 py-5' title={staticMessages.global.users} />
<div className='container mx-auto px-4'>
<div className='w-full flex flex-row-reverse items-center py-2'>
<PrimaryButton className='text-sm' title={staticMessages.global.submit} onClick={() => null} />
<PrimaryButton
className='text-sm'
title={staticMessages.global.submit}
onClick={() => null}
/>
</div>
<div className='md:grid-cols-2 gap-x-4 grid grid-cols-1 mx-auto'>
<UsersList />

View File

@ -4,7 +4,7 @@ import Sidebar from '~/driving/application/support/sidebar';
export default function MainPageLayout() {
return (
<div className='flex flex-nowrap h-screen'>
<div className='flex flex-nowrap min-h-screen'>
<Sidebar />
<main className='dipal-panel w-full text-black bg-white h-fit'>
<Outlet />