191 lines
4.2 KiB
TypeScript
191 lines
4.2 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import { TbLoader2 } from 'react-icons/tb';
|
|
import styled from '@emotion/styled';
|
|
import { parseJson } from 'nx/src/utils/json';
|
|
|
|
export type TokenFormProps = {
|
|
setOpenApiJson?: (json: object) => void;
|
|
setToken?: (token: string) => void;
|
|
isTokenValid: boolean;
|
|
setIsTokenValid: (boolean: boolean) => void;
|
|
};
|
|
|
|
const Container = styled.div`
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
height: 90vh;
|
|
`;
|
|
|
|
const Form = styled.form`
|
|
text-align: center;
|
|
padding: 50px;
|
|
`;
|
|
|
|
const StyledLink = styled.a`
|
|
color: #16233f;
|
|
text-decoration: none;
|
|
position: relative;
|
|
font-weight: bold;
|
|
transition: color 0.3s ease;
|
|
|
|
[data-theme='dark'] & {
|
|
color: #a3c0f8;
|
|
}
|
|
`;
|
|
|
|
const Input = styled.input`
|
|
padding: 6px;
|
|
margin: 20px 0 5px 0;
|
|
max-width: 460px;
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
background-color: #f3f3f3;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
|
|
[data-theme='dark'] & {
|
|
background-color: #16233f;
|
|
}
|
|
|
|
&.invalid {
|
|
border: 1px solid #f83e3e;
|
|
}
|
|
`;
|
|
|
|
const TokenInvalid = styled.span`
|
|
color: #f83e3e;
|
|
font-size: 12px;
|
|
`;
|
|
|
|
const Loader = styled(TbLoader2)`
|
|
color: #16233f;
|
|
font-size: 2rem;
|
|
animation: animate 2s infinite;
|
|
|
|
[data-theme='dark'] & {
|
|
color: #a3c0f8;
|
|
}
|
|
|
|
&.not-visible {
|
|
visibility: hidden;
|
|
}
|
|
|
|
@keyframes animate {
|
|
0% {
|
|
transform: rotate(0deg);
|
|
}
|
|
100% {
|
|
transform: rotate(720deg);
|
|
}
|
|
}
|
|
`;
|
|
|
|
const LoaderContainer = styled.div`
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
height: 50px;
|
|
`;
|
|
|
|
const TokenForm = ({
|
|
setOpenApiJson,
|
|
setToken,
|
|
isTokenValid,
|
|
setIsTokenValid,
|
|
}: TokenFormProps) => {
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const token =
|
|
parseJson(localStorage.getItem('TryIt_securitySchemeValues') || '')
|
|
?.bearerAuth ?? '';
|
|
|
|
const updateToken = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
localStorage.setItem(
|
|
'TryIt_securitySchemeValues',
|
|
JSON.stringify({ bearerAuth: event.target.value }),
|
|
);
|
|
await submitToken(event.target.value);
|
|
};
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const validateToken = (openApiJson: any) =>
|
|
setIsTokenValid(!!openApiJson.tags);
|
|
|
|
const getJson = async (token: string) => {
|
|
setIsLoading(true);
|
|
|
|
return await fetch('https://api.twenty.com/open-api', {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
.then((res) => res.json())
|
|
.then((result) => {
|
|
validateToken(result);
|
|
setIsLoading(false);
|
|
|
|
return result;
|
|
})
|
|
.catch(() => setIsLoading(false));
|
|
};
|
|
|
|
const submitToken = async (token: string) => {
|
|
if (isLoading) return;
|
|
|
|
const json = await getJson(token);
|
|
|
|
setToken && setToken(token);
|
|
|
|
setOpenApiJson && setOpenApiJson(json);
|
|
};
|
|
|
|
useEffect(() => {
|
|
(async () => {
|
|
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 (
|
|
!isTokenValid && (
|
|
<Container>
|
|
<Form>
|
|
<label>
|
|
To load your playground schema,{' '}
|
|
<StyledLink href="https://app.twenty.com/settings/developers/api-keys">
|
|
generate an API key
|
|
</StyledLink>{' '}
|
|
and paste it here:
|
|
</label>
|
|
<p>
|
|
<Input
|
|
className={token && !isLoading ? 'input invalid' : 'input'}
|
|
type="text"
|
|
readOnly={isLoading}
|
|
placeholder="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMD..."
|
|
defaultValue={token}
|
|
onChange={updateToken}
|
|
/>
|
|
<TokenInvalid
|
|
className={`${(!token || isLoading) && 'not-visible'}`}
|
|
>
|
|
Token invalid
|
|
</TokenInvalid>
|
|
<LoaderContainer>
|
|
<Loader className={`${!isLoading && 'not-visible'}`} />
|
|
</LoaderContainer>
|
|
</p>
|
|
</Form>
|
|
</Container>
|
|
)
|
|
);
|
|
};
|
|
|
|
export default TokenForm;
|