@ -0,0 +1,48 @@
|
||||
'use client';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import { Theme } from '@/app/_components/ui/theme/theme';
|
||||
import { UserGuideHomeCardsType } from '@/content/user-guide/constants/UserGuideHomeCards';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
color: ${Theme.border.color.plain};
|
||||
border: 2px solid ${Theme.border.color.plain};
|
||||
border-radius: ${Theme.border.radius.md};
|
||||
padding: ${Theme.spacing(6)};
|
||||
gap: ${Theme.spacing(4)};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
cursor: pointer;
|
||||
width: 348px;
|
||||
`;
|
||||
|
||||
const StyledHeading = styled.div`
|
||||
font-size: ${Theme.font.size.lg};
|
||||
color: ${Theme.text.color.primary};
|
||||
`;
|
||||
|
||||
const StyledSubHeading = styled.div`
|
||||
font-size: ${Theme.font.size.sm};
|
||||
color: ${Theme.text.color.secondary};
|
||||
font-family: ${Theme.font.family};
|
||||
`;
|
||||
|
||||
const StyledImage = styled.img`
|
||||
width: 300px;
|
||||
`;
|
||||
|
||||
export default function UserGuideCard({
|
||||
card,
|
||||
}: {
|
||||
card: UserGuideHomeCardsType;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
return (
|
||||
<StyledContainer onClick={() => router.push(`/user-guide/${card.url}`)}>
|
||||
<StyledImage src={card.image} alt={card.title} />
|
||||
<StyledHeading>{card.title}</StyledHeading>
|
||||
<StyledSubHeading>{card.subtitle}</StyledSubHeading>
|
||||
</StyledContainer>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,109 @@
|
||||
'use client';
|
||||
import React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import {
|
||||
DeviceType,
|
||||
useDeviceType,
|
||||
} from '@/app/_components/client-utils/useDeviceType';
|
||||
import { Breadcrumbs } from '@/app/_components/ui/layout/Breadcrumbs';
|
||||
import { Theme } from '@/app/_components/ui/theme/theme';
|
||||
import { FileContent } from '@/app/_server-utils/get-posts';
|
||||
|
||||
const StyledContainer = styled.div<{ devicetype: string }>`
|
||||
width: ${({ devicetype }) =>
|
||||
devicetype === DeviceType.TABLET
|
||||
? '70%'
|
||||
: devicetype === DeviceType.DESKTOP
|
||||
? '60%'
|
||||
: '100%'};
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
font-family: ${Theme.font.family};
|
||||
border-bottom: 1px solid ${Theme.background.transparent.medium};
|
||||
`;
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
width: 79.3%;
|
||||
padding: ${Theme.spacing(10)} 0px ${Theme.spacing(20)} 0px;
|
||||
`;
|
||||
|
||||
const StyledHeader = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${Theme.spacing(8)};
|
||||
`;
|
||||
|
||||
const StyledHeading = styled.div`
|
||||
font-size: 40px;
|
||||
font-weight: 700;
|
||||
`;
|
||||
|
||||
const StyledHeaderInfoSection = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${Theme.spacing(4)};
|
||||
`;
|
||||
|
||||
const StyledHeaderInfoSectionTitle = styled.div`
|
||||
font-size: ${Theme.font.size.sm};
|
||||
padding: ${Theme.spacing(2)} 0px;
|
||||
color: ${Theme.text.color.secondary};
|
||||
font-weight: ${Theme.font.weight.medium};
|
||||
`;
|
||||
|
||||
const StyledHeaderInfoSectionSub = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${Theme.spacing(4)};
|
||||
color: ${Theme.text.color.tertiary};
|
||||
font-family: ${Theme.font.family};
|
||||
`;
|
||||
|
||||
const StyledRectangle = styled.div`
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: ${Theme.background.transparent.medium};
|
||||
`;
|
||||
|
||||
export default function UserGuideContent({ item }: { item: FileContent }) {
|
||||
const BREADCRUMB_ITEMS = [
|
||||
{
|
||||
uri: '/user-guide',
|
||||
label: 'User Guide',
|
||||
},
|
||||
];
|
||||
const deviceType = useDeviceType();
|
||||
return (
|
||||
<StyledContainer devicetype={deviceType}>
|
||||
<StyledWrapper>
|
||||
<StyledHeader>
|
||||
<Breadcrumbs
|
||||
items={BREADCRUMB_ITEMS}
|
||||
activePage={item.itemInfo.title}
|
||||
separator="/"
|
||||
/>
|
||||
<StyledHeading>{item.itemInfo.title}</StyledHeading>
|
||||
{item.itemInfo.image && (
|
||||
<img
|
||||
id={`img-${item.itemInfo.title}`}
|
||||
src={item.itemInfo.image}
|
||||
alt={item.itemInfo.title}
|
||||
/>
|
||||
)}
|
||||
<StyledHeaderInfoSection>
|
||||
<StyledHeaderInfoSectionTitle>
|
||||
In this article
|
||||
</StyledHeaderInfoSectionTitle>
|
||||
<StyledHeaderInfoSectionSub>
|
||||
{item.itemInfo.info}
|
||||
</StyledHeaderInfoSectionSub>
|
||||
</StyledHeaderInfoSection>
|
||||
<StyledRectangle />
|
||||
</StyledHeader>
|
||||
<div>{item.content}</div>
|
||||
</StyledWrapper>
|
||||
</StyledContainer>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,93 @@
|
||||
'use client';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import {
|
||||
DeviceType,
|
||||
useDeviceType,
|
||||
} from '@/app/_components/client-utils/useDeviceType';
|
||||
import { Theme } from '@/app/_components/ui/theme/theme';
|
||||
import UserGuideCard from '@/app/_components/user-guide/UserGuideCard';
|
||||
import { UserGuideHomeCards } from '@/content/user-guide/constants/UserGuideHomeCards';
|
||||
|
||||
const StyledContainer = styled.div<{ isMobile: boolean }>`
|
||||
width: ${({ isMobile }) => (isMobile ? '100%' : '60%')};
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
width: 79.3%;
|
||||
padding: ${Theme.spacing(10)} 0px ${Theme.spacing(20)} 0px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${Theme.spacing(8)};
|
||||
`;
|
||||
|
||||
const StyledHeader = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0px;
|
||||
`;
|
||||
|
||||
const StyledHeading = styled.h1`
|
||||
line-height: 38px;
|
||||
font-weight: 700;
|
||||
font-size: 38px;
|
||||
color: ${Theme.text.color.primary};
|
||||
margin: 0px;
|
||||
`;
|
||||
|
||||
const StyledSubHeading = styled.h1`
|
||||
line-height: 12px;
|
||||
font-family: ${Theme.font.family};
|
||||
font-size: ${Theme.font.size.sm};
|
||||
font-weight: ${Theme.font.weight.regular};
|
||||
color: ${Theme.text.color.tertiary};
|
||||
`;
|
||||
|
||||
const StyledContentGrid = styled.div`
|
||||
width: 100%;
|
||||
padding-top: ${Theme.spacing(6)};
|
||||
display: grid;
|
||||
grid-template-rows: auto auto;
|
||||
grid-template-columns: auto auto;
|
||||
gap: ${Theme.spacing(6)};
|
||||
`;
|
||||
|
||||
const StyledContentFlex = styled.div`
|
||||
width: 100%;
|
||||
padding-top: ${Theme.spacing(6)};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${Theme.spacing(6)};
|
||||
`;
|
||||
|
||||
export default function UserGuideMain() {
|
||||
const deviceType = useDeviceType();
|
||||
return (
|
||||
<StyledContainer isMobile={deviceType === DeviceType.MOBILE}>
|
||||
<StyledWrapper>
|
||||
<StyledHeader>
|
||||
<StyledHeading>User Guide</StyledHeading>
|
||||
<StyledSubHeading>
|
||||
A brief guide to grasp the basics of Twenty
|
||||
</StyledSubHeading>
|
||||
</StyledHeader>
|
||||
{deviceType === DeviceType.DESKTOP ? (
|
||||
<StyledContentGrid>
|
||||
{UserGuideHomeCards.map((card) => {
|
||||
return <UserGuideCard key={card.title} card={card} />;
|
||||
})}
|
||||
</StyledContentGrid>
|
||||
) : (
|
||||
<StyledContentFlex>
|
||||
{UserGuideHomeCards.map((card) => {
|
||||
return <UserGuideCard key={card.title} card={card} />;
|
||||
})}
|
||||
</StyledContentFlex>
|
||||
)}
|
||||
</StyledWrapper>
|
||||
</StyledContainer>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
'use client';
|
||||
|
||||
import styled from '@emotion/styled';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import {
|
||||
DeviceType,
|
||||
useDeviceType,
|
||||
} from '@/app/_components/client-utils/useDeviceType';
|
||||
import { IconBook } from '@/app/_components/ui/icons';
|
||||
import { Theme } from '@/app/_components/ui/theme/theme';
|
||||
import UserGuideSidebarSection from '@/app/_components/user-guide/UserGuideSidebarSection';
|
||||
import { UserGuideIndex } from '@/content/user-guide/constants/UserGuideIndex';
|
||||
|
||||
const StyledContainer = styled.div<{ isTablet: boolean }>`
|
||||
width: ${({ isTablet }) => (isTablet ? '30%' : '20%')};
|
||||
background: ${Theme.background.secondary};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-right: 1px solid ${Theme.background.transparent.medium};
|
||||
border-bottom: 1px solid ${Theme.background.transparent.medium};
|
||||
padding: ${Theme.spacing(10)} ${Theme.spacing(3)};
|
||||
gap: ${Theme.spacing(6)};
|
||||
`;
|
||||
|
||||
const StyledHeading = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: ${Theme.spacing(2)};
|
||||
font-size: ${Theme.font.size.sm};
|
||||
font-weight: ${Theme.font.weight.medium};
|
||||
`;
|
||||
|
||||
const StyledIconContainer = styled.div`
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
font-size: ${Theme.font.size.sm};
|
||||
font-weight: ${Theme.font.weight.medium};
|
||||
color: ${Theme.text.color.secondary};
|
||||
border: 1px solid ${Theme.text.color.secondary};
|
||||
border-radius: ${Theme.border.radius.sm};
|
||||
padding: ${Theme.spacing(1)} ${Theme.spacing(1)} ${Theme.spacing(1)}
|
||||
${Theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledHeadingText = styled.div`
|
||||
cursor: pointer;
|
||||
font-size: ${Theme.font.size.sm};
|
||||
font-weight: ${Theme.font.weight.medium};
|
||||
`;
|
||||
|
||||
const UserGuideSidebar = () => {
|
||||
const router = useRouter();
|
||||
const isTablet = useDeviceType() === DeviceType.TABLET;
|
||||
return (
|
||||
<StyledContainer isTablet={isTablet}>
|
||||
<StyledHeading>
|
||||
<StyledIconContainer>
|
||||
<IconBook size={Theme.icon.size.md} />
|
||||
</StyledIconContainer>
|
||||
<StyledHeadingText onClick={() => router.push('/user-guide')}>
|
||||
User Guide
|
||||
</StyledHeadingText>
|
||||
</StyledHeading>
|
||||
{Object.entries(UserGuideIndex).map(([heading, subtopics]) => (
|
||||
<UserGuideSidebarSection
|
||||
key={heading}
|
||||
title={heading}
|
||||
subTopics={subtopics}
|
||||
/>
|
||||
))}
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserGuideSidebar;
|
||||
@ -0,0 +1,114 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
import { IconChevronDown, IconChevronRight } from '@/app/_components/ui/icons';
|
||||
import { Theme } from '@/app/_components/ui/theme/theme';
|
||||
import { IndexSubtopic } from '@/content/user-guide/constants/UserGuideIndex';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
const StyledTitle = styled.div`
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: ${Theme.spacing(2)};
|
||||
color: ${Theme.text.color.quarternary};
|
||||
padding-bottom: ${Theme.spacing(2)};
|
||||
font-family: ${Theme.font.family};
|
||||
font-size: ${Theme.font.size.xs};
|
||||
`;
|
||||
|
||||
const StyledSubTopicItem = styled.div<{ 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;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledIcon = styled.div`
|
||||
padding: 0px 4px 0px 4px;
|
||||
`;
|
||||
|
||||
const StyledRectangle = styled.div<{ isselected: boolean }>`
|
||||
height: 100%;
|
||||
width: 2px;
|
||||
background: ${(props) =>
|
||||
props.isselected
|
||||
? Theme.border.color.plain
|
||||
: Theme.background.transparent.light};
|
||||
`;
|
||||
|
||||
const UserGuideSidebarSection = ({
|
||||
title,
|
||||
subTopics,
|
||||
}: {
|
||||
title: string;
|
||||
subTopics: IndexSubtopic[];
|
||||
}) => {
|
||||
const [isUnfolded, setUnfoldedState] = useState(true);
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StyledTitle onClick={() => setUnfoldedState(!isUnfolded)}>
|
||||
{isUnfolded ? (
|
||||
<StyledIcon>
|
||||
<IconChevronDown size={Theme.icon.size.md} />
|
||||
</StyledIcon>
|
||||
) : (
|
||||
<StyledIcon>
|
||||
<IconChevronRight size={Theme.icon.size.md} />
|
||||
</StyledIcon>
|
||||
)}
|
||||
<div>{title}</div>
|
||||
</StyledTitle>
|
||||
{isUnfolded &&
|
||||
subTopics.map((subtopic, index) => {
|
||||
const isselected = pathname === `/user-guide/${subtopic.url}`;
|
||||
return (
|
||||
<StyledSubTopicItem
|
||||
key={index}
|
||||
isselected={isselected}
|
||||
onClick={() => router.push(`/user-guide/${subtopic.url}`)}
|
||||
>
|
||||
<StyledRectangle isselected={isselected} />
|
||||
{subtopic.title}
|
||||
</StyledSubTopicItem>
|
||||
);
|
||||
})}
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserGuideSidebarSection;
|
||||
@ -0,0 +1,41 @@
|
||||
'use client';
|
||||
|
||||
import styled from '@emotion/styled';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import { Theme } from '@/app/_components/ui/theme/theme';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
width: 20%;
|
||||
background: ${Theme.background.secondary};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-left: 1px solid ${Theme.background.transparent.medium};
|
||||
border-bottom: 1px solid ${Theme.background.transparent.medium};
|
||||
padding: ${Theme.spacing(10)} ${Theme.spacing(6)};
|
||||
gap: ${Theme.spacing(6)};
|
||||
`;
|
||||
|
||||
const StyledContent = styled.div`
|
||||
position: fixed;
|
||||
`;
|
||||
|
||||
const StyledHeadingText = styled.div`
|
||||
font-size: ${Theme.font.size.sm};
|
||||
color: ${Theme.text.color.quarternary};
|
||||
`;
|
||||
|
||||
const UserGuideTableContents = () => {
|
||||
const router = useRouter();
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StyledContent>
|
||||
<StyledHeadingText onClick={() => router.push('/user-guide')}>
|
||||
Table of Contents
|
||||
</StyledHeadingText>
|
||||
</StyledContent>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserGuideTableContents;
|
||||
@ -0,0 +1,21 @@
|
||||
interface Heading {
|
||||
id: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
const UserGuideTocComponent = ({ headings }: { headings: Heading[] }) => {
|
||||
return (
|
||||
<div>
|
||||
<h2>Table of Contents</h2>
|
||||
<ul>
|
||||
{headings.map((heading, index) => (
|
||||
<li key={index}>
|
||||
<a href={`#${heading.id}`}>{heading.value}</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserGuideTocComponent;
|
||||
Reference in New Issue
Block a user