fix: prevent saving API key with empty name (#18970)
Fixes: #18959 Generally, users need to type name before they create the api key. https://github.com/user-attachments/assets/bb3ec0ec-d05f-48a8-b762-a35288a9e111 <img width="656" height="559" alt="image" src="https://github.com/user-attachments/assets/cb8fd461-98f0-4475-b3f0-0de1e62624e2" />
This commit is contained in:
+6
-2
@@ -4,9 +4,10 @@ import {
|
||||
} from '@/settings/developers/utils/formatExpiration';
|
||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import { IconChevronRight } from 'twenty-ui/display';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useContext } from 'react';
|
||||
import { IconChevronRight } from 'twenty-ui/display';
|
||||
import { ThemeContext } from 'twenty-ui/theme-constants';
|
||||
import { type ApiKey } from '~/generated-metadata/graphql';
|
||||
|
||||
@@ -29,6 +30,7 @@ export const SettingsApiKeysFieldItemTableRow = ({
|
||||
apiKey,
|
||||
to,
|
||||
}: SettingsApiKeysFieldItemTableRowProps) => {
|
||||
const { t } = useLingui();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const formattedExpiration = formatExpiration(apiKey.expiresAt || null);
|
||||
|
||||
@@ -43,7 +45,9 @@ export const SettingsApiKeysFieldItemTableRow = ({
|
||||
textOverflow="ellipsis"
|
||||
clickable
|
||||
>
|
||||
<StyledEllipsisLabel>{apiKey.name}</StyledEllipsisLabel>
|
||||
<StyledEllipsisLabel>
|
||||
{apiKey.name || t`Unnamed API Key`}
|
||||
</StyledEllipsisLabel>
|
||||
</TableCell>
|
||||
|
||||
<TableCell
|
||||
|
||||
+13
-7
@@ -201,12 +201,18 @@ export const SettingsDevelopersApiKeyDetail = () => {
|
||||
const regenerateApiKey = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
if (isNonEmptyString(apiKey?.name)) {
|
||||
if (isDefined(apiKey)) {
|
||||
if (!isNonEmptyString(apiKeyName)) {
|
||||
enqueueErrorSnackBar({
|
||||
message: t`API key name cannot be empty`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const newExpiresAt = computeNewExpirationDate(
|
||||
apiKey?.expiresAt,
|
||||
apiKey?.createdAt,
|
||||
apiKey.expiresAt,
|
||||
apiKey.createdAt,
|
||||
);
|
||||
const newApiKey = await createIntegration(apiKey?.name, newExpiresAt);
|
||||
const newApiKey = await createIntegration(apiKeyName, newExpiresAt);
|
||||
await deleteIntegration(false);
|
||||
|
||||
if (isNonEmptyString(newApiKey?.token)) {
|
||||
@@ -233,9 +239,9 @@ export const SettingsDevelopersApiKeyDetail = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{apiKey?.name && (
|
||||
{isDefined(apiKey) && (
|
||||
<SubMenuTopBarContainer
|
||||
title={apiKey?.name}
|
||||
title={apiKey.name || t`Unnamed API Key`}
|
||||
links={[
|
||||
{
|
||||
children: t`Workspace`,
|
||||
@@ -245,7 +251,7 @@ export const SettingsDevelopersApiKeyDetail = () => {
|
||||
children: t`APIs & Webhooks`,
|
||||
href: getSettingsPath(SettingsPath.ApiWebhooks),
|
||||
},
|
||||
{ children: apiKey?.name },
|
||||
{ children: apiKey.name || t`Unnamed API Key` },
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
|
||||
+7
-5
@@ -1,5 +1,5 @@
|
||||
import { addDays } from 'date-fns';
|
||||
import { useState, useCallback, useEffect } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
@@ -10,6 +10,7 @@ import { apiKeyTokenFamilyState } from '@/settings/developers/states/apiKeyToken
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
import { SettingsTextInput } from '@/ui/input/components/SettingsTextInput';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
|
||||
import { useMutation, useQuery } from '@apollo/client/react';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useStore } from 'jotai';
|
||||
import { Key } from 'ts-key-enum';
|
||||
@@ -17,7 +18,6 @@ import { SettingsPath } from 'twenty-shared/types';
|
||||
import { getSettingsPath, isDefined } from 'twenty-shared/utils';
|
||||
import { H2Title } from 'twenty-ui/display';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { useMutation, useQuery } from '@apollo/client/react';
|
||||
import {
|
||||
CreateApiKeyDocument,
|
||||
GenerateApiKeyTokenDocument,
|
||||
@@ -70,6 +70,8 @@ export const SettingsDevelopersApiKeysNew = () => {
|
||||
);
|
||||
|
||||
const handleSave = async () => {
|
||||
if (!formValues.name) return;
|
||||
|
||||
const expiresAt = addDays(
|
||||
new Date(),
|
||||
formValues.expirationDate ?? 30,
|
||||
@@ -84,7 +86,7 @@ export const SettingsDevelopersApiKeysNew = () => {
|
||||
const { data: newApiKeyData } = await createApiKey({
|
||||
variables: {
|
||||
input: {
|
||||
name: formValues.name,
|
||||
name: formValues.name.trim(),
|
||||
expiresAt,
|
||||
roleId: roleIdToUse,
|
||||
},
|
||||
@@ -115,7 +117,7 @@ export const SettingsDevelopersApiKeysNew = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const canSave = !!formValues.name && !!formValues.roleId && createApiKey;
|
||||
const canSave = !!formValues.name && !!formValues.roleId;
|
||||
|
||||
if (rolesLoading) {
|
||||
return <SettingsSkeletonLoader />;
|
||||
@@ -137,7 +139,7 @@ export const SettingsDevelopersApiKeysNew = () => {
|
||||
]}
|
||||
actionButton={
|
||||
<SaveAndCancelButtons
|
||||
isSaveDisabled={!isDefined(canSave)}
|
||||
isSaveDisabled={!canSave}
|
||||
onCancel={() => {
|
||||
navigateSettings(SettingsPath.ApiWebhooks);
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user