We have recently introduced the possibility to sign up on Twenty cloud without having to input a credit card which makes the demo.twenty.com useless. Deprecating it!
213 lines
6.0 KiB
TypeScript
213 lines
6.0 KiB
TypeScript
'use client';
|
|
|
|
import { usePathname, useRouter } from 'next/navigation';
|
|
import React, { useEffect, useState } from 'react';
|
|
import { TbApi, TbChevronLeft, TbLink } from 'react-icons/tb';
|
|
|
|
// @ts-expect-error Migration loader as text not passing warnings
|
|
import tokenForm from '!css-loader!./token-form.css';
|
|
|
|
export type SubDoc = 'core' | 'metadata';
|
|
export type TokenFormProps = {
|
|
setOpenApiJson?: (json: object) => void;
|
|
setToken?: (token: string) => void;
|
|
setBaseUrl?: (baseUrl: string) => void;
|
|
isTokenValid?: boolean;
|
|
setIsTokenValid?: (arg: boolean) => void;
|
|
setLoadingState?: (arg: boolean) => void;
|
|
subDoc?: SubDoc;
|
|
};
|
|
|
|
const TokenForm = ({
|
|
setOpenApiJson,
|
|
setToken,
|
|
setBaseUrl: submitBaseUrl,
|
|
isTokenValid,
|
|
setIsTokenValid,
|
|
subDoc,
|
|
setLoadingState,
|
|
}: TokenFormProps) => {
|
|
const router = useRouter();
|
|
|
|
const pathname = usePathname();
|
|
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [locationSetting, setLocationSetting] = useState(
|
|
(typeof window !== 'undefined' &&
|
|
window.localStorage.getItem('baseUrl') &&
|
|
JSON.parse(window.localStorage.getItem('baseUrl') ?? '')
|
|
?.locationSetting) ??
|
|
'production',
|
|
);
|
|
const [baseUrl, setBaseUrl] = useState(
|
|
(typeof window !== 'undefined' &&
|
|
window.localStorage.getItem('baseUrl') &&
|
|
JSON.parse(window.localStorage.getItem('baseUrl') ?? '')?.baseUrl) ??
|
|
'https://api.twenty.com',
|
|
);
|
|
|
|
const tokenLocal = (
|
|
typeof window !== 'undefined'
|
|
? window?.localStorage?.getItem?.('TryIt_securitySchemeValues')
|
|
: '{}'
|
|
) as string;
|
|
|
|
const token = JSON.parse(tokenLocal)?.bearerAuth ?? '';
|
|
|
|
const updateLoading = (loading = false) => {
|
|
setIsLoading(loading);
|
|
setLoadingState?.(!!loading);
|
|
};
|
|
|
|
const updateToken = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
window.localStorage.setItem(
|
|
'TryIt_securitySchemeValues',
|
|
JSON.stringify({ bearerAuth: event.target.value }),
|
|
);
|
|
await submitToken(event.target.value);
|
|
};
|
|
|
|
const updateBaseUrl = (baseUrl: string, locationSetting: string) => {
|
|
let url: string;
|
|
if (locationSetting === 'production') {
|
|
url = 'https://api.twenty.com';
|
|
} else if (locationSetting === 'next') {
|
|
url = 'https://api.twenty-next.com';
|
|
} else if (locationSetting === 'localhost') {
|
|
url = 'http://localhost:3000';
|
|
} else {
|
|
url = baseUrl;
|
|
}
|
|
|
|
setBaseUrl(url);
|
|
setLocationSetting(locationSetting);
|
|
submitBaseUrl?.(url);
|
|
window.localStorage.setItem(
|
|
'baseUrl',
|
|
JSON.stringify({ baseUrl: url, locationSetting }),
|
|
);
|
|
};
|
|
|
|
const validateToken = (openApiJson: any) => {
|
|
setIsTokenValid?.(!!openApiJson.tags);
|
|
};
|
|
|
|
const getJson = async (token: string) => {
|
|
updateLoading(true);
|
|
|
|
return await fetch(baseUrl + '/open-api/' + (subDoc ?? 'core'), {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
.then((res) => res.json())
|
|
.then((result) => {
|
|
validateToken(result);
|
|
updateLoading(false);
|
|
|
|
return result;
|
|
})
|
|
.catch(() => {
|
|
updateLoading(false);
|
|
setIsTokenValid?.(false);
|
|
});
|
|
};
|
|
|
|
const submitToken = async (token: any) => {
|
|
if (isLoading) return;
|
|
|
|
const json = await getJson(token);
|
|
|
|
setToken && setToken(token);
|
|
|
|
setOpenApiJson && setOpenApiJson(json);
|
|
};
|
|
|
|
useEffect(() => {
|
|
(async () => {
|
|
updateBaseUrl(baseUrl, locationSetting);
|
|
await submitToken(token);
|
|
})();
|
|
}, []);
|
|
|
|
// We load playground style using useEffect as it breaks remaining docs style
|
|
useEffect(() => {
|
|
const styleElement = document.createElement('style');
|
|
styleElement.innerHTML = tokenForm.toString();
|
|
document.head.append(styleElement);
|
|
|
|
return () => styleElement.remove();
|
|
}, []);
|
|
|
|
return (
|
|
<div className="form-container">
|
|
<form className="form">
|
|
<div className="backButton" onClick={() => router.back()}>
|
|
<TbChevronLeft size={18} />
|
|
<span>Back</span>
|
|
</div>
|
|
<div className="inputWrapper">
|
|
<select
|
|
className="select"
|
|
onChange={(event) => {
|
|
updateBaseUrl(baseUrl, event.target.value);
|
|
}}
|
|
value={locationSetting}
|
|
>
|
|
<option value="production">Production API</option>
|
|
<option value="demo">Demo API</option>
|
|
<option value="localhost">Localhost</option>
|
|
<option value="other">Other</option>
|
|
</select>
|
|
</div>
|
|
<div className="inputWrapper">
|
|
<div className="inputIcon" title="Base URL">
|
|
<TbLink size={20} />
|
|
</div>
|
|
<input
|
|
className={'input'}
|
|
type="text"
|
|
readOnly={isLoading}
|
|
disabled={locationSetting !== 'other'}
|
|
placeholder="Base URL"
|
|
value={baseUrl}
|
|
onChange={(event) =>
|
|
updateBaseUrl(event.target.value, locationSetting)
|
|
}
|
|
onBlur={() => submitToken(token)}
|
|
/>
|
|
</div>
|
|
<div className="inputWrapper">
|
|
<div className="inputIcon" title="Api Key">
|
|
<TbApi size={20} />
|
|
</div>
|
|
<input
|
|
className={!isTokenValid && !isLoading ? 'input invalid' : 'input'}
|
|
type="text"
|
|
readOnly={isLoading}
|
|
placeholder="API Key"
|
|
defaultValue={token}
|
|
onChange={updateToken}
|
|
/>
|
|
</div>
|
|
<div className="inputWrapper" style={{ maxWidth: '100px' }}>
|
|
<select
|
|
className="select"
|
|
onChange={(event) =>
|
|
router.replace(
|
|
pathname.split('/').slice(0, -1).join('/') +
|
|
'/' +
|
|
event.target.value,
|
|
)
|
|
}
|
|
value={pathname.split('/').at(-1)}
|
|
>
|
|
<option value="core">Core</option>
|
|
<option value="metadata">Metadata</option>
|
|
</select>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default TokenForm;
|