diff --git a/packages/twenty-website/src/app/components/AvatarGrid.tsx b/packages/twenty-website/src/app/components/AvatarGrid.tsx index 5332eb3ae..833525d5b 100644 --- a/packages/twenty-website/src/app/components/AvatarGrid.tsx +++ b/packages/twenty-website/src/app/components/AvatarGrid.tsx @@ -9,14 +9,29 @@ export interface User { } const AvatarGridContainer = styled.div` - display: grid; - grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); - grid-gap: 10px; + margin: 0 auto; + max-width: 1024px; + justify-items: center; + display: flex; + justify-content: center; + gap: 32px; + flex-wrap: wrap; `; const AvatarItem = styled.div` position: relative; - width: 100%; + width: 124px; + height: 124px; + border: 3px solid #141414; + border-radius: 16px; + overflow: hidden; + transition: 200ms; + + &:hover { + -webkit-box-shadow: -6px 6px 0px 1px rgba(0, 0, 0, 1); + -moz-box-shadow: -6px 6px 0px 1px rgba(0, 0, 0, 1); + box-shadow: -6px 6px 0px 1px rgba(0, 0, 0, 1); + } img { width: 100%; diff --git a/packages/twenty-website/src/app/components/Breadcrumbs.tsx b/packages/twenty-website/src/app/components/Breadcrumbs.tsx new file mode 100644 index 000000000..045dbd7be --- /dev/null +++ b/packages/twenty-website/src/app/components/Breadcrumbs.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import styled from '@emotion/styled'; +import Link from 'next/link'; + +const Container = styled.div` + display: flex; + gap: 8px; + color: #b3b3b3; +`; + +const InternalLinkItem = styled(Link)` + text-decoration: none; + color: #b3b3b3; +`; + +const ExternalLinkItem = styled.a` + text-decoration: none; + color: #b3b3b3; +`; + +const ActivePage = styled.span` + color: #818181; +`; + +interface BreadcrumbsProps { + items: { + uri: string; + label: string; + isExternal?: boolean; + }[]; + activePage: string; + separator: string; +} + +export const Breadcrumbs = ({ + items, + activePage, + separator, +}: BreadcrumbsProps) => { + return ( + + {items.map((item, index) => ( + + {item.isExternal ? ( + {item.label} + ) : ( + {item.label} + )} + {separator} + + ))} + {activePage} + + ); +}; diff --git a/packages/twenty-website/src/app/components/HeaderDesktop.tsx b/packages/twenty-website/src/app/components/HeaderDesktop.tsx index d91b88f91..2a081465a 100644 --- a/packages/twenty-website/src/app/components/HeaderDesktop.tsx +++ b/packages/twenty-website/src/app/components/HeaderDesktop.tsx @@ -2,11 +2,10 @@ import styled from '@emotion/styled'; import { IBM_Plex_Mono } from 'next/font/google'; -import { usePathname } from 'next/navigation'; import { ExternalArrow } from '@/app/components/ExternalArrow'; -import { DiscordIcon, GithubIcon, GithubIcon2, XIcon } from './Icons'; +import { GithubIcon } from './Icons'; import { Logo } from './Logo'; const IBMPlexMono = IBM_Plex_Mono({ @@ -102,88 +101,34 @@ const LinkNextToCTA = styled.a` `; const CallToAction = () => { - const path = usePathname(); - const isTwentyDev = path.includes('developers'); - return ( - {isTwentyDev ? ( - <> -
- - - - - - - - - -
- - ) : ( - <> - - Sign in - - - Get Started - - - )} + + Sign in + + + Get Started +
); }; export const HeaderDesktop = () => { - const path = usePathname(); - const isTwentyDev = path.includes('developers'); - return ( ); diff --git a/packages/twenty-website/src/app/components/HeaderMobile.tsx b/packages/twenty-website/src/app/components/HeaderMobile.tsx index e7f7e29e6..207caf880 100644 --- a/packages/twenty-website/src/app/components/HeaderMobile.tsx +++ b/packages/twenty-website/src/app/components/HeaderMobile.tsx @@ -36,6 +36,7 @@ const LinkList = styled.div` display: flex; flex-direction: column; gap: 16px; + align-items: center; `; const ListItem = styled.a` @@ -45,7 +46,7 @@ const ListItem = styled.a` gap: 4px; align-items: center; border-radius: 8px; - height: 40px; + height: 48px; padding-left: 16px; padding-right: 16px; &:hover { @@ -174,6 +175,9 @@ const NavOpen = styled.div` gap: 33px; padding-top: 32px; z-index: 100; + transition: transform 0.2s ease-in; + display: flex; + transform-origin: top; `; const MobileMenu = styled.div` @@ -208,15 +212,19 @@ export const HeaderMobile = () => { - + - Pricing Story + Pricing Docs - 5.7k + 8.3k diff --git a/packages/twenty-website/src/app/components/Icons.tsx b/packages/twenty-website/src/app/components/Icons.tsx index b851db6cb..661b4021c 100644 --- a/packages/twenty-website/src/app/components/Icons.tsx +++ b/packages/twenty-website/src/app/components/Icons.tsx @@ -7,7 +7,7 @@ const getSize = (size: string) => { case 'L': return '48px'; default: - return '14px'; + return size; } }; @@ -117,3 +117,38 @@ export const GithubIcon2 = ({ size = 'S', color = 'rgb(179, 179, 179)' }) => { ); }; + +export const PullRequestIcon = ({ size = 'S', color = 'rgb(179,179,179)' }) => { + const dimension = getSize(size); + return ( +
+ + + +
+ ); +}; + +export const HeartIcon = ({ size = 'S', color = 'rgb(179,179,179)' }) => { + const dimension = getSize(size); + return ( +
+ + + +
+ ); +}; diff --git a/packages/twenty-website/src/app/components/developers/contributors/Header.tsx b/packages/twenty-website/src/app/components/developers/contributors/Header.tsx new file mode 100644 index 000000000..d9a8509bb --- /dev/null +++ b/packages/twenty-website/src/app/components/developers/contributors/Header.tsx @@ -0,0 +1,25 @@ +'use client'; + +import styled from '@emotion/styled'; + +const Title = styled.h2` + font-size: 56px; + font-weight: 600; + color: #b3b3b3; + margin-bottom: 0px; + margin-top: 64px; + + @media (max-width: 810px) { + font-size: 28px; + } +`; + +export const Header = () => { + return ( + <> + + Our amazing <br /> <span style={{ color: 'black' }}>Contributors</span> + + + ); +}; diff --git a/packages/twenty-website/src/app/components/oss-friends/Background.tsx b/packages/twenty-website/src/app/components/oss-friends/Background.tsx index 83040d678..f97b976d4 100644 --- a/packages/twenty-website/src/app/components/oss-friends/Background.tsx +++ b/packages/twenty-website/src/app/components/oss-friends/Background.tsx @@ -7,19 +7,20 @@ const BackgroundContainer = styled.div` top: 100%; left: 0px; width: 100%; - height: 200%; + height: 100%; + max-height: 200%; background-image: url(https://framerusercontent.com/images/nqEmdwe7yDXNsOZovuxG5zvj2E.png); background-size: auto 20px; background-repeat: repeat; transform-origin: center center; z-index: -2; - } `; const Gradient = styled.div` position: absolute; width: 100%; - height: 200%; + height: 100%; + max-height: 200%; top: 100%; left: 0; right: 0; diff --git a/packages/twenty-website/src/app/developers/contributors/[slug]/components/ActivityLog.tsx b/packages/twenty-website/src/app/developers/contributors/[slug]/components/ActivityLog.tsx index e7e193465..fbe842f93 100644 --- a/packages/twenty-website/src/app/developers/contributors/[slug]/components/ActivityLog.tsx +++ b/packages/twenty-website/src/app/developers/contributors/[slug]/components/ActivityLog.tsx @@ -2,10 +2,28 @@ import { ResponsiveTimeRange } from '@nivo/calendar'; +import { CardContainer } from '@/app/developers/contributors/[slug]/components/CardContainer'; +import { Title } from '@/app/developers/contributors/[slug]/components/Title'; + export const ActivityLog = ({ data, }: { data: { value: number; day: string }[]; }) => { - return ; + return ( + + Activity +
+ +
+
+ ); }; diff --git a/packages/twenty-website/src/app/developers/contributors/[slug]/components/Breadcrumb.tsx b/packages/twenty-website/src/app/developers/contributors/[slug]/components/Breadcrumb.tsx new file mode 100644 index 000000000..b5fea70bf --- /dev/null +++ b/packages/twenty-website/src/app/developers/contributors/[slug]/components/Breadcrumb.tsx @@ -0,0 +1,16 @@ +'use client'; + +import { Breadcrumbs } from '@/app/components/Breadcrumbs'; + +const BREADCRUMB_ITEMS = [ + { + uri: '/developers/contributors', + label: 'Contributors', + }, +]; + +export const Breadcrumb = ({ active }: { active: string }) => { + return ( + + ); +}; diff --git a/packages/twenty-website/src/app/developers/contributors/[slug]/components/CardContainer.tsx b/packages/twenty-website/src/app/developers/contributors/[slug]/components/CardContainer.tsx new file mode 100644 index 000000000..7a2f828be --- /dev/null +++ b/packages/twenty-website/src/app/developers/contributors/[slug]/components/CardContainer.tsx @@ -0,0 +1,17 @@ +import styled from '@emotion/styled'; + +export const CardContainer = styled.div` + border: 3px solid #141414; + width: 100%; + border-radius: 12px; + padding: 40px; + display: flex; + gap: 32px; + flex-direction: column; + background-color: #fafafa; + + @media (max-width: 810px) { + gap: 16px; + padding: 24px; + } +`; diff --git a/packages/twenty-website/src/app/developers/contributors/[slug]/components/ContentContainer.tsx b/packages/twenty-website/src/app/developers/contributors/[slug]/components/ContentContainer.tsx new file mode 100644 index 000000000..a24728a7f --- /dev/null +++ b/packages/twenty-website/src/app/developers/contributors/[slug]/components/ContentContainer.tsx @@ -0,0 +1,26 @@ +'use client'; + +import styled from '@emotion/styled'; + +const Container = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + width: 100%; + max-width: 898px; + padding: 40px; + + gap: 40px; + @media (max-width: 809px) { + width: 100%; + padding: 40px 24px 40px 24px; + } +`; + +export const ContentContainer = ({ + children, +}: { + children?: React.ReactNode; +}) => { + return {children}; +}; diff --git a/packages/twenty-website/src/app/developers/contributors/[slug]/components/ProfileCard.tsx b/packages/twenty-website/src/app/developers/contributors/[slug]/components/ProfileCard.tsx new file mode 100644 index 000000000..f88a8d8b2 --- /dev/null +++ b/packages/twenty-website/src/app/developers/contributors/[slug]/components/ProfileCard.tsx @@ -0,0 +1,100 @@ +'use client'; + +import styled from '@emotion/styled'; +import { format } from 'date-fns'; + +import { GithubIcon } from '@/app/components/Icons'; + +const ProfileContainer = styled.div` + display: flex; + gap: 32px; + width: 100%; + align-items: center; +`; + +const Avatar = styled.div` + border: 3px solid #141414; + width: 96px; + height: 96px; + border-radius: 16px; + overflow: hidden; + flex-shrink: 0; + + img { + width: 100%; + display: block; + height: 100%; + object-fit: cover; + } +`; + +const Details = styled.div` + display: flex; + flex-direction: column; + gap: 4px; + + .username { + font-size: 40px; + font-weight: 700; + line-height: 48px; + margin: 0; + display: flex; + gap: 12px; + + @media (max-width: 810px) { + font-size: 24px; + line-height: 28.8px; + } + } + + .duration { + font-size: 24px; + font-weight: 400; + color: #818181; + margin: 0; + + @media (max-width: 810px) { + font-size: 20px; + line-height: 28px; + } + } +`; + +const StyledGithubIcon = styled(GithubIcon)` + @media (max-width: 810px) { + width: 20px; + height: 20px; + } +`; + +interface ProfileCardProps { + username: string; + avatarUrl: string; + firstContributionAt: string; +} + +export const ProfileCard = ({ + username, + avatarUrl, + firstContributionAt, +}: ProfileCardProps) => { + return ( + + + {username} + +
+

+ @{username} + + + +

+

+ Contributing since{' '} + {format(new Date(firstContributionAt), 'MMMM yyyy')} +

+
+
+ ); +}; diff --git a/packages/twenty-website/src/app/developers/contributors/[slug]/components/ProfileInfo.tsx b/packages/twenty-website/src/app/developers/contributors/[slug]/components/ProfileInfo.tsx new file mode 100644 index 000000000..2e2c9135c --- /dev/null +++ b/packages/twenty-website/src/app/developers/contributors/[slug]/components/ProfileInfo.tsx @@ -0,0 +1,86 @@ +'use client'; + +import styled from '@emotion/styled'; + +import { CardContainer } from '@/app/developers/contributors/[slug]/components/CardContainer'; + +const Container = styled(CardContainer)` + flex-direction: row; + + @media (max-width: 810px) { + flex-direction: column; + } + + .item { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + flex-grow: 1; + flex-basis: 0; + + .title { + font-size: 24px; + color: #b3b3b3; + margin: 0; + + @media (max-width: 810px) { + font-size: 20px; + } + } + + .value { + font-size: 56px; + font-weight: 700; + color: #474747; + + @media (max-width: 810px) { + font-size: 32px; + } + } + } + + .separator { + width: 2px; + background-color: #141414; + border-radius: 40px; + + @media (max-width: 810px) { + width: 100%; + height: 2px; + } + } +`; + +interface ProfileInfoProps { + mergedPRsCount: number; + rank: string; + activeDays: number; +} + +export const ProfileInfo = ({ + mergedPRsCount, + rank, + activeDays, +}: ProfileInfoProps) => { + return ( + <> + +
+

Merged PR

+ {mergedPRsCount} +
+
+
+

Rank

+ {rank}% +
+
+
+

Active Days

+ {activeDays} +
+
+ + ); +}; diff --git a/packages/twenty-website/src/app/developers/contributors/[slug]/components/PullRequestItem.tsx b/packages/twenty-website/src/app/developers/contributors/[slug]/components/PullRequestItem.tsx new file mode 100644 index 000000000..0b1655fbf --- /dev/null +++ b/packages/twenty-website/src/app/developers/contributors/[slug]/components/PullRequestItem.tsx @@ -0,0 +1,94 @@ +import { Tooltip } from 'react-tooltip'; +import styled from '@emotion/styled'; +import { format } from 'date-fns'; + +import { PullRequestIcon } from '@/app/components/Icons'; +import { formatIntoRelativeDate } from '@/lib/utils'; + +const StyledTooltip = styled(Tooltip)``; + +const Item = styled.div` + display: flex; + gap: 17px; +`; + +const StyledTitle = styled.a` + font-size: 24px; + font-weight: 400; + color: #474747; + margin: 0; + text-decoration: none; + + @media (max-width: 810px) { + font-size: 20px; + } +`; + +const StyledDescription = styled.div` + font-size: 20px; + line-height: 28px; + font-weight: 500; + color: #b3b3b3; + + @media (max-width: 810px) { + font-size: 18px; + } +`; + +const StyledPullRequestIcon = styled(PullRequestIcon)` + @media screen { + width: 20px; + height: 20px; + } +`; + +interface PullRequestItemProps { + id: string; + title: string; + url: string; + createdAt: string; + mergedAt: string | null; + authorId: string; +} + +export const PullRequestItem = ({ + id, + title, + url, + createdAt, + mergedAt, + authorId, +}: PullRequestItemProps) => { + const prNumber = url.split('/').slice(-1)[0]; + return ( + +
+ +
+
+ + {title} + + + #{prNumber} by {authorId.slice(1)} was{' '} + {mergedAt ? `merged` : `opened`}{' '} + + {formatIntoRelativeDate(mergedAt ? mergedAt : createdAt)} + + + +
+
+ ); +}; diff --git a/packages/twenty-website/src/app/developers/contributors/[slug]/components/PullRequests.tsx b/packages/twenty-website/src/app/developers/contributors/[slug]/components/PullRequests.tsx new file mode 100644 index 000000000..869730c15 --- /dev/null +++ b/packages/twenty-website/src/app/developers/contributors/[slug]/components/PullRequests.tsx @@ -0,0 +1,44 @@ +'use client'; + +import styled from '@emotion/styled'; + +import { CardContainer } from '@/app/developers/contributors/[slug]/components/CardContainer'; +import { PullRequestItem } from '@/app/developers/contributors/[slug]/components/PullRequestItem'; +import { Title } from '@/app/developers/contributors/[slug]/components/Title'; + +const List = styled.div` + display: flex; + gap: 24px; + flex-direction: column; +`; +interface PullRequestsProps { + list: { + id: string; + title: string; + url: string; + createdAt: string; + mergedAt: string | null; + authorId: string; + }[]; +} + +export const PullRequests = ({ list }: PullRequestsProps) => { + return ( + + Pull Requests + + {list.map((pr) => ( + + ))} + + + ); +}; diff --git a/packages/twenty-website/src/app/developers/contributors/[slug]/components/ThankYou.tsx b/packages/twenty-website/src/app/developers/contributors/[slug]/components/ThankYou.tsx new file mode 100644 index 000000000..4a93eb6ba --- /dev/null +++ b/packages/twenty-website/src/app/developers/contributors/[slug]/components/ThankYou.tsx @@ -0,0 +1,38 @@ +'use client'; + +import styled from '@emotion/styled'; + +import { HeartIcon } from '@/app/components/Icons'; +import { CardContainer } from '@/app/developers/contributors/[slug]/components/CardContainer'; + +const StyledTitle = styled.div` + display: flex; + font-size: 24px; + font-weight: 500; + gap: 8px; + + @media (max-width: 810px) { + font-size: 20px; + } +`; + +const StyledHeartIcon = styled(HeartIcon)` + @media (max-width: 810px) { + width: 16px !important; + height: 16px !important; + } +`; + +interface ThankYouProps { + authorId: string; +} + +export const ThankYou = ({ authorId }: ThankYouProps) => { + return ( + + + Thank you @{authorId} + + + ); +}; diff --git a/packages/twenty-website/src/app/developers/contributors/[slug]/components/Title.tsx b/packages/twenty-website/src/app/developers/contributors/[slug]/components/Title.tsx new file mode 100644 index 000000000..8e15d0654 --- /dev/null +++ b/packages/twenty-website/src/app/developers/contributors/[slug]/components/Title.tsx @@ -0,0 +1,13 @@ +import styled from '@emotion/styled'; + +export const Title = styled.h3` + margin: 0; + font-size: 32px; + line-height: 41.6px; + font-weight: 500; + + @media (max-width: 810px) { + font-size: 24px; + line-height: 31.2px; + } +`; diff --git a/packages/twenty-website/src/app/developers/contributors/[slug]/page.tsx b/packages/twenty-website/src/app/developers/contributors/[slug]/page.tsx index 694640b09..309c817ae 100644 --- a/packages/twenty-website/src/app/developers/contributors/[slug]/page.tsx +++ b/packages/twenty-website/src/app/developers/contributors/[slug]/page.tsx @@ -1,8 +1,14 @@ import Database from 'better-sqlite3'; import { Metadata } from 'next'; -import Image from 'next/image'; -import { ActivityLog } from './components/ActivityLog'; +import { Background } from '@/app/components/oss-friends/Background'; +import { ActivityLog } from '@/app/developers/contributors/[slug]/components/ActivityLog'; +import { Breadcrumb } from '@/app/developers/contributors/[slug]/components/Breadcrumb'; +import { ContentContainer } from '@/app/developers/contributors/[slug]/components/ContentContainer'; +import { ProfileCard } from '@/app/developers/contributors/[slug]/components/ProfileCard'; +import { ProfileInfo } from '@/app/developers/contributors/[slug]/components/ProfileInfo'; +import { PullRequests } from '@/app/developers/contributors/[slug]/components/PullRequests'; +import { ThankYou } from '@/app/developers/contributors/[slug]/components/ThankYou'; interface Contributor { login: string; @@ -57,6 +63,7 @@ export default async function ({ params }: { params: { slug: string } }) { ) .all({ user_id: params.slug }) as { value: number; day: string }[]; + // Latest PRs. const pullRequestList = db .prepare( ` @@ -68,53 +75,88 @@ export default async function ({ params }: { params: { slug: string } }) { createdAt, updatedAt, closedAt, - mergedAt + mergedAt, + authorId FROM pullRequests WHERE authorId = (SELECT id FROM users WHERE login = :user_id) ORDER BY DATE(createdAt) DESC + LIMIT + 10 `, ) .all({ user_id: params.slug }) as { title: string; createdAt: string; url: string; + id: string; + mergedAt: string | null; + authorId: string; + }[]; + + const mergedPullRequests = db + .prepare( + ` + SELECT * FROM ( + SELECT + merged_pr_counts.*, + (RANK() OVER(ORDER BY merged_count) - 1) / CAST( total_authors as float) * 100 as rank_percentage + FROM + ( + SELECT + authorId, + COUNT(*) FILTER (WHERE mergedAt IS NOT NULL) as merged_count + FROM + pullRequests pr + JOIN + users u ON pr.authorId = u.id + WHERE + u.isEmployee = FALSE + GROUP BY + authorId + ) AS merged_pr_counts + CROSS JOIN + ( + SELECT COUNT(DISTINCT authorId) as total_authors + FROM pullRequests pr + JOIN + users u ON pr.authorId = u.id + WHERE + u.isEmployee = FALSE + ) AS author_counts + ) WHERE authorId = (SELECT id FROM users WHERE login = :user_id) + `, + ) + .all({ user_id: params.slug }) as { + merged_count: number; + rank_percentage: number; }[]; db.close(); return ( -
-
- {contributor.login} + + + + -

{contributor.login}

-
-
-
- -
-
- {pullRequestList.map((pr) => ( - - ))} -
-
-
+ + + + + + ); } diff --git a/packages/twenty-website/src/app/developers/contributors/page.tsx b/packages/twenty-website/src/app/developers/contributors/page.tsx index ef7ea6cfd..051a2f9fa 100644 --- a/packages/twenty-website/src/app/developers/contributors/page.tsx +++ b/packages/twenty-website/src/app/developers/contributors/page.tsx @@ -1,6 +1,9 @@ import Database from 'better-sqlite3'; import AvatarGrid from '@/app/components/AvatarGrid'; +import { Header } from '@/app/components/developers/contributors/Header'; +import { Background } from '@/app/components/oss-friends/Background'; +import { ContentContainer } from '@/app/components/oss-friends/ContentContainer'; interface Contributor { login: string; @@ -14,16 +17,19 @@ const Contributors = async () => { const contributors = db .prepare( `SELECT - u.login, - u.avatarUrl, - COUNT(pr.id) AS pullRequestCount - FROM + u.login, + u.avatarUrl, + COUNT(pr.id) AS pullRequestCount + FROM users u - JOIN + JOIN pullRequests pr ON u.id = pr.authorId - GROUP BY + WHERE + u.isEmployee = FALSE + AND u.login NOT IN ('dependabot', 'cyborch', 'emilienchvt', 'Samox') + GROUP BY u.id - ORDER BY + ORDER BY pullRequestCount DESC; `, ) @@ -32,9 +38,15 @@ const Contributors = async () => { db.close(); return ( -
- -
+ <> + + +
+
+ +
+ + ); }; diff --git a/packages/twenty-website/src/lib/utils.ts b/packages/twenty-website/src/lib/utils.ts new file mode 100644 index 000000000..91aa77754 --- /dev/null +++ b/packages/twenty-website/src/lib/utils.ts @@ -0,0 +1,32 @@ +import { differenceInDays, formatDistance } from 'date-fns'; + +const formatIntoRelativeDate = (dateString: string) => { + if (!dateString) return ''; + const inputDate = new Date(dateString); + const currentDate = new Date(); + + const daysDifference = differenceInDays(currentDate, inputDate); + let formattedDate = ''; + if (daysDifference === 0) { + formattedDate = 'today'; + } else if (daysDifference === 1) { + formattedDate = 'yesterday'; + } else if (daysDifference < 7) { + formattedDate = formatDistance(inputDate, currentDate, { addSuffix: true }); + } else if (daysDifference < 14) { + formattedDate = 'last week'; + } else if (daysDifference < 30) { + formattedDate = Math.floor(daysDifference / 7) + ' weeks ago'; + } else if (daysDifference < 60) { + formattedDate = 'last month'; + } else if (daysDifference < 365) { + formattedDate = Math.floor(daysDifference / 30) + ' months'; + } else if (daysDifference < 730) { + formattedDate = 'last year'; + } else { + formattedDate = Math.floor(daysDifference / 365) + ' years ago'; + } + return formattedDate; +}; + +export { formatIntoRelativeDate };