2062 view edit an api key (#2231)
* Add query to get api keys * Add a link to apiKey detail page * Reset generatedApiKey when leaving page * Simplify stuff * Regenerate key when clicking on button * Simplify * Fix test * Refetch apiKeys when delete or create one * Add test for utils * Create utils function * Enable null expiration dates * Update formatExpiration * Fix display * Fix noteCard * Fix errors * Fix reset * Fix display * Fix renaming * Fix tests * Fix ci * Fix mocked data * Fix test * Update coverage requiremeents * Rename folder * Code review returns * Symplify sht code
This commit is contained in:
@ -1,11 +1,18 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { SettingsHeaderContainer } from '@/settings/components/SettingsHeaderContainer';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { ApiKeyInput } from '@/settings/developers/components/ApiKeyInput';
|
||||
import { generatedApiKeyState } from '@/settings/developers/states/generatedApiKeyState';
|
||||
import { IconSettings, IconTrash } from '@/ui/display/icon';
|
||||
import { GET_API_KEYS } from '@/settings/developers/graphql/queries/getApiKeys';
|
||||
import { useGeneratedApiKeys } from '@/settings/developers/hooks/useGeneratedApiKeys';
|
||||
import { generatedApiKeyFamilyState } from '@/settings/developers/states/generatedApiKeyFamilyState';
|
||||
import { computeNewExpirationDate } from '@/settings/developers/utils/compute-new-expiration-date';
|
||||
import { formatExpiration } from '@/settings/developers/utils/format-expiration';
|
||||
import { IconRepeat, IconSettings, IconTrash } from '@/ui/display/icon';
|
||||
import { H2Title } from '@/ui/display/typography/components/H2Title';
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
@ -15,62 +22,159 @@ import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
import {
|
||||
useDeleteOneApiKeyMutation,
|
||||
useGetApiKeyQuery,
|
||||
useInsertOneApiKeyMutation,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
const StyledInfo = styled.span`
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||||
`;
|
||||
|
||||
const StyledInputContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const SettingsDevelopersApiKeyDetail = () => {
|
||||
const navigate = useNavigate();
|
||||
const { apiKeyId = '' } = useParams();
|
||||
const [generatedApiKey] = useRecoilState(generatedApiKeyState);
|
||||
const apiKeyQuery = useGetApiKeyQuery({
|
||||
|
||||
const setGeneratedApi = useGeneratedApiKeys();
|
||||
const [generatedApiKey] = useRecoilState(
|
||||
generatedApiKeyFamilyState(apiKeyId),
|
||||
);
|
||||
|
||||
const [deleteApiKey] = useDeleteOneApiKeyMutation();
|
||||
const [insertOneApiKey] = useInsertOneApiKeyMutation();
|
||||
const apiKeyData = useGetApiKeyQuery({
|
||||
variables: {
|
||||
apiKeyId,
|
||||
},
|
||||
});
|
||||
const [deleteApiKey] = useDeleteOneApiKeyMutation();
|
||||
const deleteIntegration = async () => {
|
||||
await deleteApiKey({ variables: { apiKeyId } });
|
||||
navigate('/settings/developers/api-keys');
|
||||
}).data?.findManyApiKey[0];
|
||||
|
||||
const deleteIntegration = async (redirect = true) => {
|
||||
await deleteApiKey({
|
||||
variables: { apiKeyId },
|
||||
refetchQueries: [getOperationName(GET_API_KEYS) ?? ''],
|
||||
});
|
||||
if (redirect) {
|
||||
navigate('/settings/developers/api-keys');
|
||||
}
|
||||
};
|
||||
const { expiresAt, name } = apiKeyQuery.data?.findManyApiKey[0] || {};
|
||||
|
||||
const regenerateApiKey = async () => {
|
||||
if (apiKeyData?.name) {
|
||||
const newExpiresAt = computeNewExpirationDate(
|
||||
apiKeyData.expiresAt,
|
||||
apiKeyData.createdAt,
|
||||
);
|
||||
const apiKey = await insertOneApiKey({
|
||||
variables: {
|
||||
data: {
|
||||
name: apiKeyData.name,
|
||||
expiresAt: newExpiresAt,
|
||||
},
|
||||
},
|
||||
refetchQueries: [getOperationName(GET_API_KEYS) ?? ''],
|
||||
});
|
||||
await deleteIntegration(false);
|
||||
if (apiKey.data?.createOneApiKey) {
|
||||
setGeneratedApi(
|
||||
apiKey.data.createOneApiKey.id,
|
||||
apiKey.data.createOneApiKey.token,
|
||||
);
|
||||
navigate(
|
||||
`/settings/developers/api-keys/${apiKey.data.createOneApiKey.id}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (apiKeyData) {
|
||||
return () => {
|
||||
setGeneratedApi(apiKeyId, null);
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<SubMenuTopBarContainer Icon={IconSettings} title="Settings">
|
||||
<SettingsPageContainer>
|
||||
<SettingsHeaderContainer>
|
||||
<Breadcrumb
|
||||
links={[
|
||||
{ children: 'APIs', href: '/settings/developers/api-keys' },
|
||||
{ children: name || '' },
|
||||
]}
|
||||
/>
|
||||
</SettingsHeaderContainer>
|
||||
<Section>
|
||||
<H2Title
|
||||
title="Api Key"
|
||||
description="Copy this key as it will only be visible this one time"
|
||||
/>
|
||||
<ApiKeyInput expiresAt={expiresAt} apiKey={generatedApiKey || ''} />
|
||||
</Section>
|
||||
<Section>
|
||||
<H2Title title="Name" description="Name of your API key" />
|
||||
<TextInput
|
||||
placeholder="E.g. backoffice integration"
|
||||
value={name || ''}
|
||||
disabled={true}
|
||||
fullWidth
|
||||
/>
|
||||
</Section>
|
||||
<Section>
|
||||
<H2Title title="Danger zone" description="Delete this integration" />
|
||||
<Button
|
||||
accent="danger"
|
||||
variant="secondary"
|
||||
title="Disable"
|
||||
Icon={IconTrash}
|
||||
onClick={deleteIntegration}
|
||||
/>
|
||||
</Section>
|
||||
</SettingsPageContainer>
|
||||
</SubMenuTopBarContainer>
|
||||
<>
|
||||
{apiKeyData?.name && (
|
||||
<SubMenuTopBarContainer Icon={IconSettings} title="Settings">
|
||||
<SettingsPageContainer>
|
||||
<SettingsHeaderContainer>
|
||||
<Breadcrumb
|
||||
links={[
|
||||
{ children: 'APIs', href: '/settings/developers/api-keys' },
|
||||
{ children: apiKeyData.name },
|
||||
]}
|
||||
/>
|
||||
</SettingsHeaderContainer>
|
||||
<Section>
|
||||
{generatedApiKey ? (
|
||||
<>
|
||||
<H2Title
|
||||
title="Api Key"
|
||||
description="Copy this key as it will only be visible this one time"
|
||||
/>
|
||||
<ApiKeyInput apiKey={generatedApiKey} />
|
||||
<StyledInfo>
|
||||
{formatExpiration(apiKeyData?.expiresAt || '', true, false)}
|
||||
</StyledInfo>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<H2Title
|
||||
title="Api Key"
|
||||
description="Regenerate an Api key"
|
||||
/>
|
||||
<StyledInputContainer>
|
||||
<Button
|
||||
title="Regenerate Key"
|
||||
Icon={IconRepeat}
|
||||
onClick={regenerateApiKey}
|
||||
/>
|
||||
<StyledInfo>
|
||||
{formatExpiration(
|
||||
apiKeyData?.expiresAt || '',
|
||||
true,
|
||||
false,
|
||||
)}
|
||||
</StyledInfo>
|
||||
</StyledInputContainer>
|
||||
</>
|
||||
)}
|
||||
</Section>
|
||||
<Section>
|
||||
<H2Title title="Name" description="Name of your API key" />
|
||||
<TextInput
|
||||
placeholder="E.g. backoffice integration"
|
||||
value={apiKeyData.name}
|
||||
disabled
|
||||
fullWidth
|
||||
/>
|
||||
</Section>
|
||||
<Section>
|
||||
<H2Title
|
||||
title="Danger zone"
|
||||
description="Delete this integration"
|
||||
/>
|
||||
<Button
|
||||
accent="danger"
|
||||
variant="secondary"
|
||||
title="Disable"
|
||||
Icon={IconTrash}
|
||||
onClick={() => deleteIntegration()}
|
||||
/>
|
||||
</Section>
|
||||
</SettingsPageContainer>
|
||||
</SubMenuTopBarContainer>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user