Files
twenty/packages/twenty-website/src/app/_components/docs/DocsSidebarSection.tsx
Ady Beraud 671de4170f Migrated Developer Docs (#5683)
- Migrated developer docs to Twenty website

- Modified User Guide and Docs layout to include sections and
subsections

**Section Example:**
<img width="549" alt="Screenshot 2024-05-30 at 15 44 42"
src="https://github.com/twentyhq/twenty/assets/102751374/41bd4037-4b76-48e6-bc79-48d3d6be9ab8">

**Subsection Example:**
<img width="557" alt="Screenshot 2024-05-30 at 15 44 55"
src="https://github.com/twentyhq/twenty/assets/102751374/f14c65a9-ab0c-4530-b624-5b20fc00511a">


- Created different components (Tabs, Tables, Editors etc.) for the mdx
files

**Tabs & Editor**

<img width="665" alt="Screenshot 2024-05-30 at 15 47 39"
src="https://github.com/twentyhq/twenty/assets/102751374/5166b5c7-b6cf-417d-9f29-b1f674c1c531">

**Tables**

<img width="698" alt="Screenshot 2024-05-30 at 15 57 39"
src="https://github.com/twentyhq/twenty/assets/102751374/2bbfe937-ec19-4004-ab00-f7a56e96db4a">

<img width="661" alt="Screenshot 2024-05-30 at 16 03 32"
src="https://github.com/twentyhq/twenty/assets/102751374/ae95b47c-dd92-44f9-b535-ccdc953f71ff">

- Created a crawler for Twenty Developers (now that it will be on the
twenty website). Once this PR is merged and the website is re-deployed,
we need to start crawling and make sure the index name is
‘twenty-developer’
- Added a dropdown menu in the header to access User Guide and
Developers + added Developers to footer


https://github.com/twentyhq/twenty/assets/102751374/1bd1fbbd-1e65-4461-b18b-84d4ddbb8ea1

- Made new layout responsive

Please fill in the information for each mdx file so that it can appear
on its card, as well as in the ‘In this article’ section. Example with
‘Getting Started’ in the User Guide:

<img width="786" alt="Screenshot 2024-05-30 at 16 29 39"
src="https://github.com/twentyhq/twenty/assets/102751374/2714b01d-a664-4ddc-9291-528632ee12ea">

Example with info and sectionInfo filled in for 'Getting Started':

<img width="620" alt="Screenshot 2024-05-30 at 16 33 57"
src="https://github.com/twentyhq/twenty/assets/102751374/bc69e880-da6a-4b7e-bace-1effea866c11">


Please keep in mind that the images that are being used for Developers
are the same as those found in User Guide and may not match the article.

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2024-06-03 18:52:43 +02:00

215 lines
6.2 KiB
TypeScript

'use client';
import { useEffect, useState } from 'react';
import styled from '@emotion/styled';
import { IconPoint } from '@tabler/icons-react';
import { usePathname, useRouter } from 'next/navigation';
import { IconChevronDown, IconChevronRight } from '@/app/_components/ui/icons';
import { Theme } from '@/app/_components/ui/theme/theme';
import { DocsArticlesProps } from '@/content/user-guide/constants/getDocsArticles';
import { groupArticlesByTopic } from '@/content/user-guide/constants/groupArticlesByTopic';
import { getCardPath } from '@/shared-utils/getCardPath';
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
`;
const StyledIndex = styled.div`
margin-bottom: 8px;
`;
const StyledTitle = styled.div`
cursor: pointer;
display: flex;
flex-direction: row;
align-items: center;
gap: ${Theme.spacing(2)};
color: ${Theme.text.color.quarternary};
margin-top: 8px;
padding-bottom: ${Theme.spacing(2)};
font-family: ${Theme.font.family};
font-size: ${Theme.font.size.xs};
font-weight: 600;
`;
const StyledSubTopicItem = styled.a<{ isselected: boolean }>`
cursor: pointer;
display: flex;
flex-direction: row;
align-items: center;
height: ${Theme.spacing(8)};
color: ${(props) =>
props.isselected ? Theme.text.color.primary : Theme.text.color.secondary};
font-weight: ${(props) =>
props.isselected ? Theme.font.weight.medium : Theme.font.weight.regular};
font-family: ${Theme.font.family};
font-size: ${Theme.font.size.xs};
gap: 19px;
padding: ${(props) =>
props.isselected ? '6px 12px 6px 11px' : '0px 12px 0px 11px'};
background: ${(props) =>
props.isselected
? Theme.background.transparent.light
: Theme.background.secondary};
border-radius: ${Theme.border.radius.md};
text-decoration: none;
&:focus,
&:hover,
&:visited,
&:link,
&:active {
text-decoration: none;
}
&:hover {
background: #1414140a;
}
&:active {
background: #1414140f;
}
`;
const StyledIcon = styled.div`
padding: 0px 4px 0px 4px;
display: flex;
align-items: center;
`;
const StyledIconContainer = styled.div`
margin-top: 3px;
margin-left: -8px;
color: ${Theme.color.gray30};
`;
const StyledCardTitle = styled.p`
margin: 0px -5px;
color: ${Theme.color.gray30};
font-weight: 600;
`;
const StyledRectangle = styled.div<{ isselected: boolean; isHovered: boolean }>`
height: ${(props) =>
props.isselected ? '95%' : props.isHovered ? '70%' : '100%'};
width: 2px;
background: ${(props) =>
props.isselected
? Theme.border.color.plain
: props.isHovered
? Theme.background.transparent.strong
: Theme.background.transparent.light};
transition: height 0.2s ease-in-out;
`;
interface TopicsState {
[topic: string]: boolean;
}
const DocsSidebarSection = ({
docsIndex,
}: {
docsIndex: DocsArticlesProps[];
}) => {
const pathname = usePathname();
const router = useRouter();
const topics = groupArticlesByTopic(docsIndex);
const [hoveredItem, setHoveredItem] = useState<string | null>(null);
const path = pathname.includes('user-guide')
? '/user-guide/'
: pathname.includes('developers')
? '/developers/'
: '/twenty-ui';
const initializeUnfoldedState = () => {
const unfoldedState: TopicsState = {};
Object.keys(topics).forEach((topic) => {
const containsCurrentArticle = topics[topic].some((card) => {
const topicPath = card.topic.toLowerCase().replace(/\s+/g, '-');
return pathname.includes(topicPath);
});
unfoldedState[topic] = containsCurrentArticle;
});
return unfoldedState;
};
const [unfolded, setUnfolded] = useState<TopicsState>(
initializeUnfoldedState,
);
useEffect(() => {
const newUnfoldedState = initializeUnfoldedState();
setUnfolded(newUnfoldedState);
}, [pathname]);
const toggleFold = (topic: string) => {
setUnfolded((prev: TopicsState) => ({ ...prev, [topic]: !prev[topic] }));
};
return (
<StyledContainer>
{Object.entries(topics).map(([topic, cards]) => {
const hasMultipleFiles = cards.some((card) => card.numberOfFiles > 1);
return (
<StyledIndex key={topic}>
{hasMultipleFiles ? (
<StyledTitle onClick={() => toggleFold(topic)}>
{unfolded[topic] ? (
<StyledIcon>
<IconChevronDown size={Theme.icon.size.md} />
</StyledIcon>
) : (
<StyledIcon>
<IconChevronRight size={Theme.icon.size.md} />
</StyledIcon>
)}
<div>{topic}</div>
</StyledTitle>
) : null}
{(unfolded[topic] || !hasMultipleFiles) &&
cards.map((card) => {
const isselected = pathname === `${path}${card.fileName}`;
const sectionName = card.topic
.toLowerCase()
.replace(/\s+/g, '-');
const routerPath = getCardPath(card, path, false, sectionName);
return (
<StyledSubTopicItem
key={card.title}
isselected={isselected}
href={routerPath}
onClick={() => router.push(routerPath)}
onMouseEnter={() => setHoveredItem(card.title)}
onMouseLeave={() => setHoveredItem(null)}
>
{card.numberOfFiles > 1 ? (
<>
<StyledRectangle
isselected={isselected}
isHovered={hoveredItem === card.title}
/>
{card.title}
</>
) : (
<>
<StyledIconContainer>
<IconPoint size={Theme.icon.size.md} />
</StyledIconContainer>
<StyledCardTitle>{card.title}</StyledCardTitle>
</>
)}
</StyledSubTopicItem>
);
})}
</StyledIndex>
);
})}
</StyledContainer>
);
};
export default DocsSidebarSection;