mirror of
https://github.com/twentyhq/twenty.git
synced 2024-10-27 03:33:21 +03:00
parent
68a6250757
commit
5ef5bbdc4d
@ -1,9 +0,0 @@
|
|||||||
import { Decorator } from '@storybook/react';
|
|
||||||
|
|
||||||
import { ObjectMetadataItemsProvider } from '@/object-metadata/components/ObjectMetadataItemsProvider';
|
|
||||||
|
|
||||||
export const ObjectMetadataItemsDecorator: Decorator = (Story) => (
|
|
||||||
<ObjectMetadataItemsProvider>
|
|
||||||
<Story />
|
|
||||||
</ObjectMetadataItemsProvider>
|
|
||||||
);
|
|
@ -15,7 +15,8 @@
|
|||||||
"packages/twenty-docs",
|
"packages/twenty-docs",
|
||||||
"packages/twenty-server",
|
"packages/twenty-server",
|
||||||
"packages/twenty-utils",
|
"packages/twenty-utils",
|
||||||
"packages/twenty-zapier"
|
"packages/twenty-zapier",
|
||||||
|
"packages/twenty-website"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -182,4 +182,4 @@
|
|||||||
"msw": {
|
"msw": {
|
||||||
"workerDirectory": "public"
|
"workerDirectory": "public"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,4 +154,4 @@
|
|||||||
"npm": "please-use-yarn",
|
"npm": "please-use-yarn",
|
||||||
"yarn": "^4.0.2"
|
"yarn": "^4.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1
packages/twenty-website/.env.example
Normal file
1
packages/twenty-website/.env.example
Normal file
@ -0,0 +1 @@
|
|||||||
|
BASE_URL=http://localhost:3000
|
3
packages/twenty-website/.eslintrc.json
Normal file
3
packages/twenty-website/.eslintrc.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"extends": "next/core-web-vitals"
|
||||||
|
}
|
36
packages/twenty-website/.gitignore
vendored
Normal file
36
packages/twenty-website/.gitignore
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
.yarn/install-state.gz
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# next.js
|
||||||
|
/.next/
|
||||||
|
/out/
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env*.local
|
||||||
|
|
||||||
|
# vercel
|
||||||
|
.vercel
|
||||||
|
|
||||||
|
# typescript
|
||||||
|
*.tsbuildinfo
|
||||||
|
next-env.d.ts
|
16
packages/twenty-website/README.md
Normal file
16
packages/twenty-website/README.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
This is a [Next.js](https://nextjs.org/) project.
|
||||||
|
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
First, run the development server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||||
|
|
||||||
|
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||||
|
|
||||||
|
|
4
packages/twenty-website/next.config.js
Normal file
4
packages/twenty-website/next.config.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/** @type {import('next').NextConfig} */
|
||||||
|
const nextConfig = {}
|
||||||
|
|
||||||
|
module.exports = nextConfig
|
30
packages/twenty-website/package.json
Normal file
30
packages/twenty-website/package.json
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"name": "twenty-website",
|
||||||
|
"version": "0.2.2",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next dev",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start",
|
||||||
|
"lint": "next lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@emotion/react": "^11.11.1",
|
||||||
|
"@emotion/styled": "^11.11.0",
|
||||||
|
"@tabler/icons-react": "^2.44.0",
|
||||||
|
"next": "14.0.4",
|
||||||
|
"next-mdx-remote": "^4.4.1",
|
||||||
|
"react": "^18",
|
||||||
|
"react-dom": "^18",
|
||||||
|
"remark-behead": "^3.1.0",
|
||||||
|
"remark-gfm": "^3.0.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20",
|
||||||
|
"@types/react": "^18",
|
||||||
|
"@types/react-dom": "^18",
|
||||||
|
"eslint": "^8",
|
||||||
|
"eslint-config-next": "14.0.4",
|
||||||
|
"typescript": "^5"
|
||||||
|
}
|
||||||
|
}
|
BIN
packages/twenty-website/public/images/core/logo-mini.png
Normal file
BIN
packages/twenty-website/public/images/core/logo-mini.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 570 B |
12
packages/twenty-website/public/images/core/logo.svg
Normal file
12
packages/twenty-website/public/images/core/logo.svg
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<svg width="136" height="136" viewBox="0 0 136 136" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_2343_96406)">
|
||||||
|
<path d="M136 2.28882e-05H0L0.000144482 136H136V2.28882e-05ZM27.27 50.6401C27.27 43.2101 33.3 37.1801 40.73 37.1801H66.64C67.02 37.1801 67.37 37.4101 67.53 37.7601C67.69 38.1101 67.62 38.5201 67.36 38.8101L61.68 44.9801C60.69 46.0501 59.3 46.6701 57.84 46.6701H40.8C38.57 46.6701 36.76 48.4801 36.76 50.7101V60.8901C36.76 62.2001 35.7 63.2601 34.39 63.2601H29.65C28.34 63.2601 27.28 62.2001 27.28 60.8901V50.6401H27.27ZM107.88 85.3601C107.88 92.7901 101.85 98.82 94.42 98.82H83.41C75.98 98.82 69.95 92.7901 69.95 85.3601V66.0901C69.95 64.7801 70.44 63.5201 71.33 62.5501L77.75 55.5801C78.02 55.2901 78.44 55.1901 78.82 55.3301C79.19 55.4801 79.44 55.83 79.44 56.23V85.3001C79.44 87.5301 81.25 89.3401 83.48 89.3401H94.36C96.59 89.3401 98.4 87.5301 98.4 85.3001V50.7101C98.4 48.4801 96.59 46.6701 94.36 46.6701H81.71C80.26 46.6701 78.88 47.2801 77.89 48.3401L40.16 89.3401H62.83C64.14 89.3401 65.2 90.4001 65.2 91.7101V96.4501C65.2 97.7601 64.14 98.82 62.83 98.82H32.28C29.51 98.82 27.26 96.5701 27.26 93.8001V91.29C27.26 90.03 27.73 88.8201 28.59 87.8901L70.89 41.9401C73.69 38.9001 77.62 37.1801 81.75 37.1801H94.41C101.84 37.1801 107.87 43.2101 107.87 50.6401V85.3601H107.88Z" fill="black"/>
|
||||||
|
<path d="M27.27 50.6401C27.27 43.2101 33.3 37.1801 40.73 37.1801H66.64C67.02 37.1801 67.37 37.4101 67.53 37.7601C67.69 38.1101 67.62 38.5201 67.36 38.8101L61.68 44.9801C60.69 46.0501 59.3 46.6701 57.84 46.6701H40.8C38.57 46.6701 36.76 48.4801 36.76 50.7101V60.8901C36.76 62.2001 35.7 63.2601 34.39 63.2601H29.65C28.34 63.2601 27.28 62.2001 27.28 60.8901V50.6401H27.27Z" fill="white"/>
|
||||||
|
<path d="M107.88 85.3601C107.88 92.7901 101.85 98.82 94.42 98.82H83.41C75.98 98.82 69.95 92.7901 69.95 85.3601V66.0901C69.95 64.7801 70.44 63.5201 71.33 62.5501L77.75 55.5801C78.02 55.2901 78.44 55.1901 78.82 55.3301C79.19 55.4801 79.44 55.83 79.44 56.23V85.3001C79.44 87.5301 81.25 89.3401 83.48 89.3401H94.36C96.59 89.3401 98.4 87.5301 98.4 85.3001V50.7101C98.4 48.4801 96.59 46.6701 94.36 46.6701H81.71C80.26 46.6701 78.88 47.2801 77.89 48.3401L40.16 89.3401H62.83C64.14 89.3401 65.2 90.4001 65.2 91.7101V96.4501C65.2 97.7601 64.14 98.82 62.83 98.82H32.28C29.51 98.82 27.26 96.5701 27.26 93.8001V91.29C27.26 90.03 27.73 88.8201 28.59 87.8901L70.89 41.9401C73.69 38.9001 77.62 37.1801 81.75 37.1801H94.41C101.84 37.1801 107.87 43.2101 107.87 50.6401V85.3601H107.88Z" fill="white"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_2343_96406">
|
||||||
|
<rect width="136" height="136" rx="16" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.6 KiB |
8
packages/twenty-website/src/app/api/github/route.ts
Normal file
8
packages/twenty-website/src/app/api/github/route.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
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);
|
||||||
|
}
|
4
packages/twenty-website/src/app/blog/[slug]/page.tsx
Normal file
4
packages/twenty-website/src/app/blog/[slug]/page.tsx
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export default async function BlogPost({ params }: { params: { slug: string } }) {
|
||||||
|
const posts = {};
|
||||||
|
return <>Blog Post: {params.slug}</>;
|
||||||
|
}
|
0
packages/twenty-website/src/app/blog/list-posts.tsx
Normal file
0
packages/twenty-website/src/app/blog/list-posts.tsx
Normal file
6
packages/twenty-website/src/app/blog/page.tsx
Normal file
6
packages/twenty-website/src/app/blog/page.tsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
|
||||||
|
export default async function BlogHome() {
|
||||||
|
const posts = {};
|
||||||
|
return <>Blog Home</>;
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
export const ContentContainer = ({children}: {children?: React.ReactNode}) => {
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
width: '600px',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
}}>{children}</div>
|
||||||
|
)
|
||||||
|
}
|
106
packages/twenty-website/src/app/components/FooterNav.tsx
Normal file
106
packages/twenty-website/src/app/components/FooterNav.tsx
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import styled from '@emotion/styled'
|
||||||
|
import { Logo } from './Logo';
|
||||||
|
import { DiscordIcon, GithubIcon, LinkedInIcon, XIcon } from "./Icons";
|
||||||
|
|
||||||
|
|
||||||
|
const FooterContainer = styled.div`
|
||||||
|
padding: 64px 96px 64px 96px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
color: rgb(129, 129, 129);
|
||||||
|
gap: 32px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const LeftSideFooter = styled.div`
|
||||||
|
width: 36Opx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;`;
|
||||||
|
|
||||||
|
const RightSideFooter = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;`;
|
||||||
|
|
||||||
|
const RightSideFooterColumn = styled.div`
|
||||||
|
width: 160px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const RightSideFooterLink = styled.a`
|
||||||
|
color: rgb(129, 129, 129);
|
||||||
|
text-decoration: none;
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: #000;
|
||||||
|
}`;
|
||||||
|
|
||||||
|
const RightSideFooterColumnTitle = styled.div`
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #000;
|
||||||
|
`;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const FooterNav = () => {
|
||||||
|
return <FooterContainer>
|
||||||
|
<div style={{ width: '100%', display: 'flex', flexDirection: 'row', justifyContent:'space-between'}}>
|
||||||
|
<LeftSideFooter>
|
||||||
|
<Logo />
|
||||||
|
<div>
|
||||||
|
The #1 Open Source CRM
|
||||||
|
</div>
|
||||||
|
</LeftSideFooter>
|
||||||
|
<RightSideFooter>
|
||||||
|
<RightSideFooterColumn>
|
||||||
|
<RightSideFooterColumnTitle>Company</RightSideFooterColumnTitle>
|
||||||
|
<RightSideFooterLink href='/'>Pricing</RightSideFooterLink>
|
||||||
|
<RightSideFooterLink href='/'>Story</RightSideFooterLink>
|
||||||
|
</RightSideFooterColumn>
|
||||||
|
<RightSideFooterColumn>
|
||||||
|
<RightSideFooterColumnTitle>Resources</RightSideFooterColumnTitle>
|
||||||
|
<RightSideFooterLink href='https://docs.twenty.com'>Documentation</RightSideFooterLink>
|
||||||
|
<RightSideFooterLink href='/releases'>Changelog</RightSideFooterLink>
|
||||||
|
</RightSideFooterColumn>
|
||||||
|
<RightSideFooterColumn>
|
||||||
|
<RightSideFooterColumnTitle>Other</RightSideFooterColumnTitle>
|
||||||
|
<RightSideFooterLink href='/oss-friends'>OSS Friends</RightSideFooterLink>
|
||||||
|
<RightSideFooterLink href='/legal/terms'>Terms of Service</RightSideFooterLink>
|
||||||
|
<RightSideFooterLink href='/legal/privacy'>Privacy Policy</RightSideFooterLink>
|
||||||
|
</RightSideFooterColumn>
|
||||||
|
</RightSideFooter>
|
||||||
|
</div>
|
||||||
|
<div style={{
|
||||||
|
width: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent:'space-between',
|
||||||
|
borderTop: '1px solid rgb(179, 179, 179)',
|
||||||
|
paddingTop: '32px'
|
||||||
|
}}>
|
||||||
|
<div>
|
||||||
|
<span style={{fontFamily: "Inter, sans-serif"}}>©</span>
|
||||||
|
2023 Twenty PBC
|
||||||
|
</div>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'row', justifyContent:'space-between', gap:'10px'}}>
|
||||||
|
<a href="https://x.com/twentycrm" target="_blank">
|
||||||
|
<XIcon size='M'/>
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/twentyhq/twenty" target="_blank">
|
||||||
|
<GithubIcon size='M'/>
|
||||||
|
</a>
|
||||||
|
<a href="https://www.linkedin.com/company/twenty" target="_blank">
|
||||||
|
<LinkedInIcon size='M'/>
|
||||||
|
</a>
|
||||||
|
<a href="https://discord.gg/UfGNZJfAG6" target="_blank">
|
||||||
|
<DiscordIcon size='M' />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</FooterContainer>
|
||||||
|
;
|
||||||
|
}
|
133
packages/twenty-website/src/app/components/HeaderNav.tsx
Normal file
133
packages/twenty-website/src/app/components/HeaderNav.tsx
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
'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;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
overflow: visible;
|
||||||
|
padding: 12px 16px 12px 16px;
|
||||||
|
position: relative;
|
||||||
|
border-color: rgba(20, 20, 20, 0.08);
|
||||||
|
transform-origin: 50% 50% 0px;
|
||||||
|
border-bottom: 1px solid var(--Borders-Light, #F1F1F1);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const LinkList = styled.div`
|
||||||
|
display:flex;
|
||||||
|
flex-direction: row;
|
||||||
|
`;
|
||||||
|
|
||||||
|
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: 16px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const LinkNextToCTA = styled.a`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: rgb(71, 71, 71);
|
||||||
|
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>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const HeaderNav = () => {
|
||||||
|
|
||||||
|
const isTwentyDev = false;
|
||||||
|
|
||||||
|
return <Nav>
|
||||||
|
<LogoContainer>
|
||||||
|
<Logo />
|
||||||
|
{isTwentyDev && <LogoAddon className={IBMPlexMono.className}>for Developers</LogoAddon>}
|
||||||
|
</LogoContainer>
|
||||||
|
<LinkList>
|
||||||
|
<ListItem href="/pricing">Pricing</ListItem>
|
||||||
|
<ListItem href="/story">Story</ListItem>
|
||||||
|
<ListItem href="http://docs.twenty.com">Docs <ExternalArrow /></ListItem>
|
||||||
|
<ListItem href="http://docs.twenty.com"><GithubIcon color='rgb(71,71,71)' /> 5.7k <ExternalArrow /></ListItem>
|
||||||
|
</LinkList>
|
||||||
|
<CallToAction />
|
||||||
|
</Nav>;
|
||||||
|
};
|
||||||
|
|
41
packages/twenty-website/src/app/components/Icons.tsx
Normal file
41
packages/twenty-website/src/app/components/Icons.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
const getSize = size => {
|
||||||
|
switch(size) {
|
||||||
|
case 'S':
|
||||||
|
return '14px';
|
||||||
|
case 'M':
|
||||||
|
return '24px';
|
||||||
|
case 'L':
|
||||||
|
return '48px';
|
||||||
|
default:
|
||||||
|
return '14px';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GithubIcon = ({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 14 14"><path d="M 6.979 0 C 3.12 0 0 3.143 0 7.031 C 0 10.139 1.999 12.77 4.772 13.701 C 5.119 13.771 5.246 13.55 5.246 13.364 C 5.246 13.201 5.234 12.642 5.234 12.06 C 3.293 12.479 2.889 11.222 2.889 11.222 C 2.577 10.407 2.114 10.197 2.114 10.197 C 1.479 9.767 2.161 9.767 2.161 9.767 C 2.866 9.813 3.235 10.488 3.235 10.488 C 3.859 11.559 4.865 11.257 5.269 11.07 C 5.327 10.616 5.512 10.302 5.708 10.127 C 4.16 9.964 2.531 9.359 2.531 6.658 C 2.531 5.89 2.808 5.262 3.247 4.773 C 3.178 4.598 2.935 3.876 3.316 2.91 C 3.316 2.91 3.906 2.724 5.234 3.632 C 5.803 3.478 6.39 3.4 6.979 3.399 C 7.568 3.399 8.169 3.481 8.724 3.632 C 10.053 2.724 10.642 2.91 10.642 2.91 C 11.023 3.876 10.781 4.598 10.711 4.773 C 11.162 5.262 11.428 5.89 11.428 6.658 C 11.428 9.359 9.799 9.953 8.239 10.127 C 8.493 10.349 8.712 10.768 8.712 11.431 C 8.712 12.374 8.701 13.131 8.701 13.363 C 8.701 13.55 8.828 13.771 9.175 13.701 C 11.948 12.77 13.947 10.139 13.947 7.031 C 13.958 3.143 10.827 0 6.979 0 Z" fill={color}></path></svg>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LinkedInIcon = ({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="M216,24H40A16,16,0,0,0,24,40V216a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V40A16,16,0,0,0,216,24Zm0,192H40V40H216V216ZM96,112v64a8,8,0,0,1-16,0V112a8,8,0,0,1,16,0Zm88,28v36a8,8,0,0,1-16,0V140a20,20,0,0,0-40,0v36a8,8,0,0,1-16,0V112a8,8,0,0,1,15.79-1.78A36,36,0,0,1,184,140ZM100,84A12,12,0,1,1,88,72,12,12,0,0,1,100,84Z" fill={color}></path></g></svg>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DiscordIcon = ({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="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>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const XIcon = ({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="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>
|
||||||
|
</div>
|
||||||
|
}
|
16
packages/twenty-website/src/app/components/Logo.tsx
Normal file
16
packages/twenty-website/src/app/components/Logo.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import styled from "@emotion/styled";
|
||||||
|
|
||||||
|
const Link = styled.a`
|
||||||
|
display:block;
|
||||||
|
image-rendering: pixelated;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
border-radius: 8px;
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
background-image: url("images/core/logo.svg");
|
||||||
|
opacity: 1;`;
|
||||||
|
|
||||||
|
export const Logo = () => {
|
||||||
|
return <Link href="/" />;
|
||||||
|
};
|
@ -0,0 +1,47 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { CacheProvider } from '@emotion/react'
|
||||||
|
import createCache from '@emotion/cache'
|
||||||
|
import { useServerInsertedHTML } from 'next/navigation'
|
||||||
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
export default function RootStyleRegistry({ children }) {
|
||||||
|
const [{ cache, flush }] = useState(() => {
|
||||||
|
const cache = createCache({ key: 'emotion-cache' })
|
||||||
|
cache.compat = true
|
||||||
|
const prevInsert = cache.insert
|
||||||
|
let inserted = []
|
||||||
|
cache.insert = (...args) => {
|
||||||
|
const serialized = args[1]
|
||||||
|
if (cache.inserted[serialized.name] === undefined) {
|
||||||
|
inserted.push(serialized.name)
|
||||||
|
}
|
||||||
|
return prevInsert(...args)
|
||||||
|
}
|
||||||
|
const flush = () => {
|
||||||
|
const prevInserted = inserted
|
||||||
|
inserted = []
|
||||||
|
return prevInserted
|
||||||
|
}
|
||||||
|
return { cache, flush }
|
||||||
|
})
|
||||||
|
|
||||||
|
useServerInsertedHTML(() => {
|
||||||
|
const names = flush()
|
||||||
|
if (names.length === 0) return null
|
||||||
|
let styles = ''
|
||||||
|
for (const name of names) {
|
||||||
|
styles += cache.inserted[name]
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<style
|
||||||
|
data-emotion={`${cache.key} ${names.join(' ')}`}
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: styles,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
return <CacheProvider value={cache}>{children}</CacheProvider>
|
||||||
|
}
|
43
packages/twenty-website/src/app/layout.tsx
Normal file
43
packages/twenty-website/src/app/layout.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import type { Metadata } from 'next'
|
||||||
|
import { Gabarito } from 'next/font/google'
|
||||||
|
import EmotionRootStyleRegistry from './emotion-root-style-registry'
|
||||||
|
import styled from '@emotion/styled'
|
||||||
|
import { HeaderNav } from './components/HeaderNav'
|
||||||
|
import { FooterNav } from './components/FooterNav'
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: 'Twenty.dev',
|
||||||
|
description: 'Twenty for Developer',
|
||||||
|
icons: '/favicon.ico',
|
||||||
|
}
|
||||||
|
|
||||||
|
const gabarito = Gabarito({
|
||||||
|
weight: ['400', '500'],
|
||||||
|
subsets: ['latin'],
|
||||||
|
display: 'swap',
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<html lang="en" className={gabarito.className}>
|
||||||
|
<body style={{
|
||||||
|
margin: 0,
|
||||||
|
WebkitFontSmoothing: "antialiased",
|
||||||
|
MozOsxFontSmoothing: "grayscale"
|
||||||
|
}}>
|
||||||
|
<EmotionRootStyleRegistry>
|
||||||
|
<HeaderNav />
|
||||||
|
<div style={{display: 'flex', flexDirection: 'column', alignItems: 'center'}}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
<FooterNav />
|
||||||
|
</EmotionRootStyleRegistry>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
)
|
||||||
|
}
|
14
packages/twenty-website/src/app/page.tsx
Normal file
14
packages/twenty-website/src/app/page.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import Image from 'next/image'
|
||||||
|
|
||||||
|
import { ContentContainer } from './components/ContentContainer'
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<ContentContainer>
|
||||||
|
<div style={{ minHeight: '60vh', marginTop: '50px' }}>
|
||||||
|
Part of the website is built directly with Framer, including the homepage. <br />
|
||||||
|
We use Clouflare to split the traffic between the two sites.
|
||||||
|
</div>
|
||||||
|
</ContentContainer>
|
||||||
|
)
|
||||||
|
}
|
68
packages/twenty-website/src/app/releases/page.tsx
Normal file
68
packages/twenty-website/src/app/releases/page.tsx
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { GetStaticProps } from 'next'
|
||||||
|
import { MDXRemote } from 'next-mdx-remote/rsc'
|
||||||
|
import { compileMDX } from 'next-mdx-remote/rsc'
|
||||||
|
import gfm from 'remark-gfm';
|
||||||
|
import { ContentContainer } from '../components/ContentContainer';
|
||||||
|
import { visit } from 'unist-util-visit';
|
||||||
|
import remarkBehead from 'remark-behead';
|
||||||
|
|
||||||
|
|
||||||
|
interface Release {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
body: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const Home = async () => {
|
||||||
|
const res = await fetch(`${process.env.BASE_URL}/api/github`);
|
||||||
|
const data: Release[] = await res.json();
|
||||||
|
|
||||||
|
const releases = await Promise.all(
|
||||||
|
data.map(async (release) => {
|
||||||
|
let mdxSource;
|
||||||
|
try {
|
||||||
|
mdxSource = await compileMDX({
|
||||||
|
source: release.body,
|
||||||
|
options: {
|
||||||
|
mdxOptions: {
|
||||||
|
remarkPlugins: [
|
||||||
|
gfm,
|
||||||
|
[remarkBehead, { depth: 2 }],
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
mdxSource = mdxSource.content;
|
||||||
|
} catch(error) {
|
||||||
|
console.error('An error occurred during MDX rendering:', error);
|
||||||
|
mdxSource = `<p>Oops! Something went wrong.</p> ${error}`;;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: release.id,
|
||||||
|
name: release.name,
|
||||||
|
body: mdxSource,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ContentContainer>
|
||||||
|
<h1>Releases</h1>
|
||||||
|
|
||||||
|
{releases.map(release => (
|
||||||
|
<div key={release.id}
|
||||||
|
style={{
|
||||||
|
padding: '24px 0px 24px 0px',
|
||||||
|
borderBottom: '1px solid #ccc',
|
||||||
|
}}>
|
||||||
|
<h2>{release.name}</h2>
|
||||||
|
<div>{release.body}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</ContentContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Home;
|
27
packages/twenty-website/tsconfig.json
Normal file
27
packages/twenty-website/tsconfig.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"incremental": true,
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "next"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "src/app/emotion-root-style-registry.js"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
4717
packages/twenty-website/yarn.lock
Normal file
4717
packages/twenty-website/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user