mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-25 21:13:01 +03:00
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:
parent
96d659cf56
commit
f8890689ee
@ -136,7 +136,7 @@ export const SettingsDevelopersApiKeyDetail = () => {
|
||||
{apiKeyToken ? (
|
||||
<>
|
||||
<H2Title
|
||||
title="Api Key"
|
||||
title="API Key"
|
||||
description="Copy this key as it will only be visible this one time"
|
||||
/>
|
||||
<ApiKeyInput apiKey={apiKeyToken} />
|
||||
@ -147,8 +147,8 @@ export const SettingsDevelopersApiKeyDetail = () => {
|
||||
) : (
|
||||
<>
|
||||
<H2Title
|
||||
title="Api Key"
|
||||
description="Regenerate an Api key"
|
||||
title="API Key"
|
||||
description="Regenerate an API key"
|
||||
/>
|
||||
<StyledInputContainer>
|
||||
<Button
|
||||
@ -213,7 +213,7 @@ export const SettingsDevelopersApiKeyDetail = () => {
|
||||
confirmationValue="yes"
|
||||
isOpen={isDeleteApiKeyModalOpen}
|
||||
setIsOpen={setIsDeleteApiKeyModalOpen}
|
||||
title="Delete Api key"
|
||||
title="Delete API key"
|
||||
subtitle={
|
||||
<>
|
||||
Please type "yes" to confirm you want to delete this API Key. Be
|
||||
@ -228,7 +228,7 @@ export const SettingsDevelopersApiKeyDetail = () => {
|
||||
confirmationValue="yes"
|
||||
isOpen={isRegenerateKeyModalOpen}
|
||||
setIsOpen={setIsRegenerateKeyModalOpen}
|
||||
title="Regenerate an Api key"
|
||||
title="Regenerate an API key"
|
||||
subtitle={
|
||||
<>
|
||||
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 { Section } from '@/ui/layout/section/components/Section';
|
||||
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
import { isValidUrl } from '~/utils/url/isValidUrl';
|
||||
|
||||
export const SettingsDevelopersWebhooksNew = () => {
|
||||
const navigate = useNavigate();
|
||||
@ -21,9 +22,18 @@ export const SettingsDevelopersWebhooksNew = () => {
|
||||
targetUrl: '',
|
||||
operation: '*.*',
|
||||
});
|
||||
const [isTargetUrlValid, setIsTargetUrlValid] = useState(true);
|
||||
|
||||
const { createOneRecord: createOneWebhook } = useCreateOneRecord<Webhook>({
|
||||
objectNameSingular: CoreObjectNameSingular.Webhook,
|
||||
});
|
||||
|
||||
const handleValidate = async (value: string) => {
|
||||
const trimmedUrl = value.trim();
|
||||
|
||||
setIsTargetUrlValid(isValidUrl(trimmedUrl));
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
const newWebhook = await createOneWebhook?.(formValues);
|
||||
|
||||
@ -32,7 +42,10 @@ export const SettingsDevelopersWebhooksNew = () => {
|
||||
}
|
||||
navigate(`/settings/developers/webhooks/${newWebhook.id}`);
|
||||
};
|
||||
const canSave = !!formValues.targetUrl && createOneWebhook;
|
||||
|
||||
const canSave =
|
||||
!!formValues.targetUrl && isTargetUrlValid && createOneWebhook;
|
||||
|
||||
return (
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconCode}
|
||||
@ -63,6 +76,7 @@ export const SettingsDevelopersWebhooksNew = () => {
|
||||
<TextInput
|
||||
placeholder="URL"
|
||||
value={formValues.targetUrl}
|
||||
error={!isTargetUrlValid ? 'Please enter a valid URL' : undefined}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleSave();
|
||||
@ -73,6 +87,7 @@ export const SettingsDevelopersWebhooksNew = () => {
|
||||
...prevState,
|
||||
targetUrl: value,
|
||||
}));
|
||||
handleValidate(value);
|
||||
}}
|
||||
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);
|
||||
};
|
Loading…
Reference in New Issue
Block a user