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

Commit

Permalink
feat: added main page and connected everything (#141)
Browse files Browse the repository at this point in the history
* feat: added initial implementation

* Revert "feat: added initial implementation"

This reverts commit 83f6152.

* feat: added initial screen with issues

* feat: added server loading & filters, etc

* feat: final touches before merging
  • Loading branch information
nmashchenko committed Dec 24, 2023
1 parent 7a573aa commit da6b243
Show file tree
Hide file tree
Showing 52 changed files with 920 additions and 787 deletions.
1 change: 1 addition & 0 deletions client/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module.exports = {
'localhost',
'picsum.photos',
'source.unsplash.com',
'upload.wikimedia.org',
],
remotePatterns: [
{
Expand Down
1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"react-content-loader": "^6.2.1",
"react-dom": "18.2.0",
"react-hook-form": "^7.45.4",
"react-loading-skeleton": "^3.3.1",
"react-modern-drawer": "^1.2.2",
"react-particles": "^2.12.2",
"react-responsive-modal": "^6.4.2",
Expand Down
15 changes: 11 additions & 4 deletions client/src/app/(main)/layout.module.scss
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
.container {
height: 100dvh;
width: 100%;
padding: 48px 55px;
padding: 48px 0;

@media (width <= 1120px) {
padding: 48px 24px;
padding: 48px 0;
}

@media (width <= 580px) {
padding: 24px;
padding: 28px;
}
}

.children {
width: 100%;
min-height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}

.content_zone {
padding-left: 88px;

@media screen and (max-width: 768px) {
padding-left: 0;
}
}
80 changes: 67 additions & 13 deletions client/src/app/(main)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,76 @@
'use client';

import { Typography } from '@/shared/ui';
import { useGetScreenWidth } from '@/shared/lib';
import { Flex, SearchBar } from '@/shared/ui';
import { countries, specialities } from '@/shared/constant';
import { LogoBig } from '@/shared/assets';
import { useGetUsers } from '@/entities/session';
import { useState } from 'react';
import { Cards } from '@/app/(main)/ui/cards/cards';
import styles from './layout.module.scss';
import { UserInfoModal } from '@/widgets';
import { IUserResponse } from '@teameights/types';

export default function Home() {
const width = useGetScreenWidth();
const [filters, setFilters] = useState<string | null>();
const { fetchNextPage, hasNextPage, isFetchingNextPage, data, ...result } = useGetUsers(filters);
const [open, setOpen] = useState(false);
const [selectedUser, setSelectedUser] = useState<IUserResponse>();

const handleModalOpen = (user: IUserResponse) => {
setSelectedUser(user);
setOpen(true);
};

return (
<>
<Typography size='heading_l' variant='h6'>
We are working hard to deliver teameights on NextJS/TS soon!
</Typography>

<div> The screen width is: {width} </div>

<a href='/login' style={{ color: 'green' }}>
Get to login
</a>
<UserInfoModal isOpenModal={open} handleClose={() => setOpen(false)} user={selectedUser} />
<Flex
gap={48}
direction='column'
width='100%'
justify='center'
align='center'
className={styles.content_zone}
>
<LogoBig />
<SearchBar
initialFiltersState={[
{
type: 'text',
label: 'Name',
value: 'fullName',
placeholder: 'Search by name',
filterValue: '',
},
{
label: 'Countries',
value: 'countries',
type: 'checkbox',
placeholder: 'Search by countries',
optionsArr: countries,
filterValue: [],
},
{
label: 'Specialties',
value: 'specialities',
type: 'checkbox',
placeholder: 'Search by specialty',
optionsArr: specialities,
filterValue: [],
},
]}
onChange={filterValues => {
setFilters(filterValues);
}}
/>
</Flex>
<Cards
onCardClick={handleModalOpen}
data={data}
isLoading={result.isLoading}
isFetchingNextPage={isFetchingNextPage}
hasNextPage={hasNextPage}
fetchNextPage={fetchNextPage}
/>
</>
);
}
35 changes: 35 additions & 0 deletions client/src/app/(main)/ui/cards/cards.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
.cards_zone {
padding-left: 88px;

@media screen and (max-width: 768px) {
padding-left: 0;
}
}

.cards {
display: grid;
row-gap: 50px;
margin-top: 15px;
width: 100%;
max-width: 1196px;
grid-template-columns: repeat(4, 1fr);
justify-items: center;

@media screen and (max-width: 1440px) {
max-width: 826px;
grid-template-columns: repeat(3, 1fr);
}
@media screen and (max-width: 1024px) {
max-width: 770px;
grid-template-columns: repeat(3, 1fr);
}

@media screen and (max-width: 900px) {
max-width: 526px;
grid-template-columns: repeat(2, 1fr);
}

@media screen and (max-width: 600px) {
grid-template-columns: repeat(1, 1fr);
}
}
89 changes: 89 additions & 0 deletions client/src/app/(main)/ui/cards/cards.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { InfinityPaginationResultType, IUserResponse } from '@teameights/types';
import { FC, useCallback, useRef } from 'react';
import { Flex, CardSkeleton } from '@/shared/ui';
import { UserCard } from '@/entities/user';
import styles from './cards.module.scss';
import { UsersNotFound } from '../users-not-found/users-not-found';
import {
FetchNextPageOptions,
InfiniteData,
InfiniteQueryObserverResult,
} from '@tanstack/query-core';

interface CardsProps {
onCardClick: (user: IUserResponse) => void;
data?: InfiniteData<InfinityPaginationResultType<IUserResponse>>;
isLoading: boolean;
isFetchingNextPage: boolean;
hasNextPage: boolean;
fetchNextPage: (
options?: FetchNextPageOptions | undefined
) => Promise<
InfiniteQueryObserverResult<
InfiniteData<Readonly<{ data: IUserResponse[]; hasNextPage: boolean }>, unknown>,
Error
>
>;
}
export const Cards: FC<CardsProps> = ({
onCardClick,
data,
isLoading,
isFetchingNextPage,
hasNextPage,
fetchNextPage,
}) => {
const intObserver = useRef<IntersectionObserver>();

const lastUserRef = useCallback(
(user: HTMLDivElement) => {
if (isFetchingNextPage) {
return;
}

if (intObserver.current) {
intObserver.current.disconnect();
}

intObserver.current = new IntersectionObserver(
usersPerPage => {
if (usersPerPage[0].isIntersecting && hasNextPage) {
fetchNextPage();
}
},
{ threshold: 0.9 }
);

if (user) {
intObserver.current.observe(user);
}
},
[isFetchingNextPage, fetchNextPage, hasNextPage]
);

const content = data?.pages.map(pg => {
const usersPerPage = pg.data;

return usersPerPage.map((user, index) => {
if (usersPerPage.length === index + 1) {
return (
<UserCard user={user} key={index} ref={lastUserRef} onClick={() => onCardClick(user)} />
);
}

return <UserCard user={user} key={index} onClick={() => onCardClick(user)} />;
});
});

return (
<Flex width='100%' justify='space-evenly' align='center' className={styles.cards_zone}>
<Flex width='100%' justify='center' align='center' direction='column' margin='0 0 30px 0'>
{!isLoading && !data?.pages.length && <UsersNotFound />}
<div className={styles.cards}>
{content}
{(isLoading || isFetchingNextPage) && <CardSkeleton cards={9} />}
</div>
</Flex>
</Flex>
);
};
23 changes: 23 additions & 0 deletions client/src/app/(main)/ui/users-not-found/users-not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Flex, Typography } from '@/shared/ui';
import { AstronautIllustration } from '@/shared/assets';

export const UsersNotFound = () => {
return (
<Flex width='100%' justify='center' align='center'>
<Flex
gap={36}
width='100%'
maxWidth='470px'
direction='column'
justify='center'
align='center'
>
<AstronautIllustration />
<Flex gap={8} direction='column' justify='center' align='center' width='100%'>
<Typography size='heading_m'>No results found :(</Typography>
<Typography size='body_m'>We can’t find any item matching your search</Typography>
</Flex>
</Flex>
</Flex>
);
};
2 changes: 2 additions & 0 deletions client/src/entities/session/api/useGetMe.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ export const useGetMe = () => {
},
refetchOnMount: false,
refetchOnWindowFocus: false,
retry: 1,
retryDelay: 5000,
});
};
2 changes: 2 additions & 0 deletions client/src/entities/session/api/useGetNotifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@ export const useGetNotifications = () => {
},
refetchOnMount: false,
refetchOnWindowFocus: false,
retry: 1,
retryDelay: 5000,
});
};
36 changes: 15 additions & 21 deletions client/src/entities/session/api/useGetUsers.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,24 @@
'use client';
import { useQuery } from '@tanstack/react-query';
import { IUserProtectedResponse } from '@teameights/types';
import { useInfiniteQuery } from '@tanstack/react-query';
import { InfinityPaginationResultType, IUserResponse } from '@teameights/types';
import { API } from '@/shared/api';
import { API_USERS } from '@/shared/constant';

