Marketing website improvements (#3169)
* Website improvement * Improve website design * Start writing script for user guide * Begin adding user guide
1
.gitignore
vendored
@ -16,3 +16,4 @@
|
|||||||
!.yarn/releases
|
!.yarn/releases
|
||||||
!.yarn/sdks
|
!.yarn/sdks
|
||||||
!.yarn/versions
|
!.yarn/versions
|
||||||
|
.vercel
|
||||||
|
@ -6,8 +6,7 @@ sidebar_custom_props:
|
|||||||
---
|
---
|
||||||
|
|
||||||
To use the REST API, you will need an API key.
|
To use the REST API, you will need an API key.
|
||||||
Connect to your Twenty account and follow the following
|
Connect to your Twenty account ang do to Setting > Developers to generate one.
|
||||||
[documentation](user-guide/integrations/generating-api-keys.mdx) to generate one
|
|
||||||
|
|
||||||
## Using Postman?
|
## Using Postman?
|
||||||
|
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"label": "Basics",
|
|
||||||
"position": 1
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"label": "Integrations",
|
|
||||||
"position": 2
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"label": "Others",
|
|
||||||
"position": 3
|
|
||||||
}
|
|
@ -75,11 +75,6 @@ const config = {
|
|||||||
type: "search",
|
type: "search",
|
||||||
position: "left",
|
position: "left",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
to: "/user-guide",
|
|
||||||
label: "User Guide",
|
|
||||||
position: "right",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
to: "https://github.com/twentyhq/twenty/releases",
|
to: "https://github.com/twentyhq/twenty/releases",
|
||||||
label: "Releases",
|
label: "Releases",
|
||||||
|
@ -1,16 +1,21 @@
|
|||||||
This is a [Next.js](https://nextjs.org/) project.
|
|
||||||
|
# Twenty-Website
|
||||||
|
This used for the marketing website (twenty.com).
|
||||||
|
This is not related in anyway to the main app, which you can find in twenty-front and twenty-server.
|
||||||
|
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
First, run the development server:
|
We're using Nest.JS
|
||||||
|
|
||||||
|
From the root directory:
|
||||||
```bash
|
```bash
|
||||||
yarn dev
|
nx run twenty-website:dev
|
||||||
```
|
```
|
||||||
|
Then open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||||
|
|
||||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
Or to build in prod:
|
||||||
|
```bash
|
||||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
nx run twenty-website:build
|
||||||
|
nx run twenty-website:start
|
||||||
|
```
|
Before Width: | Height: | Size: 898 KiB After Width: | Height: | Size: 898 KiB |
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.0 MiB |
Before Width: | Height: | Size: 278 KiB After Width: | Height: | Size: 278 KiB |
Before Width: | Height: | Size: 405 KiB After Width: | Height: | Size: 405 KiB |
Before Width: | Height: | Size: 280 KiB After Width: | Height: | Size: 280 KiB |
Before Width: | Height: | Size: 391 KiB After Width: | Height: | Size: 391 KiB |
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
Before Width: | Height: | Size: 4.2 MiB After Width: | Height: | Size: 4.2 MiB |
Before Width: | Height: | Size: 865 KiB After Width: | Height: | Size: 865 KiB |
Before Width: | Height: | Size: 987 KiB After Width: | Height: | Size: 987 KiB |
Before Width: | Height: | Size: 861 KiB After Width: | Height: | Size: 861 KiB |
Before Width: | Height: | Size: 932 KiB After Width: | Height: | Size: 932 KiB |
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 126 KiB |
Before Width: | Height: | Size: 145 KiB After Width: | Height: | Size: 145 KiB |
Before Width: | Height: | Size: 300 KiB After Width: | Height: | Size: 300 KiB |
Before Width: | Height: | Size: 416 KiB After Width: | Height: | Size: 416 KiB |
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 183 KiB |
Before Width: | Height: | Size: 605 KiB After Width: | Height: | Size: 605 KiB |
Before Width: | Height: | Size: 605 KiB After Width: | Height: | Size: 605 KiB |
Before Width: | Height: | Size: 863 KiB After Width: | Height: | Size: 863 KiB |
Before Width: | Height: | Size: 995 KiB After Width: | Height: | Size: 995 KiB |
@ -1,8 +0,0 @@
|
|||||||
import {NextRequest, NextResponse} from "next/server";
|
|
||||||
|
|
||||||
export async function GET (request: NextRequest){
|
|
||||||
const response = await fetch('https://api.github.com/repos/twentyhq/twenty/releases');
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
return NextResponse.json(data);
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
export default async function BlogPost({ params }: { params: { slug: string } }) {
|
|
||||||
const posts = {};
|
|
||||||
return <>Blog Post: {params.slug}</>;
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
export default async function BlogHome() {
|
|
||||||
const posts = {};
|
|
||||||
return <>Blog Home</>;
|
|
||||||
}
|
|
@ -1,11 +1,18 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
|
import styled from '@emotion/styled'
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 600px;
|
||||||
|
@media(max-width: 809px) {
|
||||||
|
width: 100%;
|
||||||
|
}`;
|
||||||
|
|
||||||
|
|
||||||
export const ContentContainer = ({children}: {children?: React.ReactNode}) => {
|
export const ContentContainer = ({children}: {children?: React.ReactNode}) => {
|
||||||
return (
|
return (
|
||||||
<div style={{
|
<Container>{children}</Container>
|
||||||
width: '600px',
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
}}>{children}</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import styled from '@emotion/styled'
|
import styled from '@emotion/styled'
|
||||||
import { Logo } from './Logo';
|
import { Logo } from './Logo';
|
||||||
import { DiscordIcon, GithubIcon, LinkedInIcon, XIcon } from "./Icons";
|
import { DiscordIcon, GithubIcon2, LinkedInIcon, XIcon } from "./Icons";
|
||||||
|
|
||||||
|
|
||||||
const FooterContainer = styled.div`
|
const FooterContainer = styled.div`
|
||||||
@ -11,6 +11,9 @@ const FooterContainer = styled.div`
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
color: rgb(129, 129, 129);
|
color: rgb(129, 129, 129);
|
||||||
gap: 32px;
|
gap: 32px;
|
||||||
|
@media(max-width: 809px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const LeftSideFooter = styled.div`
|
const LeftSideFooter = styled.div`
|
||||||
@ -21,7 +24,9 @@ const LeftSideFooter = styled.div`
|
|||||||
|
|
||||||
const RightSideFooter = styled.div`
|
const RightSideFooter = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;`;
|
justify-content: space-between;
|
||||||
|
gap: 48px;
|
||||||
|
height: 146px;`;
|
||||||
|
|
||||||
const RightSideFooterColumn = styled.div`
|
const RightSideFooterColumn = styled.div`
|
||||||
width: 160px;
|
width: 160px;
|
||||||
@ -46,7 +51,7 @@ const RightSideFooterColumnTitle = styled.div`
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const FooterNav = () => {
|
export const FooterDesktop = () => {
|
||||||
return <FooterContainer>
|
return <FooterContainer>
|
||||||
<div style={{ width: '100%', display: 'flex', flexDirection: 'row', justifyContent:'space-between'}}>
|
<div style={{ width: '100%', display: 'flex', flexDirection: 'row', justifyContent:'space-between'}}>
|
||||||
<LeftSideFooter>
|
<LeftSideFooter>
|
||||||
@ -58,8 +63,8 @@ export const FooterNav = () => {
|
|||||||
<RightSideFooter>
|
<RightSideFooter>
|
||||||
<RightSideFooterColumn>
|
<RightSideFooterColumn>
|
||||||
<RightSideFooterColumnTitle>Company</RightSideFooterColumnTitle>
|
<RightSideFooterColumnTitle>Company</RightSideFooterColumnTitle>
|
||||||
<RightSideFooterLink href='/'>Pricing</RightSideFooterLink>
|
<RightSideFooterLink href='/pricing'>Pricing</RightSideFooterLink>
|
||||||
<RightSideFooterLink href='/'>Story</RightSideFooterLink>
|
<RightSideFooterLink href='/story'>Story</RightSideFooterLink>
|
||||||
</RightSideFooterColumn>
|
</RightSideFooterColumn>
|
||||||
<RightSideFooterColumn>
|
<RightSideFooterColumn>
|
||||||
<RightSideFooterColumnTitle>Resources</RightSideFooterColumnTitle>
|
<RightSideFooterColumnTitle>Resources</RightSideFooterColumnTitle>
|
||||||
@ -91,7 +96,7 @@ export const FooterNav = () => {
|
|||||||
<XIcon size='M'/>
|
<XIcon size='M'/>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/twentyhq/twenty" target="_blank">
|
<a href="https://github.com/twentyhq/twenty" target="_blank">
|
||||||
<GithubIcon size='M'/>
|
<GithubIcon2 size='M'/>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://www.linkedin.com/company/twenty" target="_blank">
|
<a href="https://www.linkedin.com/company/twenty" target="_blank">
|
||||||
<LinkedInIcon size='M'/>
|
<LinkedInIcon size='M'/>
|
@ -22,14 +22,18 @@ const Nav = styled.nav`
|
|||||||
overflow: visible;
|
overflow: visible;
|
||||||
padding: 12px 16px 12px 16px;
|
padding: 12px 16px 12px 16px;
|
||||||
position: relative;
|
position: relative;
|
||||||
border-color: rgba(20, 20, 20, 0.08);
|
|
||||||
transform-origin: 50% 50% 0px;
|
transform-origin: 50% 50% 0px;
|
||||||
border-bottom: 1px solid var(--Borders-Light, #F1F1F1);
|
border-bottom: 1px solid rgba(20, 20, 20, 0.08);
|
||||||
|
|
||||||
|
@media(max-width: 809px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const LinkList = styled.div`
|
const LinkList = styled.div`
|
||||||
display:flex;
|
display:flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
gap: 2px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ListItem = styled.a`
|
const ListItem = styled.a`
|
||||||
@ -78,8 +82,7 @@ const StyledButton = styled.div`
|
|||||||
const CallToActionContainer = styled.div`
|
const CallToActionContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 16px;
|
gap: 8px;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
@ -89,6 +92,7 @@ const LinkNextToCTA = styled.a`
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: rgb(71, 71, 71);
|
color: rgb(71, 71, 71);
|
||||||
|
padding: 0px 16px 0px 16px;
|
||||||
span {
|
span {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}`;
|
}`;
|
||||||
@ -112,7 +116,7 @@ const ExternalArrow = () => {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HeaderNav = () => {
|
export const HeaderDesktop = () => {
|
||||||
|
|
||||||
const isTwentyDev = false;
|
const isTwentyDev = false;
|
||||||
|
|
||||||
@ -124,8 +128,8 @@ export const HeaderNav = () => {
|
|||||||
<LinkList>
|
<LinkList>
|
||||||
<ListItem href="/pricing">Pricing</ListItem>
|
<ListItem href="/pricing">Pricing</ListItem>
|
||||||
<ListItem href="/story">Story</ListItem>
|
<ListItem href="/story">Story</ListItem>
|
||||||
<ListItem href="http://docs.twenty.com">Docs <ExternalArrow /></ListItem>
|
<ListItem href="https://docs.twenty.com">Docs <ExternalArrow /></ListItem>
|
||||||
<ListItem href="http://docs.twenty.com"><GithubIcon color='rgb(71,71,71)' /> 5.7k <ExternalArrow /></ListItem>
|
<ListItem href="https://github.com/twentyhq/twenty"><GithubIcon color='rgb(71,71,71)' /> 5.7k <ExternalArrow /></ListItem>
|
||||||
</LinkList>
|
</LinkList>
|
||||||
<CallToAction />
|
<CallToAction />
|
||||||
</Nav>;
|
</Nav>;
|
176
packages/twenty-website/src/app/components/HeaderMobile.tsx
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import styled from '@emotion/styled'
|
||||||
|
import { Logo } from './Logo';
|
||||||
|
import { IBM_Plex_Mono } from 'next/font/google';
|
||||||
|
import { GithubIcon } from './Icons';
|
||||||
|
|
||||||
|
const IBMPlexMono = IBM_Plex_Mono({
|
||||||
|
weight: '500',
|
||||||
|
subsets: ['latin'],
|
||||||
|
display: 'swap',
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const Nav = styled.nav`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
overflow: visible;
|
||||||
|
padding: 0 12px;
|
||||||
|
position: relative;
|
||||||
|
transform-origin: 50% 50% 0px;
|
||||||
|
border-bottom: 1px solid rgba(20, 20, 20, 0.08);
|
||||||
|
height: 64px;
|
||||||
|
width: 100%;
|
||||||
|
@media(min-width: 810px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const LinkList = styled.div`
|
||||||
|
display:flex;
|
||||||
|
flex-direction: column;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ListItem = styled.a`
|
||||||
|
color: rgb(71, 71, 71);
|
||||||
|
text-decoration: none;
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 8px;
|
||||||
|
height: 40px;
|
||||||
|
padding-left: 16px;
|
||||||
|
padding-right: 16px;
|
||||||
|
&:hover {
|
||||||
|
background-color: #F1F1F1;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const LogoContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
width:202px;`;
|
||||||
|
|
||||||
|
const LogoAddon = styled.div`
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 150%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledButton = styled.div`
|
||||||
|
display: flex;
|
||||||
|
height: 40px;
|
||||||
|
padding-left: 16px;
|
||||||
|
padding-right: 16px;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #000;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-weight: 500;
|
||||||
|
border: none;
|
||||||
|
outline: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CallToActionContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const LinkNextToCTA = styled.a`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: rgb(71, 71, 71);
|
||||||
|
padding: 0px 16px 0px 16px;
|
||||||
|
span {
|
||||||
|
text-decoration: underline;
|
||||||
|
}`;
|
||||||
|
|
||||||
|
const CallToAction = () => {
|
||||||
|
return <CallToActionContainer>
|
||||||
|
<LinkNextToCTA href="https://github.com/twentyhq/twenty">Sign in</LinkNextToCTA>
|
||||||
|
<a href="#">
|
||||||
|
<StyledButton>
|
||||||
|
Get Started
|
||||||
|
</StyledButton>
|
||||||
|
</a>
|
||||||
|
</CallToActionContainer>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const ExternalArrow = () => {
|
||||||
|
return <div style={{width:'14px', height:'14px', fill: 'rgb(179, 179, 179)'}}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" focusable="false" color="rgb(179, 179, 179)"><g color="rgb(179, 179, 179)" ><path d="M200,64V168a8,8,0,0,1-16,0V83.31L69.66,197.66a8,8,0,0,1-11.32-11.32L172.69,72H88a8,8,0,0,1,0-16H192A8,8,0,0,1,200,64Z"></path></g></svg>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
const HamburgerContainer = styled.div`
|
||||||
|
height: 44px;
|
||||||
|
width: 44px;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
input {
|
||||||
|
height: 44px;
|
||||||
|
width: 44px;
|
||||||
|
opacity: 0;
|
||||||
|
}`;
|
||||||
|
|
||||||
|
const HamburgerLine1 = styled.div`
|
||||||
|
height: 2px;
|
||||||
|
left: calc(50.00000000000002% - 20px / 2);
|
||||||
|
position: absolute;
|
||||||
|
top: calc(37.50000000000002% - 2px / 2);
|
||||||
|
width: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: rgb(179, 179, 179);`;
|
||||||
|
|
||||||
|
const HamburgerLine2 = styled.div`
|
||||||
|
height: 2px;
|
||||||
|
left: calc(50.00000000000002% - 20px / 2);
|
||||||
|
position: absolute;
|
||||||
|
top: calc(62.50000000000002% - 2px / 2);
|
||||||
|
width: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: rgb(179, 179, 179);`;
|
||||||
|
|
||||||
|
const NavOpen = styled.div`
|
||||||
|
display:none;`;
|
||||||
|
|
||||||
|
|
||||||
|
export const HeaderMobile = () => {
|
||||||
|
|
||||||
|
const isTwentyDev = false;
|
||||||
|
|
||||||
|
return <Nav>
|
||||||
|
<LogoContainer>
|
||||||
|
<Logo />
|
||||||
|
{isTwentyDev && <LogoAddon className={IBMPlexMono.className}>for Developers</LogoAddon>}
|
||||||
|
</LogoContainer>
|
||||||
|
<HamburgerContainer>
|
||||||
|
<input type="checkbox" />
|
||||||
|
<HamburgerLine1 />
|
||||||
|
<HamburgerLine2 />
|
||||||
|
</HamburgerContainer>
|
||||||
|
|
||||||
|
<NavOpen>
|
||||||
|
<LinkList>
|
||||||
|
<ListItem href="/pricing">Pricing</ListItem>
|
||||||
|
<ListItem href="/story">Story</ListItem>
|
||||||
|
<ListItem href="https://docs.twenty.com">Docs <ExternalArrow /></ListItem>
|
||||||
|
<ListItem href="https://github.com/twentyhq/twenty"><GithubIcon color='rgb(71,71,71)' /> 5.7k <ExternalArrow /></ListItem>
|
||||||
|
</LinkList>
|
||||||
|
<CallToAction />
|
||||||
|
</NavOpen>
|
||||||
|
</Nav>;
|
||||||
|
};
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
const getSize = size => {
|
const getSize = (size: string) => {
|
||||||
switch(size) {
|
switch(size) {
|
||||||
case 'S':
|
case 'S':
|
||||||
return '14px';
|
return '14px';
|
||||||
@ -36,6 +36,16 @@ export const DiscordIcon = ({size = 'S', color = 'rgb(179, 179, 179)'}) => {
|
|||||||
export const XIcon = ({size = 'S', color = 'rgb(179, 179, 179)'}) => {
|
export const XIcon = ({size = 'S', color = 'rgb(179, 179, 179)'}) => {
|
||||||
let dimension = getSize(size);
|
let dimension = getSize(size);
|
||||||
return <div style={{width: dimension, height: dimension}}>
|
return <div style={{width: dimension, height: dimension}}>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" focusable="false" color={color} ><g color={color}><path d="M104,140a12,12,0,1,1-12-12A12,12,0,0,1,104,140Zm60-12a12,12,0,1,0,12,12A12,12,0,0,0,164,128Zm74.45,64.9-67,29.71a16.17,16.17,0,0,1-21.71-9.1l-8.11-22q-6.72.45-13.63.46t-13.63-.46l-8.11,22a16.18,16.18,0,0,1-21.71,9.1l-67-29.71a15.93,15.93,0,0,1-9.06-18.51L38,58A16.07,16.07,0,0,1,51,46.14l36.06-5.93a16.22,16.22,0,0,1,18.26,11.88l3.26,12.84Q118.11,64,128,64t19.4.93l3.26-12.84a16.21,16.21,0,0,1,18.26-11.88L205,46.14A16.07,16.07,0,0,1,218,58l29.53,116.38A15.93,15.93,0,0,1,238.45,192.9ZM232,178.28,202.47,62s0,0-.08,0L166.33,56a.17.17,0,0,0-.17,0l-2.83,11.14c5,.94,10,2.06,14.83,3.42A8,8,0,0,1,176,86.31a8.09,8.09,0,0,1-2.16-.3A172.25,172.25,0,0,0,128,80a172.25,172.25,0,0,0-45.84,6,8,8,0,1,1-4.32-15.4c4.82-1.36,9.78-2.48,14.82-3.42L89.83,56s0,0-.12,0h0L53.61,61.93a.17.17,0,0,0-.09,0L24,178.33,91,208a.23.23,0,0,0,.22,0L98,189.72a173.2,173.2,0,0,1-20.14-4.32A8,8,0,0,1,82.16,170,171.85,171.85,0,0,0,128,176a171.85,171.85,0,0,0,45.84-6,8,8,0,0,1,4.32,15.41A173.2,173.2,0,0,1,158,189.72L164.75,208a.22.22,0,0,0,.21,0Z" fill={color}></path></g></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22" id="svg2382164700">
|
||||||
|
<path d="M 15.418 19.037 L 3.44 3.637 C 3.311 3.471 3.288 3.247 3.381 3.058 C 3.473 2.87 3.665 2.75 3.875 2.75 L 6.148 2.75 C 6.318 2.75 6.478 2.829 6.582 2.963 L 18.56 18.363 C 18.689 18.529 18.712 18.753 18.619 18.942 C 18.527 19.13 18.335 19.25 18.125 19.25 L 15.852 19.25 C 15.682 19.25 15.522 19.171 15.418 19.037 Z" fill="transparent" strokeWidth="1.38" strokeMiterlimit="10" stroke={color}></path>
|
||||||
|
<path d="M 18.333 2.75 L 3.667 19.25" fill="transparent" strokeWidth="1.38" strokeLinecap="round" strokeMiterlimit="10" stroke={color}></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GithubIcon2 = ({size = 'S', color = 'rgb(179, 179, 179)'}) => {
|
||||||
|
let dimension = getSize(size);
|
||||||
|
return <div style={{width: dimension, height: dimension}}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" focusable="false" color={color}><g color={color}><path d="M208.31,75.68A59.78,59.78,0,0,0,202.93,28,8,8,0,0,0,196,24a59.75,59.75,0,0,0-48,24H124A59.75,59.75,0,0,0,76,24a8,8,0,0,0-6.93,4,59.78,59.78,0,0,0-5.38,47.68A58.14,58.14,0,0,0,56,104v8a56.06,56.06,0,0,0,48.44,55.47A39.8,39.8,0,0,0,96,192v8H72a24,24,0,0,1-24-24A40,40,0,0,0,8,136a8,8,0,0,0,0,16,24,24,0,0,1,24,24,40,40,0,0,0,40,40H96v16a8,8,0,0,0,16,0V192a24,24,0,0,1,48,0v40a8,8,0,0,0,16,0V192a39.8,39.8,0,0,0-8.44-24.53A56.06,56.06,0,0,0,216,112v-8A58.14,58.14,0,0,0,208.31,75.68ZM200,112a40,40,0,0,1-40,40H112a40,40,0,0,1-40-40v-8a41.74,41.74,0,0,1,6.9-22.48A8,8,0,0,0,80,73.83a43.81,43.81,0,0,1,.79-33.58,43.88,43.88,0,0,1,32.32,20.06A8,8,0,0,0,119.82,64h32.35a8,8,0,0,0,6.74-3.69,43.87,43.87,0,0,1,32.32-20.06A43.81,43.81,0,0,1,192,73.83a8.09,8.09,0,0,0,1,7.65A41.72,41.72,0,0,1,200,104Z" fill={color}></path></g></svg>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
7
packages/twenty-website/src/app/components/PostImage.tsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import Image from 'next/image'
|
||||||
|
|
||||||
|
export const PostImage = ({ sources, style }: { sources: { light: string, dark: string }, style?: React.CSSProperties }) => {
|
||||||
|
return <Image src={sources.light} style={style} alt={sources.light} />
|
||||||
|
}
|
26
packages/twenty-website/src/app/layout.css
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
*, *::before, *::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-smooth: antialiased;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: rgb(129, 129, 129);
|
||||||
|
&:hover {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
}
|
@ -2,19 +2,22 @@ import type { Metadata } from 'next'
|
|||||||
import { Gabarito } from 'next/font/google'
|
import { Gabarito } from 'next/font/google'
|
||||||
import EmotionRootStyleRegistry from './emotion-root-style-registry'
|
import EmotionRootStyleRegistry from './emotion-root-style-registry'
|
||||||
import styled from '@emotion/styled'
|
import styled from '@emotion/styled'
|
||||||
import { HeaderNav } from './components/HeaderNav'
|
import { HeaderDesktop } from './components/HeaderDesktop'
|
||||||
import { FooterNav } from './components/FooterNav'
|
import { FooterDesktop } from './components/FooterDesktop'
|
||||||
|
import { HeaderMobile } from '@/app/components/HeaderMobile'
|
||||||
|
import './layout.css'
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: 'Twenty.dev',
|
title: 'Twenty.dev',
|
||||||
description: 'Twenty for Developer',
|
description: 'Twenty for Developer',
|
||||||
icons: '/favicon.ico',
|
icons: '/images/core/logo.svg',
|
||||||
}
|
}
|
||||||
|
|
||||||
const gabarito = Gabarito({
|
const gabarito = Gabarito({
|
||||||
weight: ['400', '500'],
|
weight: ['400', '500'],
|
||||||
subsets: ['latin'],
|
subsets: ['latin'],
|
||||||
display: 'swap',
|
display: 'swap',
|
||||||
|
adjustFontFallback: false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@ -25,17 +28,14 @@ export default function RootLayout({
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" className={gabarito.className}>
|
<html lang="en" className={gabarito.className}>
|
||||||
<body style={{
|
<body>
|
||||||
margin: 0,
|
|
||||||
WebkitFontSmoothing: "antialiased",
|
|
||||||
MozOsxFontSmoothing: "grayscale"
|
|
||||||
}}>
|
|
||||||
<EmotionRootStyleRegistry>
|
<EmotionRootStyleRegistry>
|
||||||
<HeaderNav />
|
<HeaderDesktop />
|
||||||
<div style={{display: 'flex', flexDirection: 'column', alignItems: 'center'}}>
|
<div className="container">
|
||||||
|
<HeaderMobile />
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
<FooterNav />
|
<FooterDesktop />
|
||||||
</EmotionRootStyleRegistry>
|
</EmotionRootStyleRegistry>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
import { GetStaticProps } from 'next'
|
|
||||||
import { MDXRemote } from 'next-mdx-remote/rsc'
|
|
||||||
import { compileMDX } from 'next-mdx-remote/rsc'
|
import { compileMDX } from 'next-mdx-remote/rsc'
|
||||||
import gfm from 'remark-gfm';
|
import gfm from 'remark-gfm';
|
||||||
import { ContentContainer } from '../components/ContentContainer';
|
import { ContentContainer } from '../components/ContentContainer';
|
||||||
import { visit } from 'unist-util-visit';
|
|
||||||
import remarkBehead from 'remark-behead';
|
import remarkBehead from 'remark-behead';
|
||||||
|
import type { Metadata } from 'next'
|
||||||
|
|
||||||
|
|
||||||
interface Release {
|
interface Release {
|
||||||
@ -14,9 +12,14 @@ interface Release {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const metadata: Metadata= {
|
||||||
|
title: 'Twenty - Releases',
|
||||||
|
description: 'Latest releases of Twenty',
|
||||||
|
}
|
||||||
|
|
||||||
const Home = async () => {
|
const Home = async () => {
|
||||||
const res = await fetch(`${process.env.BASE_URL}/api/github`);
|
const response = await fetch('https://api.github.com/repos/twentyhq/twenty/releases');
|
||||||
const data: Release[] = await res.json();
|
const data: Release[] = await response.json();
|
||||||
|
|
||||||
const releases = await Promise.all(
|
const releases = await Promise.all(
|
||||||
data.map(async (release) => {
|
data.map(async (release) => {
|
||||||
@ -51,11 +54,11 @@ interface Release {
|
|||||||
<ContentContainer>
|
<ContentContainer>
|
||||||
<h1>Releases</h1>
|
<h1>Releases</h1>
|
||||||
|
|
||||||
{releases.map(release => (
|
{releases.map((release, index) => (
|
||||||
<div key={release.id}
|
<div key={release.id}
|
||||||
style={{
|
style={{
|
||||||
padding: '24px 0px 24px 0px',
|
padding: '24px 0px 24px 0px',
|
||||||
borderBottom: '1px solid #ccc',
|
borderBottom: index === releases.length - 1 ? 'none' : '1px solid #ccc',
|
||||||
}}>
|
}}>
|
||||||
<h2>{release.name}</h2>
|
<h2>{release.name}</h2>
|
||||||
<div>{release.body}</div>
|
<div>{release.body}</div>
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
import { getPost } from "@/app/user-guide/get-posts";
|
||||||
|
|
||||||
|
export default async function BlogPost({ params }: { params: { slug: string[] } }) {
|
||||||
|
const post = await getPost(params.slug as string[]);
|
||||||
|
console.log(post);
|
||||||
|
|
||||||
|
return <div>
|
||||||
|
<h1>{post?.itemInfo.title}</h1>
|
||||||
|
<div>{post?.content}</div>
|
||||||
|
</div>;
|
||||||
|
}
|
97
packages/twenty-website/src/app/user-guide/get-posts.tsx
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { compileMDX } from 'next-mdx-remote/rsc';
|
||||||
|
import { ReactElement } from 'react';
|
||||||
|
|
||||||
|
interface ItemInfo {
|
||||||
|
title: string;
|
||||||
|
position?: number;
|
||||||
|
path: string;
|
||||||
|
type: 'file' | 'directory';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FileContent {
|
||||||
|
content: ReactElement;
|
||||||
|
itemInfo: ItemInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Directory {
|
||||||
|
[key: string]: FileContent | Directory | ItemInfo;
|
||||||
|
itemInfo: ItemInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
const basePath = '/src/content/user-guide';
|
||||||
|
|
||||||
|
|
||||||
|
async function getFiles(filePath: string, position: number = 0): Promise<Directory> {
|
||||||
|
const entries = fs.readdirSync(filePath, { withFileTypes: true });
|
||||||
|
|
||||||
|
const urlpath = path.toString().split(basePath);
|
||||||
|
const pathName = urlpath.length > 1 ? urlpath[1] : path.basename(filePath);
|
||||||
|
console.log(pathName);
|
||||||
|
|
||||||
|
const directory: Directory = {
|
||||||
|
itemInfo: {
|
||||||
|
title: path.basename(filePath),
|
||||||
|
position,
|
||||||
|
type: 'directory',
|
||||||
|
path: pathName,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
directory[entry.name] = await getFiles(path.join(filePath, entry.name), position++);
|
||||||
|
} else if (entry.isFile() && path.extname(entry.name) === '.mdx') {
|
||||||
|
const fileContent = fs.readFileSync(path.join(filePath, entry.name), 'utf8');
|
||||||
|
const { content, frontmatter } = await compileMDX<{ title: string, position?: number }>({ source: fileContent, options: { parseFrontmatter: true } });
|
||||||
|
directory[entry.name] = { content, itemInfo: {...frontmatter, type: 'file', path: pathName + "/" + entry.name.replace(/\.mdx$/, '')} };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function parseFrontMatterAndCategory(directory: Directory, dirPath: string): Promise<Directory> {
|
||||||
|
const parsedDirectory: Directory = {
|
||||||
|
itemInfo: directory.itemInfo,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const entry in directory) {
|
||||||
|
if (entry !== 'itemInfo' && directory[entry] instanceof Object) {
|
||||||
|
parsedDirectory[entry] = await parseFrontMatterAndCategory(directory[entry] as Directory, path.join(dirPath, entry));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const categoryPath = path.join(dirPath, '_category_.json');
|
||||||
|
|
||||||
|
if (fs.existsSync(categoryPath)) {
|
||||||
|
const categoryJson: ItemInfo = JSON.parse(fs.readFileSync(categoryPath, 'utf8'));
|
||||||
|
parsedDirectory.itemInfo = categoryJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsedDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getPosts(): Promise<Directory> {
|
||||||
|
const postsDirectory = path.join(process.cwd(), basePath);
|
||||||
|
const directory = await getFiles(postsDirectory);
|
||||||
|
return parseFrontMatterAndCategory(directory, postsDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getPost(slug: string[]): Promise<FileContent | null> {
|
||||||
|
const postsDirectory = path.join(process.cwd(), basePath);
|
||||||
|
const modifiedSlug = slug.join('/');
|
||||||
|
const filePath = path.join(postsDirectory, `${modifiedSlug}.mdx`);
|
||||||
|
|
||||||
|
console.log(filePath);
|
||||||
|
|
||||||
|
if (!fs.existsSync(filePath)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileContent = fs.readFileSync(filePath, 'utf8');
|
||||||
|
const { content, frontmatter } = await compileMDX<{ title: string, position?: number }>({ source: fileContent, options: { parseFrontmatter: true } });
|
||||||
|
|
||||||
|
return { content, itemInfo: {...frontmatter, type: 'file', path: modifiedSlug }};
|
||||||
|
}
|
47
packages/twenty-website/src/app/user-guide/page.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { ContentContainer } from '@/app/components/ContentContainer';
|
||||||
|
import { getPosts, Directory, FileContent } from '@/app/user-guide/get-posts';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
|
||||||
|
const DirectoryItem = ({ name, item }: { name: string, item: Directory | FileContent }) => {
|
||||||
|
if ('content' in item) {
|
||||||
|
// If the item is a file, we render a link.
|
||||||
|
return (
|
||||||
|
<div key={name}>
|
||||||
|
<Link href={`/user-guide/${item.itemInfo.path}`}>
|
||||||
|
{item.itemInfo.title}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// If the item is a directory, we render the title and the items in the directory.
|
||||||
|
return (
|
||||||
|
<div key={name}>
|
||||||
|
<h2>{item.itemInfo.title}</h2>
|
||||||
|
{Object.entries(item).map(([childName, childItem]) => {
|
||||||
|
if (childName !== 'itemInfo') {
|
||||||
|
return <DirectoryItem key={childName} name={childName} item={childItem as Directory | FileContent} />;
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export default async function BlogHome() {
|
||||||
|
|
||||||
|
const posts = await getPosts();
|
||||||
|
|
||||||
|
|
||||||
|
return <ContentContainer>
|
||||||
|
<h1>User Guide</h1>
|
||||||
|
<div>
|
||||||
|
{Object.entries(posts).map(([name, item]) => {
|
||||||
|
if (name !== 'itemInfo') {
|
||||||
|
return <DirectoryItem key={name} name={name} item={item as Directory | FileContent} />;
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</ContentContainer>;
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"title": "Basics",
|
||||||
|
"position": 1
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: User Guide
|
title: Get started
|
||||||
displayed_sidebar: userSidebar
|
displayed_sidebar: userSidebar
|
||||||
sidebar_class_name: hidden
|
sidebar_class_name: hidden
|
||||||
sidebar_position: 0
|
sidebar_position: 0
|
||||||
@ -8,7 +8,6 @@ sidebar_custom_props:
|
|||||||
isSidebarRoot: true
|
isSidebarRoot: true
|
||||||
---
|
---
|
||||||
|
|
||||||
import ThemedImage from '@theme/ThemedImage';
|
|
||||||
|
|
||||||
# Welcome to Twenty's User Guide
|
# Welcome to Twenty's User Guide
|
||||||
|
|
||||||
@ -46,13 +45,13 @@ There are multiple ways to create notes and tasks. Learn more about [Notes](./ba
|
|||||||
|
|
||||||
You can also upload and attach files for each record. To do so, expand a record, and head over to the <b>Files</b> tab. You'll then see the `+ Add file` button.
|
You can also upload and attach files for each record. To do so, expand a record, and head over to the <b>Files</b> tab. You'll then see the `+ Add file` button.
|
||||||
|
|
||||||
<ThemedImage sources={{light: "../img/user-guide/attach-files-to-records-light.png", dark:"../img/user-guide/attach-files-to-records-dark.png"}} style={{width:'100%', maxWidth:'800px'}}/>
|
<img src="/images/user-guide/attach-files-to-records-light.png" style={{width:'100%', maxWidth:'800px'}}/>
|
||||||
|
|
||||||
## Add Records To Favorites
|
## Add Records To Favorites
|
||||||
|
|
||||||
You can add records to your favorites for quick access. To do so, expand the record you want to add, and click on the heart icon on the top right. You'll now be able to see your favorite records in your sidebar right above your workspace.
|
You can add records to your favorites for quick access. To do so, expand the record you want to add, and click on the heart icon on the top right. You'll now be able to see your favorite records in your sidebar right above your workspace.
|
||||||
|
|
||||||
<ThemedImage sources={{light: "../img/user-guide/view-favorite-records-light.png", dark:"../img/user-guide/view-favorite-records-dark.png"}} style={{width:'100%', maxWidth:'800px'}}/>
|
<img src="/images/user-guide/view-favorite-records-light.png" style={{width:'100%', maxWidth:'800px'}}/>
|
||||||
|
|
||||||
## Import data
|
## Import data
|
||||||
|
|
@ -1,11 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: Creating Custom Objects
|
title: Custom Objects
|
||||||
sidebar_position: 1
|
sidebar_position: 1
|
||||||
sidebar_custom_props:
|
sidebar_custom_props:
|
||||||
icon: TbAugmentedReality
|
icon: TbAugmentedReality
|
||||||
---
|
---
|
||||||
|
|
||||||
import ThemedImage from '@theme/ThemedImage';
|
|
||||||
|
|
||||||
Objects are structures that allow you to store data (records, attributes, and values) specific to an organization. Twenty provides both standard and custom objects.
|
Objects are structures that allow you to store data (records, attributes, and values) specific to an organization. Twenty provides both standard and custom objects.
|
||||||
|
|
||||||
@ -22,7 +21,7 @@ To create a new custom object:
|
|||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
<ThemedImage sources={{light: "/img/user-guide/view-all-objects-light.png", dark:"/img/user-guide/view-all-objects-dark.png"}} style={{width:'100%', maxWidth:'800px'}}/>
|
<img src="/images/user-guide/view-all-objects-light.png" style={{width:'100%', maxWidth:'800px'}}/>
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
@ -31,7 +30,7 @@ To create a new custom object:
|
|||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
<div style={{textAlign: 'center'}}>
|
<div style={{textAlign: 'center'}}>
|
||||||
<img src="/img/user-guide/create-custom-object.gif" alt="Create custom object" />
|
<img src="/images/user-guide/create-custom-object.gif" alt="Create custom object" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
@ -39,5 +38,5 @@ To create a new custom object:
|
|||||||
4. Once you create your custom object, you'll be able to manage it. You can edit the name, icon and description, view the different fields, and add more fields.
|
4. Once you create your custom object, you'll be able to manage it. You can edit the name, icon and description, view the different fields, and add more fields.
|
||||||
|
|
||||||
<div style={{textAlign: 'center'}}>
|
<div style={{textAlign: 'center'}}>
|
||||||
<img src="/img/user-guide/manage-custom-object.png" alt="Create custom object" />
|
<img src="/images/user-guide/manage-custom-object.png" alt="Create custom object" />
|
||||||
</div>
|
</div>
|
@ -5,7 +5,7 @@ sidebar_custom_props:
|
|||||||
icon: TbNote
|
icon: TbNote
|
||||||
---
|
---
|
||||||
|
|
||||||
import ThemedImage from '@theme/ThemedImage';
|
import PostImage from '@theme/PostImage';
|
||||||
|
|
||||||
Easily create a note to keep track of important information.
|
Easily create a note to keep track of important information.
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ Easily create a note to keep track of important information.
|
|||||||
|
|
||||||
To attach a note to the record, go to the <b>Notes</b> tab of a record page and click on `+ New Note`. You can also format, comment, and upload images to your notes.
|
To attach a note to the record, go to the <b>Notes</b> tab of a record page and click on `+ New Note`. You can also format, comment, and upload images to your notes.
|
||||||
|
|
||||||
<ThemedImage sources={{light: "/img/user-guide/create-new-note-light.png", dark:"/img/user-guide/create-new-note-dark.png"}} style={{width:'100%', maxWidth:'800px'}}/>
|
<img src="images/user-guide/create-new-note-light.png" style={{width:'100%', maxWidth:'800px'}}/>
|
||||||
|
|
||||||
## Format Notes
|
## Format Notes
|
||||||
|
|
@ -5,11 +5,11 @@ sidebar_custom_props:
|
|||||||
icon: TbTargetArrow
|
icon: TbTargetArrow
|
||||||
---
|
---
|
||||||
|
|
||||||
import ThemedImage from '@theme/ThemedImage';
|
import PostImage from '@theme/PostImage';
|
||||||
|
|
||||||
All opportunities are presented in a Kanban board, where each column represents the stage of your workflow and each card represents a record. For each card, you can list the amount, close date, probability, and the point of contact. You can also move each card between stages as it goes through your workflow.
|
All opportunities are presented in a Kanban board, where each column represents the stage of your workflow and each card represents a record. For each card, you can list the amount, close date, probability, and the point of contact. You can also move each card between stages as it goes through your workflow.
|
||||||
|
|
||||||
<ThemedImage sources={{light: "/img/user-guide/all-opportunities-light.png", dark:"/img/user-guide/all-opportunities-dark.png"}} style={{width:'100%', maxWidth:'800px'}}/>
|
<img src="/images/user-guide/all-opportunities-light.png" style={{width:'100%', maxWidth:'800px'}}/>
|
||||||
|
|
||||||
## Add and delete stages
|
## Add and delete stages
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ It's easy to add filters and update your view to focus on only the most importan
|
|||||||
- You can also add another filter by following the same steps or clicking on the `+ Add filter` button on top of the columns.
|
- You can also add another filter by following the same steps or clicking on the `+ Add filter` button on top of the columns.
|
||||||
- To remove a filter condition, simply click on the <b>X</b> next to the attribute you used to filter the records.
|
- To remove a filter condition, simply click on the <b>X</b> next to the attribute you used to filter the records.
|
||||||
|
|
||||||
<ThemedImage sources={{light: "/img/user-guide/filter-opportunities-light.png", dark:"/img/user-guide/filter-opportunities-dark.png"}} style={{width:'100%', maxWidth:'800px'}}/>
|
<img src="/images/user-guide/filter-opportunities-light.png" style={{width:'100%', maxWidth:'800px'}}/>
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
@ -41,10 +41,10 @@ You can configure your kanban board to display some fields and hide others. By d
|
|||||||
|
|
||||||
You can also rearrange the order of fields by holding down the field name and dragging it to where you want it.
|
You can also rearrange the order of fields by holding down the field name and dragging it to where you want it.
|
||||||
|
|
||||||
<ThemedImage sources={{light: "/img/user-guide/display-fields-light.png", dark:"/img/user-guide/display-fields-dark.png"}} style={{width:'100%', maxWidth:'800px'}}/>
|
<img src="/images/user-guide/display-fields-light.png" style={{width:'100%', maxWidth:'800px'}}/>
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
You can also hide all the fields, and get an overview of all the opportunities at a glance. To do so, click on <b>Options</b> on the top right and turn on the toggle in front of the <b>Compact view</b> option.
|
You can also hide all the fields, and get an overview of all the opportunities at a glance. To do so, click on <b>Options</b> on the top right and turn on the toggle in front of the <b>Compact view</b> option.
|
||||||
|
|
||||||
<ThemedImage sources={{light: "/img/user-guide/compact-opportunities-view-light.png", dark:"/img/user-guide/compact-opportunities-view-dark.png"}} style={{width:'100%', maxWidth:'800px'}}/>
|
<img src="/images/user-guide/compact-opportunities-view-light.png"style={{width:'100%', maxWidth:'800px'}}/>
|
@ -5,11 +5,11 @@ sidebar_custom_props:
|
|||||||
icon: TbChecklist
|
icon: TbChecklist
|
||||||
---
|
---
|
||||||
|
|
||||||
import ThemedImage from '@theme/ThemedImage';
|
import PostImage from '../../../components/PostImage'
|
||||||
|
|
||||||
You can find all the tasks from across your workspace in the <b>Tasks</b> window in your sidebar. You can also find a dedicated tab for Tasks on each record so you can add and edit tasks directly from each record. Alternatively, you can click on the `+` button on the top right of each record page and then click on <b>Task</b> to create a new task.
|
You can find all the tasks from across your workspace in the <b>Tasks</b> window in your sidebar. You can also find a dedicated tab for Tasks on each record so you can add and edit tasks directly from each record. Alternatively, you can click on the `+` button on the top right of each record page and then click on <b>Task</b> to create a new task.
|
||||||
|
|
||||||
<ThemedImage sources={{light: "/img/user-guide/create-new-task-light.png", dark:"/img/user-guide/create-new-task-dark.png"}} style={{width:'100%', maxWidth:'800px'}}/>
|
<img src="/images/user-guide/create-new-task-light.png" style={{width:'100%', maxWidth:'800px'}}/>
|
||||||
|
|
||||||
## Tasks page
|
## Tasks page
|
||||||
|
|
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"title": "Integrations",
|
||||||
|
"position": 2
|
||||||
|
}
|
@ -21,7 +21,7 @@ Sync Twenty with 3000+ apps using [Zapier](https://zapier.com/), and automate yo
|
|||||||
6. Enter your API key and click on 'Yes, Continue to Twenty.'
|
6. Enter your API key and click on 'Yes, Continue to Twenty.'
|
||||||
|
|
||||||
<div style={{textAlign: 'center'}}>
|
<div style={{textAlign: 'center'}}>
|
||||||
<img src="/img/user-guide/connect-zapier.png" alt="Connect Twenty to Zapier" />
|
<img src="/images/user-guide/connect-zapier.png" alt="Connect Twenty to Zapier" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
You can now continue creating your automation!
|
You can now continue creating your automation!
|
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"title": "Others",
|
||||||
|
"position": 3
|
||||||
|
}
|