diff --git a/packages/twenty-website/src/app/_components/releases/Line.tsx b/packages/twenty-website/src/app/_components/releases/Line.tsx new file mode 100644 index 000000000..5d1f564fb --- /dev/null +++ b/packages/twenty-website/src/app/_components/releases/Line.tsx @@ -0,0 +1,36 @@ +'use client'; + +import styled from '@emotion/styled'; + +const StyledLineContainer = styled.div` + width: 810px; + margin: 0 auto; + display: flex; + + @media (max-width: 810px) { + width: auto; + margin: 24px 0; + display: block; + } +`; + +const StyledLine = styled.div` + height: 1px; + background-color: #d9d9d9; + margin-bottom: 48px; + margin-left: 148px; + margin-top: 48px; + width: 100%; + + @media (max-width: 810px) { + margin: 0; + } +`; + +export const Line = () => { + return ( + + + + ); +}; diff --git a/packages/twenty-website/src/app/_components/releases/Release.tsx b/packages/twenty-website/src/app/_components/releases/Release.tsx new file mode 100644 index 000000000..d6a33e2a0 --- /dev/null +++ b/packages/twenty-website/src/app/_components/releases/Release.tsx @@ -0,0 +1,110 @@ +'use client'; + +import styled from '@emotion/styled'; +import { Gabarito } from 'next/font/google'; +import { compileMDX } from 'next-mdx-remote/rsc'; +import remarkBehead from 'remark-behead'; +import gfm from 'remark-gfm'; + +import { ReleaseNote } from '@/app/get-releases'; + +const StyledContainer = styled.div` + width: 810px; + margin: 0 auto; + display: flex; + font-weight: 400; + + @media (max-width: 810px) { + width: auto; + margin: 24px 0; + display: block; + } +`; + +const StyledVersion = styled.div` + text-align: center; + width: 148px; + font-size: 24px; + display: flex; + flex-flow: column; + align-items: start; + font-weight: 500; + + @media (max-width: 810px) { + width: 100%; + font-size: 20px; + flex-flow: row; + justify-content: space-between; + } +`; + +const StyledRelease = styled.span` + color: #b3b3b3; +`; + +const StyledDate = styled.span` + color: #474747; + font-size: 16px; +`; + +const StlyedContent = styled.div` + flex: 1; + + h3 { + color: #141414; + font-size: 40px; + margin: 0; + } + + p { + color: #474747; + font-size: 16px; + line-height: 28.8px; + color: #818181; + font-weight: 400; + } + + img { + max-width: 100%; + } +`; + +const gabarito = Gabarito({ + weight: ['400', '500'], + subsets: ['latin'], + display: 'swap', + adjustFontFallback: false, +}); + +export const Release = async ({ release }: { release: ReleaseNote }) => { + let mdxSource; + try { + mdxSource = await compileMDX({ + source: release.content, + options: { + mdxOptions: { + remarkPlugins: [gfm, [remarkBehead, { depth: 2 }]], + }, + }, + }); + mdxSource = mdxSource.content; + } catch (error) { + console.error('An error occurred during MDX rendering:', error); + mdxSource = `

Oops! Something went wrong.

${error}`; + } + + return ( + + + {release.release} + + {release.date.endsWith(new Date().getFullYear().toString()) + ? release.date.slice(0, -5) + : release.date} + + + + {mdxSource} + + ); +}; diff --git a/packages/twenty-website/src/app/_components/releases/StyledTitle.tsx b/packages/twenty-website/src/app/_components/releases/StyledTitle.tsx new file mode 100644 index 000000000..816cc23b1 --- /dev/null +++ b/packages/twenty-website/src/app/_components/releases/StyledTitle.tsx @@ -0,0 +1,30 @@ +'use client'; + +import styled from '@emotion/styled'; + +const StyledTitle = styled.div` + margin: 64px auto; + text-align: center; + font-size: 1.8em; + + @media (max-width: 810px) { + font-size: 1em; + margin: 16px auto; + } +`; +const StyledHeader = styled.h1` + color: #b3b3b3; + margin: 0; +`; +const StyledSubHeader = styled.h1` + margin: 0; +`; + +export const Title = () => { + return ( + + Latest + Releases + + ); +}; diff --git a/packages/twenty-website/src/app/get-releases.tsx b/packages/twenty-website/src/app/get-releases.tsx new file mode 100644 index 000000000..c9691b786 --- /dev/null +++ b/packages/twenty-website/src/app/get-releases.tsx @@ -0,0 +1,49 @@ +import fs from 'fs'; +import matter from 'gray-matter'; + +export interface ReleaseNote { + slug: string; + date: string; + release: string; + content: string; +} + +function compareSemanticVersions(a: string, b: string) { + const a1 = a.split('.'); + const b1 = b.split('.'); + + const len = Math.min(a1.length, b1.length); + + for (let i = 0; i < len; i++) { + const a2 = +a1[i] || 0; + const b2 = +b1[i] || 0; + + if (a2 !== b2) { + return a2 > b2 ? 1 : -1; + } + } + return b1.length - a1.length; +} + +export async function getReleases(): Promise { + const files = fs.readdirSync('src/content/releases'); + const releasenotes: ReleaseNote[] = []; + + for (const fileName of files) { + if (!fileName.endsWith('.md') && !fileName.endsWith('.mdx')) { + continue; + } + const file = fs.readFileSync(`src/content/releases/${fileName}`, 'utf-8'); + const { data: frontmatter, content } = matter(file); + releasenotes.push({ + slug: fileName.slice(0, -4), + date: frontmatter.Date, + release: frontmatter.release, + content: content, + }); + } + + releasenotes.sort((a, b) => compareSemanticVersions(b.release, a.release)); + + return releasenotes; +} diff --git a/packages/twenty-website/src/app/releases/page.tsx b/packages/twenty-website/src/app/releases/page.tsx new file mode 100644 index 000000000..fa820f3ae --- /dev/null +++ b/packages/twenty-website/src/app/releases/page.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { Metadata } from 'next'; + +import { Line } from '@/app/_components/releases/Line'; +import { Release } from '@/app/_components/releases/Release'; +import { Title } from '@/app/_components/releases/StyledTitle'; +import { ContentContainer } from '@/app/_components/ui/layout/ContentContainer'; +import { getReleases } from '@/app/get-releases'; + +export const metadata: Metadata = { + title: 'Twenty - Releases', + description: 'Latest releases of Twenty', +}; + +const Home = async () => { + const releases = await getReleases(); + + return ( + + + + {releases.map((note, index) => ( + <React.Fragment key={note.slug}> + <Release release={note} /> + {index != releases.length - 1 && <Line />} + </React.Fragment> + ))} + </ContentContainer> + ); +}; + +export default Home; diff --git a/packages/twenty-website/src/content/releases/CompactView.mdx b/packages/twenty-website/src/content/releases/CompactView.mdx new file mode 100644 index 000000000..24078f610 --- /dev/null +++ b/packages/twenty-website/src/content/releases/CompactView.mdx @@ -0,0 +1,11 @@ +--- +release: 0.3.2 +Date: May 6th 2024 +--- + +# Compact view + + +Standard objects are in-built objects with a set of attributes available for all users. All workspaces come with three standard objects by default: People, Companies, and Opportunities. Standard objects have standard fields that are also available for all Twenty users, like Account Owner and URL. + +![](/images/user-guide/view-all-objects-light.png) \ No newline at end of file diff --git a/packages/twenty-website/src/content/releases/Inbox.mdx b/packages/twenty-website/src/content/releases/Inbox.mdx new file mode 100644 index 000000000..4a76b8cdb --- /dev/null +++ b/packages/twenty-website/src/content/releases/Inbox.mdx @@ -0,0 +1,10 @@ +--- +release: 0.4.3 +Date: February 24th 2024 +--- + +# Inbox + + + +An inbox gathering task in a CRM system is an automated process that collects and organizes various types of communications such as notes, emails, and notifications. Notes are often summaries or details related to customer interactions that can be recorded and tracked. This system also archives and prioritizes emails and notifications, making it easier to manage customer relationships and ensure all communication is accounted for and responded to in a timely manner. \ No newline at end of file