interface IQueryParams {
page?: number;
limit?: number;
filters?: string | null;
sort?: string;
}

export const useGetUsers = ({ page, limit, filters, sort }: IQueryParams) => {
const queryString = [
page && `page=${page}`,
limit && `limit=${limit}`,
filters && `filters=${filters}`,
sort && `sort=${sort}`,
].join('');

return useQuery({
queryKey: ['useGetUsers', queryString],
queryFn: async () => {
const { data } = await API.get<IUserProtectedResponse>(`${API_USERS}?${queryString}`);
export const useGetUsers = (filters?: string | null) => {
return useInfiniteQuery({
queryKey: ['useGetUsers', filters],
queryFn: async ({ queryKey, pageParam }) => {
let url = `${API_USERS}?page=${pageParam}&limit=9`;
if (queryKey[1]) {
url = `${url}&filters=${queryKey[1]}`;
}
const { data } = await API.get<InfinityPaginationResultType<IUserResponse>>(url);
return data;
},
initialPageParam: 1,
getNextPageParam: (lastPage, allPages) => {
return lastPage.hasNextPage ? allPages.length + 1 : undefined;
},
refetchOnMount: false,
refetchOnWindowFocus: false,
});
Expand Down

This file was deleted.

This file was deleted.

Loading

2 comments on commit da6b243

@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-1w158mfxr-exortme1ster.vercel.app

Built with commit da6b243.
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-847s7geho-exortme1ster.vercel.app

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

Please sign in to comment.