fix: Fixed API typo and webhook checkerror (#6779)
## Issue 1.There was an Api typo with API under Developers section #6778 2. Webhook lacked an check method for the `TextInput` #6774 ## After- <img width="649" alt="Screenshot 2024-08-29 at 2 13 21 AM" src="https://github.com/user-attachments/assets/bc9595f8-533f-430e-bc18-56373983eec8"> https://github.com/user-attachments/assets/8e2b06bc-308a-48ad-8ecb-9d0a130877bc --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -136,7 +136,7 @@ export const SettingsDevelopersApiKeyDetail = () => {
|
|||||||
{apiKeyToken ? (
|
{apiKeyToken ? (
|
||||||
<>
|
<>
|
||||||
<H2Title
|
<H2Title
|
||||||
title="Api Key"
|
title="API Key"
|
||||||
description="Copy this key as it will only be visible this one time"
|
description="Copy this key as it will only be visible this one time"
|
||||||
/>
|
/>
|
||||||
<ApiKeyInput apiKey={apiKeyToken} />
|
<ApiKeyInput apiKey={apiKeyToken} />
|
||||||
@ -147,8 +147,8 @@ export const SettingsDevelopersApiKeyDetail = () => {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<H2Title
|
<H2Title
|
||||||
title="Api Key"
|
title="API Key"
|
||||||
description="Regenerate an Api key"
|
description="Regenerate an API key"
|
||||||
/>
|
/>
|
||||||
<StyledInputContainer>
|
<StyledInputContainer>
|
||||||
<Button
|
<Button
|
||||||
@ -213,7 +213,7 @@ export const SettingsDevelopersApiKeyDetail = () => {
|
|||||||
confirmationValue="yes"
|
confirmationValue="yes"
|
||||||
isOpen={isDeleteApiKeyModalOpen}
|
isOpen={isDeleteApiKeyModalOpen}
|
||||||
setIsOpen={setIsDeleteApiKeyModalOpen}
|
setIsOpen={setIsDeleteApiKeyModalOpen}
|
||||||
title="Delete Api key"
|
title="Delete API key"
|
||||||
subtitle={
|
subtitle={
|
||||||
<>
|
<>
|
||||||
Please type "yes" to confirm you want to delete this API Key. Be
|
Please type "yes" to confirm you want to delete this API Key. Be
|
||||||
@ -228,7 +228,7 @@ export const SettingsDevelopersApiKeyDetail = () => {
|
|||||||
confirmationValue="yes"
|
confirmationValue="yes"
|
||||||
isOpen={isRegenerateKeyModalOpen}
|
isOpen={isRegenerateKeyModalOpen}
|
||||||
setIsOpen={setIsRegenerateKeyModalOpen}
|
setIsOpen={setIsRegenerateKeyModalOpen}
|
||||||
title="Regenerate an Api key"
|
title="Regenerate an API key"
|
||||||
subtitle={
|
subtitle={
|
||||||
<>
|
<>
|
||||||
If you’ve lost this key, you can regenerate it, but be aware that
|
If you’ve lost this key, you can regenerate it, but be aware that
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import { TextInput } from '@/ui/input/components/TextInput';
|
|||||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||||
import { Section } from '@/ui/layout/section/components/Section';
|
import { Section } from '@/ui/layout/section/components/Section';
|
||||||
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||||
|
import { isValidUrl } from '~/utils/url/isValidUrl';
|
||||||
|
|
||||||
export const SettingsDevelopersWebhooksNew = () => {
|
export const SettingsDevelopersWebhooksNew = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -21,9 +22,18 @@ export const SettingsDevelopersWebhooksNew = () => {
|
|||||||
targetUrl: '',
|
targetUrl: '',
|
||||||
operation: '*.*',
|
operation: '*.*',
|
||||||
});
|
});
|
||||||
|
const [isTargetUrlValid, setIsTargetUrlValid] = useState(true);
|
||||||
|
|
||||||
const { createOneRecord: createOneWebhook } = useCreateOneRecord<Webhook>({
|
const { createOneRecord: createOneWebhook } = useCreateOneRecord<Webhook>({
|
||||||
objectNameSingular: CoreObjectNameSingular.Webhook,
|
objectNameSingular: CoreObjectNameSingular.Webhook,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleValidate = async (value: string) => {
|
||||||
|
const trimmedUrl = value.trim();
|
||||||
|
|
||||||
|
setIsTargetUrlValid(isValidUrl(trimmedUrl));
|
||||||
|
};
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
const newWebhook = await createOneWebhook?.(formValues);
|
const newWebhook = await createOneWebhook?.(formValues);
|
||||||
|
|
||||||
@ -32,7 +42,10 @@ export const SettingsDevelopersWebhooksNew = () => {
|
|||||||
}
|
}
|
||||||
navigate(`/settings/developers/webhooks/${newWebhook.id}`);
|
navigate(`/settings/developers/webhooks/${newWebhook.id}`);
|
||||||
};
|
};
|
||||||
const canSave = !!formValues.targetUrl && createOneWebhook;
|
|
||||||
|
const canSave =
|
||||||
|
!!formValues.targetUrl && isTargetUrlValid && createOneWebhook;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SubMenuTopBarContainer
|
<SubMenuTopBarContainer
|
||||||
Icon={IconCode}
|
Icon={IconCode}
|
||||||
@ -63,6 +76,7 @@ export const SettingsDevelopersWebhooksNew = () => {
|
|||||||
<TextInput
|
<TextInput
|
||||||
placeholder="URL"
|
placeholder="URL"
|
||||||
value={formValues.targetUrl}
|
value={formValues.targetUrl}
|
||||||
|
error={!isTargetUrlValid ? 'Please enter a valid URL' : undefined}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
handleSave();
|
handleSave();
|
||||||
@ -73,6 +87,7 @@ export const SettingsDevelopersWebhooksNew = () => {
|
|||||||
...prevState,
|
...prevState,
|
||||||
targetUrl: value,
|
targetUrl: value,
|
||||||
}));
|
}));
|
||||||
|
handleValidate(value);
|
||||||
}}
|
}}
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -0,0 +1,25 @@
|
|||||||
|
import { isValidUrl } from '../isValidUrl';
|
||||||
|
|
||||||
|
describe('isValidUrl', () => {
|
||||||
|
it('test cases', () => {
|
||||||
|
// Truthy
|
||||||
|
expect(isValidUrl('https://www.example.com')).toBe(true);
|
||||||
|
expect(isValidUrl('http://192.168.2.0:3000')).toBe(true);
|
||||||
|
expect(isValidUrl('http://localhost')).toBe(true);
|
||||||
|
expect(isValidUrl('http://localhost:3000')).toBe(true);
|
||||||
|
expect(isValidUrl('http://subdomain.example.com')).toBe(true);
|
||||||
|
expect(isValidUrl('https://www.example.com/path')).toBe(true);
|
||||||
|
expect(isValidUrl('https://www.example.com/path/path2?query=123')).toBe(
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
expect(isValidUrl('http://localhost:3000')).toBe(true);
|
||||||
|
expect(isValidUrl('example.com')).toBe(true);
|
||||||
|
expect(isValidUrl('www.subdomain.example.com')).toBe(true);
|
||||||
|
|
||||||
|
// Falsy
|
||||||
|
expect(isValidUrl('?o')).toBe(false);
|
||||||
|
expect(isValidUrl('')).toBe(false);
|
||||||
|
expect(isValidUrl('\\')).toBe(false);
|
||||||
|
expect(isValidUrl('wwwexamplecom')).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
8
packages/twenty-front/src/utils/url/isValidUrl.ts
Normal file
8
packages/twenty-front/src/utils/url/isValidUrl.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export const isValidUrl = (url: string) => {
|
||||||
|
const urlRegex =
|
||||||
|
/^(https?:\/\/)?((([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,})|(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|(localhost))(:\d+)?(\/[^\s]*)?(\?[^\s]*)?$/;
|
||||||
|
|
||||||
|
const urlPattern = new RegExp(urlRegex, 'i');
|
||||||
|
|
||||||
|
return !!urlPattern.test(url);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user