New folder structure for website (#4159)

New folder structure
This commit is contained in:
Félix Malfait
2024-02-23 17:42:13 +01:00
committed by GitHub
parent 06c4665a44
commit 5de1c2c31d
65 changed files with 128 additions and 537 deletions

View File

@ -1,32 +0,0 @@
'use client';
import { ResponsiveTimeRange } from '@nivo/calendar';
import { CardContainer } from '@/app/developers/contributors/[slug]/components/CardContainer';
import { Title } from '@/app/developers/contributors/[slug]/components/Title';
export const ActivityLog = ({
data,
}: {
data: { value: number; day: string }[];
}) => {
if (!data.length) {
return null;
}
return (
<CardContainer>
<Title>Activity</Title>
<div style={{ width: '100%', height: '214px' }}>
<ResponsiveTimeRange
data={data}
emptyColor="#F4EFFF"
colors={['#E9DFFF', '#B28FFE', '#915FFD']}
dayBorderWidth={2}
dayBorderColor="#ffffff"
dayRadius={4}
daySpacing={2}
/>
</div>
</CardContainer>
);
};

View File

@ -1,16 +0,0 @@
'use client';
import { Breadcrumbs } from '@/app/components/Breadcrumbs';
const BREADCRUMB_ITEMS = [
{
uri: '/developers/contributors',
label: 'Contributors',
},
];
export const Breadcrumb = ({ active }: { active: string }) => {
return (
<Breadcrumbs items={BREADCRUMB_ITEMS} activePage={active} separator="/" />
);
};

View File

@ -1,17 +0,0 @@
import styled from '@emotion/styled';
export const CardContainer = styled.div`
border: 3px solid #141414;
width: 100%;
border-radius: 12px;
padding: 40px;
display: flex;
gap: 32px;
flex-direction: column;
background-color: #fafafa;
@media (max-width: 810px) {
gap: 16px;
padding: 24px;
}
`;

View File

@ -1,26 +0,0 @@
'use client';
import styled from '@emotion/styled';
const Container = styled.div`
display: flex;
flex-direction: column;
align-items: flex-start;
width: 100%;
max-width: 898px;
padding: 40px;
gap: 40px;
@media (max-width: 809px) {
width: 100%;
padding: 40px 24px 40px 24px;
}
`;
export const ContentContainer = ({
children,
}: {
children?: React.ReactNode;
}) => {
return <Container>{children}</Container>;
};

View File

@ -1,102 +0,0 @@
'use client';
import styled from '@emotion/styled';
import { format } from 'date-fns';
import { GithubIcon } from '@/app/components/Icons';
const ProfileContainer = styled.div`
display: flex;
gap: 32px;
width: 100%;
align-items: center;
`;
const Avatar = styled.div`
border: 3px solid #141414;
width: 96px;
height: 96px;
border-radius: 16px;
overflow: hidden;
flex-shrink: 0;
img {
width: 100%;
display: block;
height: 100%;
object-fit: cover;
}
`;
const Details = styled.div`
display: flex;
flex-direction: column;
gap: 4px;
.username {
font-size: 40px;
font-weight: 700;
line-height: 48px;
margin: 0;
display: flex;
gap: 12px;
@media (max-width: 810px) {
font-size: 24px;
line-height: 28.8px;
}
}
.duration {
font-size: 24px;
font-weight: 400;
color: #818181;
margin: 0;
@media (max-width: 810px) {
font-size: 20px;
line-height: 28px;
}
}
`;
const StyledGithubIcon = styled(GithubIcon)`
@media (max-width: 810px) {
width: 20px;
height: 20px;
}
`;
interface ProfileCardProps {
username: string;
avatarUrl: string;
firstContributionAt: string;
}
export const ProfileCard = ({
username,
avatarUrl,
firstContributionAt,
}: ProfileCardProps) => {
return (
<ProfileContainer>
<Avatar>
<img src={avatarUrl} alt={username} />
</Avatar>
<Details>
<h3 className="username">
@{username}
<a href={`https://github.com/${username}`} target="_blank">
<StyledGithubIcon size="M" color="rgba(0,0,0,1)" />
</a>
</h3>
{firstContributionAt && (
<p className="duration">
Contributing since{' '}
{format(new Date(firstContributionAt), 'MMMM yyyy')}
</p>
)}
</Details>
</ProfileContainer>
);
};

View File

@ -1,86 +0,0 @@
'use client';
import styled from '@emotion/styled';
import { CardContainer } from '@/app/developers/contributors/[slug]/components/CardContainer';
const Container = styled(CardContainer)`
flex-direction: row;
@media (max-width: 810px) {
flex-direction: column;
}
.item {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
flex-grow: 1;
flex-basis: 0;
.title {
font-size: 24px;
color: #b3b3b3;
margin: 0;
@media (max-width: 810px) {
font-size: 20px;
}
}
.value {
font-size: 56px;
font-weight: 700;
color: #474747;
@media (max-width: 810px) {
font-size: 32px;
}
}
}
.separator {
width: 2px;
background-color: #141414;
border-radius: 40px;
@media (max-width: 810px) {
width: 100%;
height: 2px;
}
}
`;
interface ProfileInfoProps {
mergedPRsCount: number;
rank: string;
activeDays: number;
}
export const ProfileInfo = ({
mergedPRsCount,
rank,
activeDays,
}: ProfileInfoProps) => {
return (
<>
<Container>
<div className="item">
<p className="title">Merged PR</p>
<span className="value">{mergedPRsCount}</span>
</div>
<div className="separator"></div>
<div className="item">
<p className="title">Rank</p>
<span className="value">{rank}%</span>
</div>
<div className="separator"></div>
<div className="item">
<p className="title">Active Days</p>
<span className="value">{activeDays}</span>
</div>
</Container>
</>
);
};

View File

@ -1,109 +0,0 @@
import { Tooltip } from 'react-tooltip';
import styled from '@emotion/styled';
import { format } from 'date-fns';
import { PullRequestIcon } from '@/app/components/Icons';
import { formatIntoRelativeDate } from '@/lib/utils';
const StyledTooltip = styled(Tooltip)``;
const Item = styled.div`
display: flex;
gap: 17px;
`;
const StyledTitle = styled.a`
font-size: 24px;
font-weight: 400;
color: #474747;
margin: 0;
text-decoration: none;
@media (max-width: 810px) {
font-size: 20px;
}
`;
const StyledPrLink = styled.a`
cursor: pointer;
text-decoration: none;
&:hover {
text-decoration: underline;
color: #474747;
}
`;
const StyledDescription = styled.div`
font-size: 20px;
line-height: 28px;
font-weight: 500;
color: #b3b3b3;
@media (max-width: 810px) {
font-size: 18px;
}
`;
const StyledPullRequestIcon = styled(PullRequestIcon)`
@media screen {
width: 20px;
height: 20px;
}
`;
interface PullRequestItemProps {
id: string;
title: string;
url: string;
createdAt: string;
mergedAt: string | null;
authorId: string;
}
export const PullRequestItem = ({
id,
title,
url,
createdAt,
mergedAt,
authorId,
}: PullRequestItemProps) => {
const prNumber = url.split('/').slice(-1)[0];
return (
<Item key={id}>
<div>
<StyledPullRequestIcon
color={mergedAt ? '#915FFD' : '#1A7F37'}
size="M"
/>
</div>
<div>
<StyledTitle href={url} target="_blank">
{title}
</StyledTitle>
<StyledDescription>
<StyledPrLink
href={'https://github.com/twentyhq/twenty/pull/' + prNumber}
target="__blank"
>
#{prNumber}
</StyledPrLink>{' '}
by {authorId} was {mergedAt ? `merged` : `opened`}{' '}
<span id={`date-${prNumber}`}>
{formatIntoRelativeDate(mergedAt ? mergedAt : createdAt)}
</span>
<StyledTooltip
anchorSelect={`#date-${prNumber}`}
content={format(
new Date(mergedAt ? mergedAt : createdAt),
'dd MMMM yyyy',
)}
clickable
noArrow
/>
</StyledDescription>
</div>
</Item>
);
};

View File

@ -1,44 +0,0 @@
'use client';
import styled from '@emotion/styled';
import { CardContainer } from '@/app/developers/contributors/[slug]/components/CardContainer';
import { PullRequestItem } from '@/app/developers/contributors/[slug]/components/PullRequestItem';
import { Title } from '@/app/developers/contributors/[slug]/components/Title';
const List = styled.div`
display: flex;
gap: 24px;
flex-direction: column;
`;
interface PullRequestsProps {
list: {
id: string;
title: string;
url: string;
createdAt: string;
mergedAt: string | null;
authorId: string;
}[];
}
export const PullRequests = ({ list }: PullRequestsProps) => {
return (
<CardContainer>
<Title>Pull Requests</Title>
<List>
{list.map((pr) => (
<PullRequestItem
key={pr.id}
id={pr.id}
title={pr.title}
url={pr.url}
createdAt={pr.createdAt}
mergedAt={pr.mergedAt}
authorId={pr.authorId}
/>
))}
</List>
</CardContainer>
);
};

View File

@ -1,38 +0,0 @@
'use client';
import styled from '@emotion/styled';
import { HeartIcon } from '@/app/components/Icons';
import { CardContainer } from '@/app/developers/contributors/[slug]/components/CardContainer';
const StyledTitle = styled.div`
display: flex;
font-size: 24px;
font-weight: 500;
gap: 8px;
@media (max-width: 810px) {
font-size: 20px;
}
`;
const StyledHeartIcon = styled(HeartIcon)`
@media (max-width: 810px) {
width: 16px !important;
height: 16px !important;
}
`;
interface ThankYouProps {
authorId: string;
}
export const ThankYou = ({ authorId }: ThankYouProps) => {
return (
<CardContainer>
<StyledTitle>
Thank you @{authorId} <StyledHeartIcon color="#333333" size="18px" />
</StyledTitle>
</CardContainer>
);
};

View File

@ -1,13 +0,0 @@
import styled from '@emotion/styled';
export const Title = styled.h3`
margin: 0;
font-size: 32px;
line-height: 41.6px;
font-weight: 500;
@media (max-width: 810px) {
font-size: 24px;
line-height: 31.2px;
}
`;

View File

@ -1,134 +0,0 @@
export const dynamic = 'force-dynamic';
import { Metadata } from 'next';
import { Background } from '@/app/components/oss-friends/Background';
import { ActivityLog } from '@/app/developers/contributors/[slug]/components/ActivityLog';
import { Breadcrumb } from '@/app/developers/contributors/[slug]/components/Breadcrumb';
import { ContentContainer } from '@/app/developers/contributors/[slug]/components/ContentContainer';
import { ProfileCard } from '@/app/developers/contributors/[slug]/components/ProfileCard';
import { ProfileInfo } from '@/app/developers/contributors/[slug]/components/ProfileInfo';
import { PullRequests } from '@/app/developers/contributors/[slug]/components/PullRequests';
import { ThankYou } from '@/app/developers/contributors/[slug]/components/ThankYou';
import { findAll } from '@/database/database';
import { pullRequestModel, userModel } from '@/database/model';
export function generateMetadata({
params,
}: {
params: { slug: string };
}): Metadata {
return {
title: params.slug + ' | Contributors',
};
}
export default async function ({ params }: { params: { slug: string } }) {
const contributors = await findAll(userModel);
const contributor = contributors.find(
(contributor) => contributor.id === params.slug,
);
if (!contributor) {
return;
}
const pullRequests = await findAll(pullRequestModel);
const mergedPullRequests = pullRequests
.filter((pr) => pr.mergedAt !== null)
.filter(
(pr) =>
![
'dependabot',
'cyborch',
'emilienchvt',
'Samox',
'charlesBochet',
'gitstart-app',
'thaisguigon',
'lucasbordeau',
'magrinj',
'Weiko',
'gitstart-twenty',
'bosiraphael',
'martmull',
'FelixMalfait',
'thomtrp',
'Bonapara',
'nimraahmed',
].includes(pr.authorId),
);
const contributorPullRequests = pullRequests.filter(
(pr) => pr.authorId === contributor.id,
);
const mergedContributorPullRequests = contributorPullRequests.filter(
(pr) => pr.mergedAt !== null,
);
const mergedContributorPullRequestsByContributor = mergedPullRequests.reduce(
(acc, pr) => {
acc[pr.authorId] = (acc[pr.authorId] || 0) + 1;
return acc;
},
{},
);
const mergedContributorPullRequestsByContributorArray = Object.entries(
mergedContributorPullRequestsByContributor,
)
.map(([authorId, value]) => ({ authorId, value }))
.sort((a, b) => b.value - a.value);
const contributorRank =
((mergedContributorPullRequestsByContributorArray.findIndex(
(contributor) => contributor.authorId === params.slug,
) +
1) /
contributors.length) *
100;
const pullRequestActivity = contributorPullRequests.reduce((acc, pr) => {
const date = new Date(pr.createdAt).toISOString().split('T')[0];
acc[date] = (acc[date] || 0) + 1;
return acc;
}, []);
const pullRequestActivityArray = Object.entries(pullRequestActivity)
.map(([day, value]) => ({ day, value }))
.sort((a, b) => new Date(a.day).getTime() - new Date(b.day).getTime());
return (
<>
<Background />
<ContentContainer>
<Breadcrumb active={contributor.id} />
<ProfileCard
username={contributor.id}
avatarUrl={contributor.avatarUrl}
firstContributionAt={pullRequestActivityArray[0]?.day}
/>
<ProfileInfo
mergedPRsCount={mergedContributorPullRequests.length}
rank={Math.ceil(Number(contributorRank)).toFixed(0)}
activeDays={pullRequestActivityArray.length}
/>
<ActivityLog data={pullRequestActivityArray} />
<PullRequests
list={
contributorPullRequests.slice(0, 9) as {
id: string;
title: string;
url: string;
createdAt: string;
mergedAt: string | null;
authorId: string;
}[]
}
/>
<ThankYou authorId={contributor.login} />
</ContentContainer>
</>
);
}

View File

@ -1,335 +0,0 @@
export const dynamic = 'force-dynamic';
import { global } from '@apollo/client/utilities/globals';
import { graphql } from '@octokit/graphql';
import { insertMany, migrate } from '@/database/database';
import {
issueLabelModel,
issueModel,
labelModel,
pullRequestLabelModel,
pullRequestModel,
userModel,
} from '@/database/model';
interface LabelNode {
id: string;
name: string;
color: string;
description: string;
}
export interface AuthorNode {
resourcePath: string;
login: string;
avatarUrl: string;
url: string;
}
interface PullRequestNode {
id: string;
title: string;
body: string;
url: string;
createdAt: string;
updatedAt: string;
closedAt: string;
mergedAt: string;
author: AuthorNode;
labels: {
nodes: LabelNode[];
};
}
interface IssueNode {
id: string;
title: string;
body: string;
url: string;
createdAt: string;
updatedAt: string;
closedAt: string;
author: AuthorNode;
labels: {
nodes: LabelNode[];
};
}
interface PageInfo {
hasNextPage: boolean;
endCursor: string | null;
}
interface PullRequests {
nodes: PullRequestNode[];
pageInfo: PageInfo;
}
interface Issues {
nodes: IssueNode[];
pageInfo: PageInfo;
}
interface AssignableUserNode {
login: string;
}
interface AssignableUsers {
nodes: AssignableUserNode[];
}
interface RepoData {
repository: {
pullRequests: PullRequests;
issues: Issues;
assignableUsers: AssignableUsers;
};
}
const query = graphql.defaults({
headers: {
Authorization: 'bearer ' + global.process.env.GITHUB_TOKEN,
},
});
async function fetchData(
cursor: string | null = null,
isIssues: boolean = false,
accumulatedData: Array<PullRequestNode | IssueNode> = [],
): Promise<Array<PullRequestNode | IssueNode>> {
const { repository } = await query<RepoData>(
`
query ($cursor: String) {
repository(owner: "twentyhq", name: "twenty") {
pullRequests(first: 100, after: $cursor, orderBy: {field: CREATED_AT, direction: DESC}) @skip(if: ${isIssues}) {
nodes {
id
title
body
url
createdAt
updatedAt
closedAt
mergedAt
author {
resourcePath
login
avatarUrl(size: 460)
url
}
labels(first: 100) {
nodes {
id
name
color
description
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
issues(first: 100, after: $cursor, orderBy: {field: CREATED_AT, direction: DESC}) @skip(if: ${!isIssues}) {
nodes {
id
title
body
url
createdAt
updatedAt
closedAt
author {
resourcePath
login
avatarUrl
url
}
labels(first: 100) {
nodes {
id
name
color
description
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
`,
{ cursor },
);
const newAccumulatedData: Array<PullRequestNode | IssueNode> = [
...accumulatedData,
...(isIssues ? repository.issues.nodes : repository.pullRequests.nodes),
];
const pageInfo = isIssues
? repository.issues.pageInfo
: repository.pullRequests.pageInfo;
if (pageInfo.hasNextPage) {
return fetchData(pageInfo.endCursor, isIssues, newAccumulatedData);
} else {
return newAccumulatedData;
}
}
async function fetchAssignableUsers(): Promise<Set<string>> {
const { repository } = await query<RepoData>(`
query {
repository(owner: "twentyhq", name: "twenty") {
assignableUsers(first: 100) {
nodes {
login
}
}
}
}
`);
return new Set(repository.assignableUsers.nodes.map((user) => user.login));
}
export async function GET() {
if (!global.process.env.GITHUB_TOKEN) {
return new Response('No GitHub token provided', { status: 500 });
}
await migrate();
// TODO if we ever hit API Rate Limiting
const lastPRCursor = null;
const lastIssueCursor = null;
const assignableUsers = await fetchAssignableUsers();
const fetchedPRs = (await fetchData(lastPRCursor)) as Array<PullRequestNode>;
const fetchedIssues = (await fetchData(
lastIssueCursor,
true,
)) as Array<IssueNode>;
for (const pr of fetchedPRs) {
if (pr.author == null) {
continue;
}
await insertMany(
userModel,
[
{
id: pr.author.login,
avatarUrl: pr.author.avatarUrl,
url: pr.author.url,
isEmployee: assignableUsers.has(pr.author.login) ? '1' : '0',
},
],
{ onConflictKey: 'id' },
);
await insertMany(
pullRequestModel,
[
{
id: pr.id,
title: pr.title,
body: pr.body,
url: pr.url,
createdAt: pr.createdAt,
updatedAt: pr.updatedAt,
closedAt: pr.closedAt,
mergedAt: pr.mergedAt,
authorId: pr.author.login,
},
],
{ onConflictKey: 'id' },
);
for (const label of pr.labels.nodes) {
await insertMany(
labelModel,
[
{
id: label.id,
name: label.name,
color: label.color,
description: label.description,
},
],
{ onConflictKey: 'id' },
);
await insertMany(pullRequestLabelModel, [
{
pullRequestId: pr.id,
labelId: label.id,
},
]);
}
}
for (const issue of fetchedIssues) {
if (issue.author == null) {
continue;
}
await insertMany(
userModel,
[
{
id: issue.author.login,
avatarUrl: issue.author.avatarUrl,
url: issue.author.url,
isEmployee: assignableUsers.has(issue.author.login) ? '1' : '0',
},
],
{ onConflictKey: 'id' },
);
await insertMany(
issueModel,
[
{
id: issue.id,
title: issue.title,
body: issue.body,
url: issue.url,
createdAt: issue.createdAt,
updatedAt: issue.updatedAt,
closedAt: issue.closedAt,
authorId: issue.author.login,
},
],
{ onConflictKey: 'id' },
);
for (const label of issue.labels.nodes) {
await insertMany(
labelModel,
[
{
id: label.id,
name: label.name,
color: label.color,
description: label.description,
},
],
{ onConflictKey: 'id' },
);
await insertMany(issueLabelModel, [
{
pullRequestId: issue.id,
labelId: label.id,
},
]);
}
}
return new Response('Data synced', {
status: 200,
});
}

View File

@ -1,58 +0,0 @@
export const dynamic = 'force-dynamic';
import AvatarGrid from '@/app/components/AvatarGrid';
import { Header } from '@/app/components/developers/contributors/Header';
import { Background } from '@/app/components/oss-friends/Background';
import { ContentContainer } from '@/app/components/oss-friends/ContentContainer';
import { findAll } from '@/database/database';
import { pullRequestModel, userModel } from '@/database/model';
interface Contributor {
id: string;
avatarUrl: string;
}
const Contributors = async () => {
const contributors = await findAll(userModel);
const pullRequests = await findAll(pullRequestModel);
const pullRequestByAuthor = pullRequests.reduce((acc, pr) => {
acc[pr.authorId] = acc[pr.authorId] ? acc[pr.authorId] + 1 : 1;
return acc;
}, {});
const fitlerContributors = contributors
.filter((contributor) => contributor.isEmployee === '0')
.filter(
(contributor) =>
![
'dependabot',
'cyborch',
'emilienchvt',
'Samox',
'nimraahmed',
'gitstart-app',
].includes(contributor.id),
)
.map((contributor) => {
contributor.pullRequestCount = pullRequestByAuthor[contributor.id] || 0;
return contributor;
})
.sort((a, b) => b.pullRequestCount - a.pullRequestCount)
.filter((contributor) => contributor.pullRequestCount > 0);
return (
<>
<Background />
<ContentContainer>
<Header />
<div>
<AvatarGrid users={fitlerContributors as Contributor[]} />
</div>
</ContentContainer>
</>
);
};
export default Contributors;

View File

@ -1,27 +0,0 @@
'use client';
import { createGraphiQLFetcher } from '@graphiql/toolkit';
import { GraphiQL } from 'graphiql';
import dynamic from 'next/dynamic';
import 'graphiql/graphiql.css';
// Create a named function for your component
function GraphiQLComponent() {
const fetcher = createGraphiQLFetcher({
url: 'https://api.twenty.com/graphql',
});
return <GraphiQL fetcher={fetcher} />;
}
// Dynamically import the GraphiQL component with SSR disabled
const GraphiQLWithNoSSR = dynamic(() => Promise.resolve(GraphiQLComponent), {
ssr: false,
});
const GraphQLDocs = () => {
return <GraphiQLWithNoSSR />;
};
export default GraphQLDocs;

View File

@ -1,63 +0,0 @@
import { ContentContainer } from '@/app/components/ContentContainer';
const DeveloperDocsLayout = ({ children }: { children: React.ReactNode }) => {
return (
<ContentContainer>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<div
style={{
borderRight: '1px solid rgba(20, 20, 20, 0.08)',
paddingRight: '24px',
minWidth: '200px',
paddingTop: '48px',
}}
>
<h4 style={{ textTransform: 'uppercase', color: '#B3B3B3' }}>
Install & Maintain
</h4>
<a style={{ textDecoration: 'none', color: '#333' }} href="/">
Local setup
</a>{' '}
<br />
<a style={{ textDecoration: 'none', color: '#333' }} href="/">
Self-hosting
</a>{' '}
<br />
<a style={{ textDecoration: 'none', color: '#333' }} href="/">
Upgrade guide
</a>{' '}
<br /> <br />
<h4 style={{ textTransform: 'uppercase', color: '#B3B3B3' }}>
Resources
</h4>
<a style={{ textDecoration: 'none', color: '#333' }} href="/">
Contributors Guide
</a>{' '}
<br />
<a
style={{ textDecoration: 'none', color: '#333' }}
href="/developers/docs/graphql"
>
GraphQL API
</a>{' '}
<br />
<a
style={{ textDecoration: 'none', color: '#333', display: 'flex' }}
href="/developers/rest"
>
Rest API
</a>
<a style={{ textDecoration: 'none', color: '#333' }} href="/">
Twenty UI
</a>{' '}
<br />
</div>
<div style={{ padding: '24px', minHeight: '80vh', width: '100%' }}>
{children}
</div>
</div>
</ContentContainer>
);
};
export default DeveloperDocsLayout;

View File

@ -1,9 +0,0 @@
const DeveloperDocs = () => {
return (
<div>
<h1>Developer Docs</h1>
</div>
);
};
export default DeveloperDocs;

View File

@ -1,33 +0,0 @@
'use client';
/*import { API } from '@stoplight/elements';/
import '@stoplight/elements/styles.min.css';
/*
const RestApiComponent = ({ openApiJson }: { openApiJson: any }) => {
// We load spotlightTheme style using useEffect as it breaks remaining docs style
useEffect(() => {
const styleElement = document.createElement('style');
// styleElement.innerHTML = spotlightTheme.toString();
document.head.append(styleElement);
return () => styleElement.remove();
}, []);
return (
<API apiDescriptionDocument={JSON.stringify(openApiJson)} router="hash" />
);
};*/
const RestApi = () => {
/* const [openApiJson, setOpenApiJson] = useState({});
const children = <RestApiComponent openApiJson={openApiJson} />;*/
return <>API</>;
// return <Playground setOpenApiJson={setOpenApiJson}>{children}</Playground>;
};
export default RestApi;

View File

@ -1,9 +0,0 @@
const Developers = () => {
return (
<div>
<p>This page should probably be built on Framer</p>
</div>
);
};
export default Developers;