Skip to content
This repository has been archived by the owner on Mar 23, 2024. It is now read-only.

Commit

Permalink
Feature/friends (#143)
Browse files Browse the repository at this point in the history
* feat: create friendship system

* test: add e2e tests for friendship functionality

* wip: add basic profile layout

* feat: add profile list icons

* feat: add icons

* feat: add icons

* feat: add social links

* feat: add logo

* fix: layout behind aside menu

* feat: add back button

* feat: add age

* fix: add migration

* fix: remove profile id at the button link

* fix: toggle leader icon based on fetch data

* feat: add empty placeholder for about section

* update: add check friendship status

* feat: add [username] to the path

* Revert "feat: add [username] to the path"

This reverts commit 5027de5.

* fix: change route to the profile page

* feat: add empty state

* update: reworked notification update

* update: updated friendship delete

* fix: incorrect image reference

* fix: case when links are missing

* fix: minor ui changes

* refactor: run prettier

* feat: add skills (except projects & tournaments)

* feat: add friends button logics

* refactor: remove eslint errors

* fix: back icon position

* refactor: run Prettier

* fix: specialty field to match updated type

* fix: skills to match updated structure

* fix: replace useGetMe with useGetUser

* feat: add Friendship seed

* fix: remove nav placeholder shrinking

* fix: profile nav button path

* fix: full navbar size

* wip: add list of friends

* feat: add friends modal

* feat: add responsiveness

* refactor: move logics to page.tsx

* feat: add friend removal

* refactor: remove unused import

* fix: end -> flex-end

* refactor: split ui to folders

* fix: remove icon overlay

* fix: back icon position

* fix: small updates

* fix: prettier

* fix: backend lint

* update: added username check

* update: added username check

* update: updated username check

* feat: friend button states

* fix: display correct skills

* refactor: apply prettier

* feat: add friend button (at user modal)

* feat: add friend notification

* feat: add profile link to user modal

* refactor: add friend notification interface

* feat: add width prop to friend btn

* feat: add friend button to infoModal

* feat:

* fix: fixed friendship status receiving

* fix: cleanup

* fix: generation

---------

Co-authored-by: Romas Bitinas <[email protected]>
Co-authored-by: Nikita Mashchenko <[email protected]>
  • Loading branch information
3 people committed Mar 9, 2024
1 parent e384b9c commit ceb0887
Show file tree
Hide file tree
Showing 74 changed files with 2,463 additions and 115 deletions.
55 changes: 55 additions & 0 deletions client/src/app/(main)/[username]/profile/layout.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@

.container {
display: flex;
width: 100%;
height: 100%;
position: relative;
@media (max-width: 790px) {
position: unset;
}
.body {
width: 100%;
}
}

.back {
position: absolute;

left: 0;
display: flex;
align-items: center;
gap: 6px;
&:hover {
opacity: .7;
transition: .3s;
}

@media (max-width: 790px) {
top: 46px;
right: 15px;
left: unset;
}
}

.profile_row {
@media (max-width: 1045px) {
flex-direction: column;
width: 100%;
& > div {
width: 100%;
}
}
}

.sm_card {
width: 40%;
}

.lg_card {
width: 60%;
}

.list_card {
width: 40%;
gap: 18px;
}
24 changes: 24 additions & 0 deletions client/src/app/(main)/[username]/profile/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use client';
import styles from './layout.module.scss';
import { Flex, Typography } from '@/shared/ui';
import { ArrowLeftIcon, LogoBig } from '@/shared/assets';
import { useRouter } from 'next/navigation';

export default function Layout({ children }: { children: React.ReactNode }) {
const router = useRouter();

return (
<div className={styles.container}>
<Flex direction={'column'} width={'100%'} gap={'30px'}>
<button onClick={router.back} className={styles.back}>
<ArrowLeftIcon />
<Typography>Back</Typography>
</button>
<Flex justify='center'>
<LogoBig />
</Flex>
{children}
</Flex>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { createContext } from 'react';

export const ProfileContext = createContext(false);
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { useGetUsers } from '@/entities/session';
import { IUserResponse } from '@teameights/types';

export const useGetUserByName = (username: string): { data: IUserResponse | undefined } => {
const users = useGetUsers(JSON.stringify({ username: username }));

return { data: users?.data?.pages[0]?.data[0] };
};
46 changes: 46 additions & 0 deletions client/src/app/(main)/[username]/profile/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use client';
import styles from './layout.module.scss';
import { useGetMe } from '@/entities/session';
import { Header } from './ui/header/header';
import { CardSkeleton, Flex } from '@/shared/ui';
import { List } from './ui/list/list';
import { About } from './ui/about/about';
import { useParams } from 'next/navigation';
import { Friends } from './ui/friends/friends';
import { Fields } from './ui/fields/fields';
import { useGetUserByName } from './lib/useGetUserByName';
import { ProfileContext } from './lib/profile-context';

export default function Page() {
const { data: me } = useGetMe();
const { username } = useParams();
const { data: user } = useGetUserByName(username as string);
const isMyProf = me?.username === username;

let body = (
<Flex direction='column' gap='30px'>
<CardSkeleton borderRadius={15} width={'100%'} height={'227px'} />
<CardSkeleton borderRadius={15} width={'100%'} height={'227px'} />
</Flex>
);

if (user) {
body = (
<>
<Header />
<Flex padding='0 0 48px 0' direction='column' gap='30px'>
<Flex className={styles.profile_row} gap='30px'>
<List />
<About />
</Flex>
<Flex className={styles.profile_row} gap='30px'>
<Friends />
<Fields />
</Flex>
</Flex>
</>
);
}

return <ProfileContext.Provider value={isMyProf}> {body} </ProfileContext.Provider>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.container {

}
58 changes: 58 additions & 0 deletions client/src/app/(main)/[username]/profile/ui/about/about.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Card } from '../card/card';
import { Flex, Typography } from '@/shared/ui';
import { GithubIcon } from '@/shared/assets/icons/github-icon';
import { BehanceIcon } from '@/shared/assets/icons/behance';
import { TelegramIcon } from '@/shared/assets/icons/telegram';
import { LinkedinIcon } from '@/shared/assets/icons/linkedin';
import { useParams } from 'next/navigation';
import { useGetUserByName } from '../../lib/useGetUserByName';
import styles from '../../layout.module.scss';

export const About = () => {
const { username } = useParams();
const { data: user } = useGetUserByName(username as string);

const linksPresent = user?.links && Object.keys(user.links).length;
const descPresent = typeof user?.description === 'string';
return (
<Card className={styles.lg_card}>
<Flex direction='column' gap='24px' height='100%'>
<Typography size={'heading_s'} color={'greenBright'}>
About
</Typography>
{!descPresent && (
<Typography size={'body_s'} color={'greyNormal'}>
No description added.
</Typography>
)}
<Flex flex={1}>
{descPresent && <Typography size={'body_s'}>{user?.description}</Typography>}
</Flex>
{linksPresent && (
<Flex align={'center'} gap={24}>
{user?.links?.github && (
<a target='_blank' href={user.links.github}>
<GithubIcon width={28} />
</a>
)}
{user?.links?.behance && (
<a target='_blank' href={user.links.behance}>
<BehanceIcon width={28} />
</a>
)}
{user?.links?.telegram && (
<a target='_blank' href={user.links.telegram}>
<TelegramIcon width={28} />
</a>
)}
{user?.links?.linkedIn && (
<a target='_blank' href={user.links.linkedIn}>
<LinkedinIcon width={28} />
</a>
)}
</Flex>
)}
</Flex>
</Card>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.card {
background: rgba(26, 28, 34, 1);
border-radius: 15px;
overflow: hidden;
padding: 32px;
}
14 changes: 14 additions & 0 deletions client/src/app/(main)/[username]/profile/ui/card/card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ReactNode } from 'react';
import styles from './card.module.scss';
import clsx from 'clsx';

interface CardProps {
children: ReactNode;
className?: string;
borderRadius?: string;
}

export const Card = ({ children, className }: CardProps) => {
const cls = clsx(styles.card, className);
return <div className={cls}>{children}</div>;
};
35 changes: 35 additions & 0 deletions client/src/app/(main)/[username]/profile/ui/fields/education.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Flex, Typography } from '@/shared/ui';
import { useParams } from 'next/navigation';
import { useGetUserByName } from '../../lib/useGetUserByName';

export const Education = () => {
const { username } = useParams();
const { data: user } = useGetUserByName(username as string);
const universities = user?.universities;
if (!universities) return <Typography>No information</Typography>;
return (
<Flex gap='24px' direction='column'>
{universities.map((education, i) => {
const start = new Date(education.admissionDate).getFullYear();
const end = education.graduationDate
? new Date(education.graduationDate).getFullYear()
: 'Present';
return (
<Flex key={i} width={'100%'} justify={'space-between'}>
<Flex gap={'8px'} direction={'column'}>
<Typography size={'body_m'}>
{education.name ?? (education as unknown as { university: string }).university}
</Typography>
<Typography color={'greyNormal'} size={'body_s'}>
{education.degree} in {education.major}
</Typography>
</Flex>
<Typography>
{start} - {end}
</Typography>
</Flex>
);
})}
</Flex>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.selected {
border-bottom: 1px solid #5bd424;
}

.field_text {
transition: 0.3s;
transition-property: color;
}

.fields_container {
min-height: 150px;
}

.nav_bar {
overflow-x: auto;
}
46 changes: 46 additions & 0 deletions client/src/app/(main)/[username]/profile/ui/fields/fields.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Card } from '../card/card';
import { Flex, Typography } from '@/shared/ui';
import { useState } from 'react';
import styles from './fields.module.scss';
import { Skills } from './skills';
import { WorkExperience } from './work-experience';
import { Education } from './education';
import layoutStyles from '../../layout.module.scss';
export const Fields = () => {
const [field, setField] = useState<keyof typeof fields>('Skills');

const fields = {
Skills: <Skills />,
Projects: null,
'Work experience': <WorkExperience />,
Education: <Education />,
Tournaments: null,
};

return (
<Card className={layoutStyles.lg_card}>
<Flex direction='column' gap='24px' className={styles.fields_container}>
<Flex className={styles.nav_bar} gap='16px'>
{Object.keys(fields).map(key => {
const classProps = field === key ? { className: styles.selected } : {};
return (
<button
onClick={() => setField(key as keyof typeof fields)}
{...classProps}
key={key}
>
<Typography
className={styles.field_text}
color={field === key ? 'greenBright' : 'greyNormal'}
>
{key}
</Typography>
</button>
);
})}
</Flex>
{fields[field]}
</Flex>
</Card>
);
};
36 changes: 36 additions & 0 deletions client/src/app/(main)/[username]/profile/ui/fields/skills.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { BadgeText, Flex, Typography } from '@/shared/ui';
import { useGetUserByName } from '../../lib/useGetUserByName';
import { useParams } from 'next/navigation';
import { BadgeIcon } from '@/shared/ui';

export const Skills = () => {
const { username } = useParams();
const { data: user } = useGetUserByName(username as string);
const skills = {
coreTools: {
badge: ({ data }: { data: string }) => <BadgeIcon data={data} isActive={true} />,
title: 'Core Tools',
},
additionalTools: {
badge: BadgeText,
title: 'Additional Tools',
},
};
return (
<Flex gap='24px' direction='column'>
{user!.skills &&
Object.entries(skills).map(skill => {
const skillName = skill[0] as keyof typeof skills;
const Badge = skills[skillName].badge;
return (
<Flex key={skillName} direction='column' gap='8px'>
<Typography>{skills[skillName].title}</Typography>
<Flex wrap='wrap' gap='8px'>
{user?.skills![skillName]?.map((lang: string) => <Badge key={lang} data={lang} />)}
</Flex>
</Flex>
);
})}
</Flex>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Flex, Typography } from '@/shared/ui';
import { useParams } from 'next/navigation';
import { useGetUserByName } from '../../lib/useGetUserByName';
export const WorkExperience = () => {
const { username } = useParams();
const { data: user } = useGetUserByName(username as string);
const jobs = user?.jobs;
return (
<Flex gap='24px' direction='column'>
{jobs?.map((job, i: number) => {
const start = new Date(job.startDate).getFullYear();
const end = job.endDate ? new Date(job.endDate).getFullYear() : 'Present';
return (
<Flex key={i} width={'100%'} justify={'space-between'}>
<Flex gap={'8px'} direction={'column'}>
<Typography size={'body_m'}>{job.company}</Typography>
<Typography color={'greyNormal'} size={'body_s'}>
{job.title}
</Typography>
</Flex>
<Typography>
{start} - {end}
</Typography>
</Flex>
);
})}
</Flex>
);
};
Loading

2 comments on commit ceb0887

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy preview for teameights ready!

✅ Preview
https://teameights-h6juc21cc-exortme1ster.vercel.app

Built with commit ceb0887.
This pull request is being automatically deployed with vercel-action

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy preview for teameights-storybook ready!

✅ Preview
https://teameights-storybook-q9lwvi80m-exortme1ster.vercel.app

Built with commit ceb0887.
This pull request is being automatically deployed with vercel-action

Please sign in to comment.