Commit c5909088 authored by tom's avatar tom

create, update and delete operations

parent a651daec
import { Flex, Text, FormControl, FormLabel, Textarea } from '@chakra-ui/react'; import { Flex, Text, FormControl, FormLabel, Textarea } from '@chakra-ui/react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import type { ChangeEvent } from 'react'; import type { ChangeEvent } from 'react';
import type { PublicTag } from 'types/api/account'; import type { PublicTags, PublicTag } from 'types/api/account';
import DeleteModal from 'ui/shared/DeleteModal'; import DeleteModal from 'ui/shared/DeleteModal';
...@@ -15,15 +16,33 @@ type Props = { ...@@ -15,15 +16,33 @@ type Props = {
const DeletePublicTagModal: React.FC<Props> = ({ isOpen, onClose, data, onDeleteSuccess }) => { const DeletePublicTagModal: React.FC<Props> = ({ isOpen, onClose, data, onDeleteSuccess }) => {
const [ reason, setReason ] = useState<string>('');
const tags = data.tags.split(';'); const tags = data.tags.split(';');
const onDelete = useCallback(() => { const queryClient = useQueryClient();
const deleteApiKey = (reason: string) => {
const body = JSON.stringify({ remove_reason: reason });
return fetch(`/api/account/public-tags/${ data.id }`, { method: 'DELETE', body });
};
const mutation = useMutation(deleteApiKey, {
onSuccess: async() => {
queryClient.setQueryData([ 'public-tags' ], (prevData: PublicTags | undefined) => {
return prevData?.filter((item) => item.id !== data.id);
});
onClose();
},
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('delete', data); onError: console.error,
onDeleteSuccess(); });
}, [ data, onDeleteSuccess ]);
const [ reason, setReason ] = useState<string>(''); const onDelete = useCallback(() => {
mutation.mutate(reason);
onDeleteSuccess();
}, [ reason, mutation, onDeleteSuccess ]);
const onFieldChange = useCallback((event: ChangeEvent<HTMLTextAreaElement>) => { const onFieldChange = useCallback((event: ChangeEvent<HTMLTextAreaElement>) => {
setReason(event.currentTarget.value); setReason(event.currentTarget.value);
...@@ -85,6 +104,7 @@ const DeletePublicTagModal: React.FC<Props> = ({ isOpen, onClose, data, onDelete ...@@ -85,6 +104,7 @@ const DeletePublicTagModal: React.FC<Props> = ({ isOpen, onClose, data, onDelete
onDelete={ onDelete } onDelete={ onDelete }
title="Request to remove a public tag" title="Request to remove a public tag"
renderContent={ renderContent } renderContent={ renderContent }
pending={ mutation.isLoading }
/> />
); );
}; };
......
...@@ -7,24 +7,24 @@ import type { Inputs } from './PublicTagsForm'; ...@@ -7,24 +7,24 @@ import type { Inputs } from './PublicTagsForm';
interface Props { interface Props {
control: Control<Inputs>; control: Control<Inputs>;
canReport: boolean; isDisabled?: boolean;
} }
export default function PublicTagFormAction({ control, canReport }: Props) { export default function PublicTagFormAction({ control, isDisabled }: Props) {
const renderRadioGroup = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'action'>}) => { const renderRadioGroup = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'action'>}) => {
return ( return (
<RadioGroup defaultValue="add" value={ field.value } colorScheme="blue"> <RadioGroup defaultValue="add" colorScheme="blue" { ...field }>
<Stack spacing={ 5 }> <Stack spacing={ 5 }>
<Radio value="add"> <Radio value="add">
I want to add tags for my project I want to add tags for my project
</Radio> </Radio>
<Radio value="report" isDisabled={ canReport }> <Radio value="report" isDisabled={ isDisabled }>
I want to report an incorrect public tag I want to report an incorrect public tag
</Radio> </Radio>
</Stack> </Stack>
</RadioGroup> </RadioGroup>
); );
}, [ canReport ]); }, [ isDisabled ]);
return ( return (
<Controller <Controller
......
...@@ -6,11 +6,12 @@ import { ...@@ -6,11 +6,12 @@ import {
Text, Text,
HStack, HStack,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import type { Path } from 'react-hook-form'; import type { Path, SubmitHandler } from 'react-hook-form';
import { useForm, useFieldArray } from 'react-hook-form'; import { useForm, useFieldArray } from 'react-hook-form';
import type { PublicTag } from 'types/api/account'; import type { PublicTags, PublicTag, PublicTagNew } from 'types/api/account';
import PublicTagFormAction from './PublicTagFormAction'; import PublicTagFormAction from './PublicTagFormAction';
import PublicTagFormAddressInput from './PublicTagFormAddressInput'; import PublicTagFormAddressInput from './PublicTagFormAddressInput';
...@@ -23,41 +24,44 @@ type Props = { ...@@ -23,41 +24,44 @@ type Props = {
} }
export type Inputs = { export type Inputs = {
userName: string; fullName?: string;
userEmail: string; email?: string;
companyName: string; companyName?: string;
companyUrl: string; companyUrl?: string;
action: 'add' | 'report'; action: 'add' | 'report';
tag: string; tags?: string;
addresses: Array<{ addresses?: Array<{
name: string; name: string;
address: string; address: string;
}>; }>;
comment: string; comment?: string;
} }
const placeholders = { const placeholders = {
userName: 'Your name', fullName: 'Your name',
userEmail: 'Email', email: 'Email',
companyName: 'Company name', companyName: 'Company name',
companyUrl: 'Company website', companyUrl: 'Company website',
tag: 'Public tag (max 35 characters)', tags: 'Public tag (max 35 characters)',
comment: 'Specify the reason for adding tags and color preference(s).', comment: 'Specify the reason for adding tags and color preference(s).',
} as Record<Path<Inputs>, string>; } as Record<Path<Inputs>, string>;
const ADDRESS_INPUT_BUTTONS_WIDTH = 170; const ADDRESS_INPUT_BUTTONS_WIDTH = 170;
const PublicTagsForm = ({ changeToDataScreen, data }: Props) => { const PublicTagsForm = ({ changeToDataScreen, data }: Props) => {
const queryClient = useQueryClient();
const { control, handleSubmit, formState: { errors } } = useForm<Inputs>({ const { control, handleSubmit, formState: { errors } } = useForm<Inputs>({
defaultValues: { defaultValues: {
userName: data?.full_name, fullName: data?.full_name,
userEmail: data?.email, email: data?.email,
companyName: data?.company, companyName: data?.company,
companyUrl: data?.website, companyUrl: data?.website,
tag: data?.tags.split(';').map((tag) => tag).join('; '), tags: data?.tags.split(';').map((tag) => tag).join('; '),
addresses: data?.addresses.split(';').map((address, index: number) => ({ name: `address.${ index }.address`, address })) || addresses: data?.addresses.split(';').map((address, index: number) => ({ name: `address.${ index }.address`, address })) ||
[ { name: 'address.0.address', address: '' } ], [ { name: 'address.0.address', address: '' } ],
comment: data?.additional_comment, comment: data?.additional_comment,
action: data?.is_owner === undefined || data?.is_owner ? 'add' : 'report',
}, },
}); });
...@@ -67,10 +71,61 @@ const PublicTagsForm = ({ changeToDataScreen, data }: Props) => { ...@@ -67,10 +71,61 @@ const PublicTagsForm = ({ changeToDataScreen, data }: Props) => {
}); });
const onAddFieldClick = useCallback(() => append({ address: '' }), [ append ]); const onAddFieldClick = useCallback(() => append({ address: '' }), [ append ]);
const onRemoveFieldClick = useCallback((index: number) => () => remove(index), [ remove ]); const onRemoveFieldClick = useCallback((index: number) => () => remove(index), [ remove ]);
const changeToData = useCallback(() => { const updatePublicTag = (formData: Inputs) => {
const payload: PublicTagNew = {
full_name: formData.fullName || '',
email: formData.email || '',
company: formData.companyName || '',
website: formData.companyUrl || '',
is_owner: formData.action === 'add',
addresses_array: formData.addresses?.map(({ address }) => address) || [],
tags: formData.tags?.split(';').map((s) => s.trim()).join(';') || '',
additional_comment: formData.comment || '',
};
const body = JSON.stringify(payload);
if (!data?.id) {
return fetch('/api/account/public-tags', { method: 'POST', body });
}
return fetch(`/api/account/public-tags/${ data.id }`, { method: 'PUT', body });
};
const mutation = useMutation(updatePublicTag, {
onSuccess: async(data) => {
const response: PublicTag = await data.json();
queryClient.setQueryData([ 'public-tags' ], (prevData: PublicTags | undefined) => {
const isExisting = prevData && prevData.some((item) => item.id === response.id);
if (isExisting) {
return prevData.map((item) => {
if (item.id === response.id) {
return response;
}
return item;
});
}
return [ ...(prevData || []), response ];
});
changeToDataScreen(true); changeToDataScreen(true);
},
// eslint-disable-next-line no-console
onError: console.error,
});
const onSubmit: SubmitHandler<Inputs> = useCallback((data) => {
mutation.mutate(data);
}, [ mutation ]);
const changeToData = useCallback(() => {
changeToDataScreen(false);
}, [ changeToDataScreen ]); }, [ changeToDataScreen ]);
return ( return (
...@@ -78,24 +133,24 @@ const PublicTagsForm = ({ changeToDataScreen, data }: Props) => { ...@@ -78,24 +133,24 @@ const PublicTagsForm = ({ changeToDataScreen, data }: Props) => {
<Text size="sm" variant="secondary" paddingBottom={ 5 }>Company info</Text> <Text size="sm" variant="secondary" paddingBottom={ 5 }>Company info</Text>
<Grid templateColumns="1fr 1fr" rowGap={ 4 } columnGap={ 5 }> <Grid templateColumns="1fr 1fr" rowGap={ 4 } columnGap={ 5 }>
<GridItem> <GridItem>
<PublicTagsFormInput<Inputs> fieldName="userName" control={ control } label={ placeholders.userName } required/> <PublicTagsFormInput<Inputs> fieldName="fullName" control={ control } label={ placeholders.fullName } required/>
</GridItem> </GridItem>
<GridItem> <GridItem>
<PublicTagsFormInput<Inputs> fieldName="companyName" control={ control } label={ placeholders.companyName }/> <PublicTagsFormInput<Inputs> fieldName="companyName" control={ control } label={ placeholders.companyName }/>
</GridItem> </GridItem>
<GridItem> <GridItem>
<PublicTagsFormInput<Inputs> fieldName="userEmail" control={ control } label={ placeholders.userEmail } required/> <PublicTagsFormInput<Inputs> fieldName="email" control={ control } label={ placeholders.email } required/>
</GridItem> </GridItem>
<GridItem> <GridItem>
<PublicTagsFormInput<Inputs> fieldName="companyUrl" control={ control } label={ placeholders.companyUrl }/> <PublicTagsFormInput<Inputs> fieldName="companyUrl" control={ control } label={ placeholders.companyUrl }/>
</GridItem> </GridItem>
</Grid> </Grid>
<Box marginTop={ 4 } marginBottom={ 8 }> <Box marginTop={ 4 } marginBottom={ 8 }>
<PublicTagFormAction canReport={ Boolean(data) } control={ control }/> <PublicTagFormAction control={ control }/>
</Box> </Box>
<Text size="sm" variant="secondary" marginBottom={ 5 }>Public tags (2 tags maximum, please use &quot;;&quot; as a divider)</Text> <Text size="sm" variant="secondary" marginBottom={ 5 }>Public tags (2 tags maximum, please use &quot;;&quot; as a divider)</Text>
<Box marginBottom={ 4 }> <Box marginBottom={ 4 }>
<PublicTagsFormInput<Inputs> fieldName="tag" control={ control } label={ placeholders.tag } required/> <PublicTagsFormInput<Inputs> fieldName="tags" control={ control } label={ placeholders.tags } required/>
</Box> </Box>
{ fields.map((field, index) => { { fields.map((field, index) => {
return ( return (
...@@ -118,8 +173,8 @@ const PublicTagsForm = ({ changeToDataScreen, data }: Props) => { ...@@ -118,8 +173,8 @@ const PublicTagsForm = ({ changeToDataScreen, data }: Props) => {
<Button <Button
size="lg" size="lg"
variant="primary" variant="primary"
onClick={ handleSubmit(changeToData) } onClick={ handleSubmit(onSubmit) }
disabled={ Object.keys(errors).length > 0 } disabled={ Object.keys(errors).length > 0 || mutation.isLoading }
> >
Send request Send request
</Button> </Button>
......
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