Commit 70bea3dd authored by tom's avatar tom

crud operations

parent daf122d9
......@@ -4,13 +4,14 @@ import {
FormControl,
FormLabel,
Input,
Textarea,
} from '@chakra-ui/react';
// import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import React, { useCallback, useEffect } from 'react';
import type { ControllerRenderProps } from 'react-hook-form';
import type { ControllerRenderProps, SubmitHandler } from 'react-hook-form';
import { useForm, Controller } from 'react-hook-form';
import type { CustomAbi } from 'types/api/account';
import type { CustomAbi, CustomAbis } from 'types/api/account';
type Props = {
data?: CustomAbi;
......@@ -20,59 +21,61 @@ type Props = {
type Inputs = {
contract_address_hash: string;
name: string;
abi: string;
}
// idk, maybe there is no limit
const NAME_MAX_LENGTH = 100;
const CustomAbiForm: React.FC<Props> = ({ data }) => {
const { control, formState: { errors }, setValue } = useForm<Inputs>();
// const queryClient = useQueryClient();
const CustomAbiForm: React.FC<Props> = ({ data, onClose }) => {
const { control, formState: { errors }, setValue, handleSubmit } = useForm<Inputs>();
const queryClient = useQueryClient();
useEffect(() => {
setValue('contract_address_hash', data?.contract_address_hash || '');
setValue('name', data?.name || '');
setValue('abi', JSON.stringify(data?.abi) || '');
}, [ setValue, data ]);
// const updateApiKey = (data: Inputs) => {
// const body = JSON.stringify({ name: data.name });
const customAbiKey = (data: Inputs & { id?: number }) => {
const body = JSON.stringify({ name: data.name, contract_address_hash: data.contract_address_hash, abi: data.abi });
// if (!data.token) {
// return fetch('/api/account/api-keys', { method: 'POST', body });
// }
if (!data.id) {
return fetch('/api/account/custom-abis', { method: 'POST', body });
}
// return fetch(`/api/account/api-keys/${ data.token }`, { method: 'PUT', body });
// };
return fetch(`/api/account/custom-abis/${ data.id }`, { method: 'PUT', body });
};
// const mutation = useMutation(updateApiKey, {
// onSuccess: async(data) => {
// const response: CustomAbi = await data.json();
const mutation = useMutation(customAbiKey, {
onSuccess: async(data) => {
const response: CustomAbi = await data.json();
// queryClient.setQueryData([ 'api-keys' ], (prevData: CustomAbis | undefined) => {
// const isExisting = prevData && prevData.some((item) => item.api_key === response.api_key);
queryClient.setQueryData([ 'custom-abis' ], (prevData: CustomAbis | undefined) => {
const isExisting = prevData && prevData.some((item) => item.id === response.id);
// if (isExisting) {
// return prevData.map((item) => {
// if (item.api_key === response.api_key) {
// return response;
// }
if (isExisting) {
return prevData.map((item) => {
if (item.id === response.id) {
return response;
}
// return item;
// });
// }
return item;
});
}
// return [ ...(prevData || []), response ];
// });
return [ ...(prevData || []), response ];
});
// onClose();
// },
// // eslint-disable-next-line no-console
// onError: console.error,
// });
onClose();
},
// eslint-disable-next-line no-console
onError: console.error,
});
// const onSubmit: SubmitHandler<Inputs> = useCallback((data) => {
// mutation.mutate(data);
// }, [ mutation ]);
const onSubmit: SubmitHandler<Inputs> = useCallback((formData) => {
mutation.mutate({ ...formData, id: data?.id });
}, [ mutation, data ]);
const renderContractAddressInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'contract_address_hash'>}) => {
return (
......@@ -99,16 +102,29 @@ const CustomAbiForm: React.FC<Props> = ({ data }) => {
);
}, [ errors ]);
const renderAbiInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'abi'>}) => {
return (
<FormControl variant="floating" id="abi" isRequired>
<Textarea
{ ...field }
size="lg"
isInvalid={ Boolean(errors.abi) }
/>
<FormLabel>{ `Custom ABI [{...}] (JSON format)` }</FormLabel>
</FormControl>
);
}, [ errors ]);
return (
<>
<Box marginBottom={ 5 }>
<Box>
<Controller
name="contract_address_hash"
control={ control }
render={ renderContractAddressInput }
/>
</Box>
<Box marginBottom={ 8 }>
<Box marginTop={ 5 }>
<Controller
name="name"
control={ control }
......@@ -118,13 +134,20 @@ const CustomAbiForm: React.FC<Props> = ({ data }) => {
render={ renderNameInput }
/>
</Box>
<Box marginTop={ 5 }>
<Controller
name="abi"
control={ control }
render={ renderAbiInput }
/>
</Box>
<Box marginTop={ 8 }>
<Button
size="lg"
variant="primary"
// onClick={ handleSubmit(onSubmit) }
onClick={ handleSubmit(onSubmit) }
disabled={ Object.keys(errors).length > 0 }
// isLoading={ mutation.isLoading }
isLoading={ mutation.isLoading }
>
{ data ? 'Save' : 'Create custom ABI' }
</Button>
......
import {
Table,
Thead,
Tbody,
Tr,
Th,
TableContainer,
} from '@chakra-ui/react';
import React from 'react';
import type { CustomAbis, CustomAbi } from 'types/api/account';
import CustomAbiTableItem from './CustomAbiTableItem';
interface Props {
data: CustomAbis;
onEditClick: (item: CustomAbi) => void;
onDeleteClick: (item: CustomAbi) => void;
}
const CustomAbiTable = ({ data, onDeleteClick, onEditClick }: Props) => {
return (
<TableContainer width="100%">
<Table variant="simple" minWidth="600px">
<Thead>
<Tr>
<Th>ABI for Smart contract address (0x...)</Th>
<Th width="108px"></Th>
</Tr>
</Thead>
<Tbody>
{ data.map((item) => (
<CustomAbiTableItem
item={ item }
key={ item.id }
onDeleteClick={ onDeleteClick }
onEditClick={ onEditClick }
/>
)) }
</Tbody>
</Table>
</TableContainer>
);
};
export default React.memo(CustomAbiTable);
import {
Tr,
Td,
HStack,
Text,
} from '@chakra-ui/react';
import React, { useCallback } from 'react';
import type { CustomAbi } from 'types/api/account';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import DeleteButton from 'ui/shared/DeleteButton';
import EditButton from 'ui/shared/EditButton';
interface Props {
item: CustomAbi;
onEditClick: (item: CustomAbi) => void;
onDeleteClick: (item: CustomAbi) => void;
}
const CustomAbiTableItem = ({ item, onEditClick, onDeleteClick }: Props) => {
const onItemEditClick = useCallback(() => {
return onEditClick(item);
}, [ item, onEditClick ]);
const onItemDeleteClick = useCallback(() => {
return onDeleteClick(item);
}, [ item, onDeleteClick ]);
return (
<Tr alignItems="top" key={ item.id }>
<Td>
<HStack>
<Text fontSize="md" fontWeight={ 600 }>{ item.contract_address_hash }</Text>
<CopyToClipboard text={ item.contract_address_hash }/>
</HStack>
<Text fontSize="sm" marginTop={ 0.5 } variant="secondary">{ item.name }</Text>
</Td>
<Td>
<HStack spacing={ 6 }>
<EditButton onClick={ onItemEditClick }/>
<DeleteButton onClick={ onItemDeleteClick }/>
</HStack>
</Td>
</Tr>
);
};
export default React.memo(CustomAbiTableItem);
import { Text } from '@chakra-ui/react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import React, { useCallback } from 'react';
import type { CustomAbi, CustomAbis } from 'types/api/account';
import DeleteModal from 'ui/shared/DeleteModal';
type Props = {
isOpen: boolean;
onClose: () => void;
data: CustomAbi;
}
const DeleteCustomAbiModal: React.FC<Props> = ({ isOpen, onClose, data }) => {
const queryClient = useQueryClient();
const deleteApiKey = () => {
return fetch(`/api/account/custom-abis/${ data.id }`, { method: 'DELETE' });
};
const mutation = useMutation(deleteApiKey, {
onSuccess: async() => {
queryClient.setQueryData([ 'custom-abis' ], (prevData: CustomAbis | undefined) => {
return prevData?.filter((item) => item.id !== data.id);
});
onClose();
},
// eslint-disable-next-line no-console
onError: console.error,
});
const onDelete = useCallback(() => {
mutation.mutate(data);
}, [ data, mutation ]);
const renderText = useCallback(() => {
return (
<Text display="flex">Custom ABI for<Text fontWeight="600" whiteSpace="pre">{ ` "${ data.name || 'name' }" ` }</Text>will be deleted</Text>
);
}, [ data.name ]);
return (
<DeleteModal
isOpen={ isOpen }
onClose={ onClose }
onDelete={ onDelete }
title="Remove custom ABI"
renderContent={ renderText }
pending={ mutation.isLoading }
/>
);
};
export default React.memo(DeleteCustomAbiModal);
......@@ -4,20 +4,18 @@ import React, { useCallback, useState } from 'react';
import type { CustomAbi, CustomAbis } from 'types/api/account';
// import ApiKeyTable from 'ui/apiKey/ApiKeyTable/ApiKeyTable';
// import DeleteApiKeyModal from 'ui/apiKey/DeleteApiKeyModal';
import CustomAbiModal from 'ui/customAbi/CustomAbiModal/CustomAbiModal';
import CustomAbiTable from 'ui/customAbi/CustomAbiTable/CustomAbiTable';
import DeleteCustomAbiModal from 'ui/customAbi/DeleteCustomAbiModal';
import AccountPageHeader from 'ui/shared/AccountPageHeader';
import Page from 'ui/shared/Page/Page';
const DATA_LIMIT = 3;
const CustomAbiPage: React.FC = () => {
const customAbiModalProps = useDisclosure();
// const deleteModalProps = useDisclosure();
const deleteModalProps = useDisclosure();
const [ customAbiModalData, setCustomAbiModalData ] = useState<CustomAbi>();
// const [ deleteModalData, setDeleteModalData ] = useState<CustomAbi>();
const [ deleteModalData, setDeleteModalData ] = useState<CustomAbi>();
const { data, isLoading, isError } = useQuery<unknown, unknown, CustomAbis>([ 'custom-abis' ], async() => {
const response = await fetch('/api/account/custom-abis');
......@@ -27,56 +25,48 @@ const CustomAbiPage: React.FC = () => {
return response.json();
});
// const onEditClick = useCallback((data: CustomAbi) => {
// setCustomAbiModalData(data);
// customAbiModalProps.onOpen();
// }, [ customAbiModalProps ]);
const onEditClick = useCallback((data: CustomAbi) => {
setCustomAbiModalData(data);
customAbiModalProps.onOpen();
}, [ customAbiModalProps ]);
const onCustomAbiModalClose = useCallback(() => {
setCustomAbiModalData(undefined);
customAbiModalProps.onClose();
}, [ customAbiModalProps ]);
// const onDeleteClick = useCallback((data: CustomAbi) => {
// setDeleteModalData(data);
// deleteModalProps.onOpen();
// }, [ deleteModalProps ]);
const onDeleteClick = useCallback((data: CustomAbi) => {
setDeleteModalData(data);
deleteModalProps.onOpen();
}, [ deleteModalProps ]);
// const onDeleteModalClose = useCallback(() => {
// setDeleteModalData(undefined);
// deleteModalProps.onClose();
// }, [ deleteModalProps ]);
const onDeleteModalClose = useCallback(() => {
setDeleteModalData(undefined);
deleteModalProps.onClose();
}, [ deleteModalProps ]);
const content = (() => {
if (isLoading || isError) {
return <Spinner/>;
}
const canAdd = data.length < DATA_LIMIT;
return (
<>
{ /* { data.length > 0 && (
<ApiKeyTable
{ data.length > 0 && (
<CustomAbiTable
data={ data }
onDeleteClick={ onDeleteClick }
onEditClick={ onEditClick }
limit={ DATA_LIMIT }
/>
) } */ }
) }
<HStack marginTop={ 8 } spacing={ 5 }>
<Button
variant="primary"
size="lg"
onClick={ customAbiModalProps.onOpen }
disabled={ !canAdd }
>
Add custom ABI
</Button>
{ !canAdd && (
<Text fontSize="sm" variant="secondary">
{ `You have added the maximum number of API keys (${ DATA_LIMIT }). Contact us to request additional keys.` }
</Text>
) }
</HStack>
</>
);
......@@ -92,7 +82,7 @@ const CustomAbiPage: React.FC = () => {
{ content }
</Box>
<CustomAbiModal { ...customAbiModalProps } onClose={ onCustomAbiModalClose } data={ customAbiModalData }/>
{ /* { deleteModalData && <DeleteApiKeyModal { ...deleteModalProps } onClose={ onDeleteModalClose } data={ deleteModalData }/> } */ }
{ deleteModalData && <DeleteCustomAbiModal { ...deleteModalProps } onClose={ onDeleteModalClose } data={ deleteModalData }/> }
</Page>
);
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment