Website UI design (#4829)

**Fixed different issues** :
 
- Multiple CSS fixes: font-size, colors, margins, z-index ...
- Fixed hover on contributor avatars
- Added link to contributors in footer
- Made the year in the footer dynamic (2023 --> 2024)
- Added name of contributor in "Thank you" section of Contributor page
- Added footer in small screens
- Made Activity Log Responsive 
- Fixed bug in "saving issues to DB", title was null everywhere. I
needed to implement an "upsert" behaviour to update the existing
database on init

**To be noted :** 

There is the following bug on production happening on mobile when you
refresh a second time :

<img width="1440" alt="Screenshot 2024-04-05 at 01 30 58"
src="https://github.com/twentyhq/twenty/assets/102751374/b935b07a-63dc-463d-8dcb-070ad4ef6db0">


It seems to be related to the following issue on mdx :
[https://github.com/hashicorp/next-mdx-remote/issues/350](https://github.com/hashicorp/next-mdx-remote/issues/350)

I added the following code that fixed this bug for me in development
(this needs to be tested in production) :

```
const serialized = await serialize(content, {
    mdxOptions: {
      development: process.env.NODE_ENV === 'development',
    }
  })
```

---------

Co-authored-by: Ady Beraud <a.beraud96@gmail.com>
This commit is contained in:
Ady Beraud
2024-04-05 08:41:08 +02:00
committed by GitHub
parent e8c58ae541
commit b82519301c
25 changed files with 140 additions and 53 deletions

View File

@ -1,10 +1,20 @@
'use client';
import { ResponsiveTimeRange } from '@nivo/calendar';
import styled from '@emotion/styled';
import { TimeRange } from '@nivo/calendar';
import { CardContainer } from '@/app/_components/contributors/CardContainer';
import { Title } from '@/app/_components/contributors/Title';
const CalendarContentContainer = styled.div`
@media (max-width: 890px) {
overflow-x: auto;
&::-webkit-scrollbar {
display: none;
}
}
`;
export const ActivityLog = ({
data,
}: {
@ -16,17 +26,20 @@ export const ActivityLog = ({
return (
<CardContainer>
<Title>Activity</Title>
<div style={{ width: '100%', height: '214px' }}>
<ResponsiveTimeRange
<CalendarContentContainer>
<TimeRange
height={150}
width={725}
data={data}
emptyColor="#F4EFFF"
colors={['#E9DFFF', '#B28FFE', '#915FFD']}
dayBorderWidth={2}
dayBorderColor="#ffffff"
weekdayTicks={[]}
weekdayLegendOffset={0}
dayBorderWidth={0}
dayRadius={4}
daySpacing={2}
daySpacing={4}
/>
</div>
</CalendarContentContainer>
</CardContainer>
);
};

View File

@ -52,6 +52,8 @@ const AvatarItem = styled.div`
transition:
opacity 0.3s ease,
visibility 0.3s;
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
}
&:hover .username {

View File

@ -11,7 +11,6 @@ export const CardContainer = styled.div`
background-color: #fafafa;
@media (max-width: 810px) {
gap: 16px;
padding: 24px;
}
`;

View File

@ -9,11 +9,11 @@ const Container = styled.div`
width: 100%;
max-width: 898px;
padding: 40px;
gap: 40px;
@media (max-width: 809px) {
width: 100%;
padding: 40px 24px 40px 24px;
gap: 24px;
}
`;

View File

@ -4,13 +4,19 @@ import styled from '@emotion/styled';
const Title = styled.h2`
font-size: 56px;
font-weight: 600;
font-weight: bold;
color: #b3b3b3;
margin-bottom: 0px;
margin-bottom: 32px;
margin-top: 64px;
text-align: center;
display: flex;
justify-items: center;
align-items: center;
flex-direction: column;
@media (max-width: 810px) {
font-size: 28px;
font-size: 32px;
margin-bottom: 22px;
}
`;
@ -18,7 +24,8 @@ export const Header = () => {
return (
<>
<Title>
Our amazing <br /> <span style={{ color: 'black' }}>Contributors</span>
Our amazing <br />
<span style={{ color: '#141414' }}>Contributors</span>
</Title>
</>
);

View File

@ -10,6 +10,10 @@ const ProfileContainer = styled.div`
gap: 32px;
width: 100%;
align-items: center;
@media (max-width: 600px) {
flex-direction: column;
align-items: start;
}
`;
const Avatar = styled.div`
@ -33,6 +37,10 @@ const Details = styled.div`
flex-direction: column;
gap: 4px;
@media (max-width: 810px) {
gap: 8px;
}
.username {
font-size: 40px;
font-weight: 700;
@ -42,7 +50,7 @@ const Details = styled.div`
gap: 12px;
@media (max-width: 810px) {
font-size: 24px;
font-size: 32px;
line-height: 28.8px;
}
}

View File

@ -3,6 +3,7 @@
import styled from '@emotion/styled';
import { CardContainer } from '@/app/_components/contributors/CardContainer';
import { Theme } from '@/app/_components/ui/theme/theme';
const Container = styled(CardContainer)`
flex-direction: row;
@ -21,8 +22,8 @@ const Container = styled(CardContainer)`
.title {
font-size: 24px;
color: #b3b3b3;
margin: 0;
color: ${Theme.text.color.quarternary};
margin: 8px;
@media (max-width: 810px) {
font-size: 20px;
@ -32,7 +33,7 @@ const Container = styled(CardContainer)`
.value {
font-size: 56px;
font-weight: 700;
color: #474747;
color: ${Theme.text.color.secondary};
@media (max-width: 810px) {
font-size: 32px;
@ -72,7 +73,7 @@ export const ProfileInfo = ({
</div>
<div className="separator"></div>
<div className="item">
<p className="title">Rank</p>
<p className="title">Ranking</p>
<span className="value">{rank}%</span>
</div>
<div className="separator"></div>

View File

@ -3,6 +3,7 @@ import styled from '@emotion/styled';
import { format } from 'date-fns';
import { PullRequestIcon } from '@/app/_components/ui/icons/SvgIcons';
import { Theme } from '@/app/_components/ui/theme/theme';
import { formatIntoRelativeDate } from '@/shared-utils/formatIntoRelativeDate';
const StyledTooltip = styled(Tooltip)``;
@ -15,7 +16,7 @@ const Item = styled.div`
const StyledTitle = styled.a`
font-size: 24px;
font-weight: 400;
color: #474747;
color: ${Theme.text.color.secondary};
margin: 0;
text-decoration: none;
@ -27,6 +28,7 @@ const StyledTitle = styled.a`
const StyledPrLink = styled.a`
cursor: pointer;
text-decoration: none;
color: ${Theme.text.color.quarternary} !important;
&:hover {
text-decoration: underline;
@ -38,7 +40,8 @@ const StyledDescription = styled.div`
font-size: 20px;
line-height: 28px;
font-weight: 500;
color: #b3b3b3;
color: ${Theme.text.color.quarternary};
margin-top: 4px;
@media (max-width: 810px) {
font-size: 18px;

View File

@ -10,6 +10,7 @@ const List = styled.div`
display: flex;
gap: 24px;
flex-direction: column;
overflow: hidden;
`;
interface PullRequestsProps {
list: {

View File

@ -6,7 +6,6 @@ import { CardContainer } from '@/app/_components/contributors/CardContainer';
import { HeartIcon } from '@/app/_components/ui/icons/SvgIcons';
const StyledTitle = styled.div`
display: flex;
font-size: 24px;
font-weight: 500;
gap: 8px;
@ -18,20 +17,21 @@ const StyledTitle = styled.div`
const StyledHeartIcon = styled(HeartIcon)`
@media (max-width: 810px) {
display: inline-block !important;
width: 16px !important;
height: 16px !important;
}
`;
interface ThankYouProps {
authorId: string;
username: string;
}
export const ThankYou = ({ authorId }: ThankYouProps) => {
export const ThankYou = ({ username }: ThankYouProps) => {
return (
<CardContainer>
<StyledTitle>
Thank you @{authorId} <StyledHeartIcon color="#333333" size="18px" />
Thank you @{username} <StyledHeartIcon color="#333333" size="18px" />
</StyledTitle>
</CardContainer>
);

View File

@ -11,7 +11,7 @@ const Container = styled.div`
gap: 26px;
@media (max-width: 809px) {
width: 100%;
padding: 0px 12px 0px 12px;
padding: 0px 12px 40px 12px;
}
`;

View File

@ -9,17 +9,17 @@ const StyledLineContainer = styled.div`
@media (max-width: 810px) {
width: auto;
margin: 24px 0;
display: block;
margin: 24px 0px 64px;
}
`;
const StyledLine = styled.div`
height: 1px;
background-color: #d9d9d9;
margin-bottom: 48px;
margin-bottom: 80px;
margin-left: 148px;
margin-top: 48px;
margin-top: 40px;
width: 100%;
@media (max-width: 810px) {

View File

@ -6,6 +6,7 @@ import { compileMDX } from 'next-mdx-remote/rsc';
import remarkBehead from 'remark-behead';
import gfm from 'remark-gfm';
import { Theme } from '@/app/_components/ui/theme/theme';
import { ReleaseNote } from '@/app/releases/api/route';
const StyledContainer = styled.div`
@ -16,7 +17,6 @@ const StyledContainer = styled.div`
@media (max-width: 810px) {
width: auto;
margin: 24px 0;
display: block;
}
`;
@ -35,17 +35,19 @@ const StyledVersion = styled.div`
font-size: 20px;
flex-flow: row;
justify-content: space-between;
margin-bottom: 24px;
}
`;
const StyledRelease = styled.span`
color: #141414;
color: ${Theme.text.color.quarternary};
line-height: 140%;
`;
const StyledDate = styled.span`
color: #474747;
font-size: 16px;
color: ${Theme.text.color.secondary};
font-weight: 400;
font-size: ${Theme.font.size.sm};
`;
const StlyedContent = styled.div`
@ -54,16 +56,20 @@ const StlyedContent = styled.div`
gap: 64px;
h3 {
color: #141414;
color: ${Theme.text.color.primary};
font-weight: 700;
font-size: 40px;
margin: 0;
}
p {
color: #474747;
color: ${Theme.text.color.secondary};
font-family: ${Theme.font.family};
font-size: 16px;
line-height: 28.8px;
font-weight: 400;
margin: 40px 0px;
text-align: justify;
}
img {
@ -73,16 +79,16 @@ const StlyedContent = styled.div`
@media (max-width: 810px) {
h3 {
font-size: 24px;
margin: 24px 0 40px;
}
}
`;
const gabarito = Gabarito({
weight: ['400', '500'],
weight: ['400', '500', '600', '700'],
subsets: ['latin'],
display: 'swap',
adjustFontFallback: false,
variable: '--font-gabarito',
});
export const Release = async ({ release }: { release: ReleaseNote }) => {
@ -92,6 +98,7 @@ export const Release = async ({ release }: { release: ReleaseNote }) => {
source: release.content,
options: {
mdxOptions: {
development: process.env.NODE_ENV === 'development',
remarkPlugins: [gfm, [remarkBehead, { depth: 2 }]],
},
},

View File

@ -139,7 +139,9 @@ export const PullRequestIcon = ({ size = 'S', color = 'rgb(179,179,179)' }) => {
export const HeartIcon = ({ size = 'S', color = 'rgb(179,179,179)' }) => {
const dimension = getSize(size);
return (
<div style={{ width: dimension, height: dimension }}>
<span
style={{ width: dimension, height: dimension, display: 'inline-block' }}
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 20" fill="none">
<path
d="M18.513 10.572L11.013 18L3.513 10.572C3.0183 10.0906 2.62864 9.51201 2.36854 8.87263C2.10845 8.23325 1.98356 7.54694 2.00173 6.85693C2.01991 6.16691 2.18076 5.48813 2.47415 4.86333C2.76755 4.23853 3.18713 3.68125 3.70648 3.22657C4.22583 2.7719 4.8337 2.42968 5.49181 2.22147C6.14991 2.01327 6.844 1.94358 7.53036 2.0168C8.21673 2.09001 8.8805 2.30455 9.47987 2.6469C10.0792 2.98925 10.6012 3.45199 11.013 4.00599C11.4265 3.45602 11.9491 2.99731 12.5481 2.6586C13.1471 2.31988 13.8095 2.10844 14.4939 2.03751C15.1784 1.96658 15.8701 2.03769 16.5258 2.24639C17.1815 2.45508 17.787 2.79687 18.3045 3.25036C18.8221 3.70385 19.2404 4.25928 19.5334 4.88189C19.8264 5.50449 19.9877 6.18088 20.0073 6.8687C20.0269 7.55653 19.9043 8.24099 19.6471 8.87924C19.39 9.5175 19.0039 10.0958 18.513 10.578"
@ -149,7 +151,7 @@ export const HeartIcon = ({ size = 'S', color = 'rgb(179,179,179)' }) => {
strokeLinejoin="round"
/>
</svg>
</div>
</span>
);
};

View File

@ -30,7 +30,7 @@ const ExternalLinkItem = styled.a`
`;
const ActivePage = styled.span`
color: ${Theme.text.color.secondary};
color: ${Theme.text.color.primary};
font-weight: ${Theme.font.weight.medium};
`;

View File

@ -9,7 +9,7 @@ const Container = styled.div`
padding: 0px 96px 0px 96px;
@media (max-width: 809px) {
width: 100%;
padding: 0px 12px 0px 12px;
padding: 0px 24px 0px 24px;
}
`;

View File

@ -19,7 +19,7 @@ const FooterContainer = styled.div`
color: rgb(129, 129, 129);
gap: 32px;
@media (max-width: 809px) {
display: none;
padding: 36px 24px;
}
`;
@ -28,6 +28,9 @@ const LeftSideFooter = styled.div`
display: flex;
flex-direction: column;
gap: 16px;
@media (max-width: 809px) {
display: none;
}
`;
const RightSideFooter = styled.div`
@ -35,6 +38,10 @@ const RightSideFooter = styled.div`
justify-content: space-between;
gap: 48px;
height: 146px;
@media (max-width: 809px) {
flex-direction: column;
height: fit-content;
}
`;
const RightSideFooterColumn = styled.div`
@ -96,6 +103,9 @@ export const FooterDesktop = () => {
</RightSideFooterColumn>
<RightSideFooterColumn>
<RightSideFooterColumnTitle>Other</RightSideFooterColumnTitle>
<RightSideFooterLink href="/contributors">
Contributors
</RightSideFooterLink>
<RightSideFooterLink href="/oss-friends">
OSS Friends
</RightSideFooterLink>
@ -120,7 +130,7 @@ export const FooterDesktop = () => {
>
<div>
<span style={{ fontFamily: 'Inter, sans-serif' }}>©</span>
2023 Twenty PBC
{new Date().getFullYear()} Twenty PBC
</div>
<div
style={{

View File

@ -119,22 +119,26 @@ export const LinkNextToCTA = styled.a`
export const HamburgerContainer = styled.div`
height: 44px;
width: 44px;
cursor: pointer;
position: relative;
input {
cursor: pointer;
height: 44px;
width: 44px;
opacity: 0;
z-index: 1;
}
#line1 {
transition: transform 0.5s;
transition-timing-function: ease-in-out;
z-index: 0;
}
#line2 {
transition: transform 0.5s;
transition-timing-function: ease-in-out;
z-index: 0;
}
#menu-input:checked ~ #line1 {
@ -154,6 +158,7 @@ export const HamburgerLine1 = styled.div`
width: 20px;
border-radius: 10px;
background-color: rgb(179, 179, 179);
z-index: 0;
`;
export const HamburgerLine2 = styled.div`
@ -164,6 +169,7 @@ export const HamburgerLine2 = styled.div`
width: 20px;
border-radius: 10px;
background-color: rgb(179, 179, 179);
z-index: 0;
`;
export const NavOpen = styled.div`

View File

@ -29,7 +29,7 @@ export interface Directory {
async function getFiles(
filePath: string,
basePath: string,
position: number = 0,
position = 0,
): Promise<Directory> {
const entries = fs.readdirSync(filePath, { withFileTypes: true });
@ -99,13 +99,14 @@ async function parseFrontMatterAndCategory(
return parsedDirectory;
}
export async function compileMDXFile(filePath: string, addToc: boolean = true) {
export async function compileMDXFile(filePath: string, addToc = true) {
const fileContent = fs.readFileSync(filePath, 'utf8');
const compiled = await compileMDX<{ title: string; position?: number }>({
source: fileContent,
options: {
parseFrontmatter: true,
mdxOptions: {
development: process.env.NODE_ENV === 'development',
remarkPlugins: [gfm],
rehypePlugins: [rehypeSlug, ...(addToc ? [toc] : [])],
},

View File

@ -127,7 +127,7 @@ export default async function ({ params }: { params: { slug: string } }) {
}[]
}
/>
<ThankYou authorId={contributor.login} />
<ThankYou username={contributor.id} />
</ContentContainer>
</>
);

View File

@ -15,6 +15,7 @@ export async function savePRsToDB(
if (pr.author == null) {
continue;
}
await insertMany(
userModel,
[
@ -43,7 +44,7 @@ export async function savePRsToDB(
authorId: pr.author.login,
},
],
{ onConflictKey: 'id' },
{ onConflictKey: 'id', onConflictUpdateObject: { title: pr.title } },
);
for (const label of pr.labels.nodes) {

View File

@ -47,9 +47,7 @@ const Contributors = async () => {
<Background />
<ContentContainer>
<Header />
<div>
<AvatarGrid users={fitlerContributors as Contributor[]} />
</div>
<AvatarGrid users={fitlerContributors as Contributor[]} />
</ContentContainer>
</>
);

View File

@ -76,10 +76,25 @@ const findAll = (model: SQLiteTableWithColumns<any>) => {
const insertMany = async (
model: SQLiteTableWithColumns<any>,
data: any,
options?: { onConflictKey?: string },
options?: {
onConflictKey?: string;
onConflictUpdateObject?: any;
},
) => {
if (isSqliteDriver) {
const query = sqliteDb.insert(model).values(data);
if (options?.onConflictUpdateObject) {
if (options?.onConflictKey) {
return query
.onConflictDoUpdate({
target: [model[options.onConflictKey]],
set: options.onConflictUpdateObject,
})
.execute();
}
}
if (options?.onConflictKey) {
return query
.onConflictDoNothing({
@ -87,10 +102,23 @@ const insertMany = async (
})
.execute();
}
return query.execute();
}
if (isPgDriver) {
const query = pgDb.insert(model).values(data);
if (options?.onConflictUpdateObject) {
if (options?.onConflictKey) {
return query
.onConflictDoUpdate({
target: [model[options.onConflictKey]],
set: options.onConflictUpdateObject,
})
.execute();
}
}
if (options?.onConflictKey) {
return query
.onConflictDoNothing({

View File

@ -9,7 +9,7 @@ export const pgUsers = pgTable('users', {
export const pgPullRequests = pgTable('pullRequests', {
id: text('id').primaryKey(),
name: text('title'),
title: text('title'),
body: text('body'),
url: text('url'),
createdAt: text('createdAt'),

View File

@ -9,7 +9,7 @@ export const sqlLiteUsers = sqliteTable('users', {
export const sqlLitePullRequests = sqliteTable('pullRequests', {
id: text('id').primaryKey(),
name: text('title'),
title: text('title'),
body: text('body'),
url: text('url'),
createdAt: text('createdAt'),