Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Task solution Jakub Sepetowski #4

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
8d819be
Add types definitions and add functions for reading data from JSON an…
sepetowski Dec 27, 2023
d94a466
Implemented a function that merges two lists into one, filters based …
sepetowski Dec 27, 2023
8ee4743
add shadcn ui components and made some basic layout
sepetowski Dec 27, 2023
f4c2056
Added pagination to the crew API, utilizing React Query
sepetowski Dec 27, 2023
195cf7c
Fixed a typo in the function name
sepetowski Dec 27, 2023
249db77
Enhanced application functionality by adding validation for the query…
sepetowski Dec 28, 2023
b438361
Improved layout and added display of crew members
sepetowski Dec 28, 2023
0d28863
Add pagination functionality.
sepetowski Dec 28, 2023
c4a416c
improve design of crew mebmer card and pagination buttons
sepetowski Dec 28, 2023
1d63d68
Handle errors during API crew informations fetching
sepetowski Dec 28, 2023
9eac3fe
made custom 404 page
sepetowski Dec 28, 2023
ad50e11
add link to repo in nav, add hover effect on crew member card and and…
sepetowski Dec 28, 2023
469e8cf
Refine main page appearance
sepetowski Dec 28, 2023
2e2c32a
Remove unnecessary imports
sepetowski Dec 28, 2023
903a4be
Set up testing environment
sepetowski Dec 28, 2023
3af2711
add test for home and 404 page and for component: nav, pagination, er…
sepetowski Dec 29, 2023
5ae62d1
Remove unnecessary enum, fix data attributes for tests
sepetowski Dec 29, 2023
160f22d
Move data fetching logic to custom hook
sepetowski Dec 29, 2023
cab0450
Add Prettier and format the code
sepetowski Dec 29, 2023
1a01ee0
Integrate Zod for data type validation in file reads and improve endp…
sepetowski Dec 29, 2023
e69191c
Added pagination rendering check and tests
sepetowski Dec 30, 2023
e658539
code foramt
sepetowski Dec 30, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
{
"extends": "next/core-web-vitals"
"extends": [
"next/core-web-vitals",
"plugin:testing-library/react",
"plugin:jest-dom/recommended"
]
}
5 changes: 5 additions & 0 deletions .prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
tabWidth: 4,
semi: true,
singleQuote: true,
};
30 changes: 15 additions & 15 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,24 @@ In the interest of fostering an open and welcoming environment, we pledge to mak

Examples of behaviour that contributes to creating a positive environment include:

- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Referring to people by their preferred pronouns and using gender-neutral pronouns when uncertain
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Referring to people by their preferred pronouns and using gender-neutral pronouns when uncertain

Examples of unacceptable behaviour by participants include:

- Trolling, insulting/derogatory comments, public or private harassment
- Publishing others' private information, such as a physical or electronic address, without explicit permission
- Not being respectful to reasonable communication boundaries, such as 'leave me alone,' 'go away,' or 'I’m not discussing this with you.'
- The usage of sexualised language or imagery and unwelcome sexual attention or advances
- Swearing, usage of strong or disturbing language
- Demonstrating the graphics or any other content you know may be considered disturbing
- Starting and/or participating in arguments related to politics
- Assuming or promoting any kind of inequality including but not limited to: age, body size, disability, ethnicity, gender identity and expression, nationality and race, personal appearance, religion, or sexual identity and orientation
- Drug promotion of any kind
- Attacking personal tastes
- Other conduct which you know could reasonably be considered inappropriate in a professional setting.
- Trolling, insulting/derogatory comments, public or private harassment
- Publishing others' private information, such as a physical or electronic address, without explicit permission
- Not being respectful to reasonable communication boundaries, such as 'leave me alone,' 'go away,' or 'I’m not discussing this with you.'
- The usage of sexualised language or imagery and unwelcome sexual attention or advances
- Swearing, usage of strong or disturbing language
- Demonstrating the graphics or any other content you know may be considered disturbing
- Starting and/or participating in arguments related to politics
- Assuming or promoting any kind of inequality including but not limited to: age, body size, disability, ethnicity, gender identity and expression, nationality and race, personal appearance, religion, or sexual identity and orientation
- Drug promotion of any kind
- Attacking personal tastes
- Other conduct which you know could reasonably be considered inappropriate in a professional setting.

## Enforcement

Expand Down
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ Warunki zaliczenia zadania:

```ts
type CrewMember = {
fullName: string;
nationality: string;
age: number;
profession: string;
fullName: string;
nationality: string;
age: number;
profession: string;
};
```

Expand All @@ -33,11 +33,11 @@ type CrewMember = {

## Kryteria oceny

- Jakość kodu
- Użyte narzędzia
- Poprawność działania kodu
- Jakość kodu
- Użyte narzędzia
- Poprawność działania kodu

## Podpowiedzi

- możesz zainstalować dowolne bilbioteki potrzebne do realizacji zadania
- jeżeli brakuje danych, możesz je wymyślić lub wygenerować
- możesz zainstalować dowolne bilbioteki potrzebne do realizacji zadania
- jeżeli brakuje danych, możesz je wymyślić lub wygenerować
17 changes: 17 additions & 0 deletions components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "styles/globals.css",
"baseColor": "slate",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}
35 changes: 35 additions & 0 deletions components/crewInformation/CrewInformationContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use client';

import React, { useMemo } from 'react';
import { useParams } from 'next/navigation';
import { CrewMembers } from './CrewMembers';
import { CrewInformationLoading } from './CrewInformationLoading';
import { Pagination } from '@/components/pagination/Pagination';
import { CrewInformationError } from '../errors/CrewInformationError';
import { useGetCrew } from '@/hooks/useGetCrew';

export const CrewInformationContainer = () => {
const params = useParams();
const pageNumber = useMemo(() => {
return params && params.page ? Number(params.page) : 1;
}, [params]);

const { data: crewInfomration, isLoading, error } = useGetCrew(pageNumber);

if (error) return <CrewInformationError error={error} />;
return (
<>
{isLoading && !crewInfomration ? (
<CrewInformationLoading />
) : (
<>
<CrewMembers crew={crewInfomration?.crewmates} />
<Pagination
currentPage={pageNumber}
maxItems={crewInfomration?.crewAmmount ?? 0}
/>
</>
)}
</>
);
};
10 changes: 10 additions & 0 deletions components/crewInformation/CrewInformationLoading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from 'react';
import { LoadingState } from '@/components/ui/loading-state';

export const CrewInformationLoading = () => {
return (
<div className="w-full mt-32 flex justify-center items-center">
<LoadingState data-testid="crew-loader" className="w-12 h-12" />
</div>
);
};
36 changes: 36 additions & 0 deletions components/crewInformation/CrewMember.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Globe2, GraduationCap, Hourglass, User } from 'lucide-react';
import { CrewMemberSchema } from '@/schema/crew/crewMemberSchema';

interface Props {
member: CrewMemberSchema;
}

export const CrewMember = ({
member: { fullName, age, nationality, profession },
}: Props) => {
return (
<Card className="hover:scale-[1.02] transition-transform duration-200">
<CardHeader>
<CardTitle className="flex gap-2">
<User /> <span>{fullName}</span>
</CardTitle>
</CardHeader>
<CardContent>
<div className="flex items-center gap-2">
<Hourglass size={16} />
<span>{age}</span>
</div>
<div className="flex items-center gap-2">
<Globe2 size={16} />
<span>{nationality}</span>
</div>
<div className="flex items-center gap-2">
<GraduationCap size={16} />
<span>{profession}</span>
</div>
</CardContent>
</Card>
);
};
24 changes: 24 additions & 0 deletions components/crewInformation/CrewMembers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';
import { CrewMember } from './CrewMember';
import { CrewMemberSchema } from '@/schema/crew/crewMemberSchema';

interface Props {
crew?: CrewMemberSchema[];
}

export const CrewMembers = ({ crew }: Props) => {
return (
<>
<h1 className="mt-4 mb-10 text-xl sm:text-2xl font-semibold">
Informations about{' '}
<span className="text-primary font-bold">crew members</span>
</h1>
<div className="w-full gap-4 max-w-5xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-2 ">
{crew &&
crew.map((crewMember, i) => (
<CrewMember member={crewMember} key={i} />
))}
</div>
</>
);
};
33 changes: 33 additions & 0 deletions components/errors/CrewInformationError.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import { buttonVariants } from '@/components/ui/button';
import Link from 'next/link';

interface Props {
error: Error | null;
}

export const CrewInformationError = ({ error }: Props) => {
return (
<div className=" w-full mt-20 flex flex-col justify-center items-center text-center gap-6">
<h1 className="text-2xl md:text-4xl font-semibold">
Ops... Something went wrong
</h1>
<p className="text-lg text-muted-foreground">{error?.message}</p>

<div className="flex items-center gap-4 mt-10">
<Link
href="/"
className={buttonVariants({ variant: 'secondary' })}
>
Back home
</Link>
<Link
href="/task/1"
className={buttonVariants({ variant: 'default' })}
>
Try again
</Link>
</div>
</div>
);
};
28 changes: 28 additions & 0 deletions components/home/Home.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import { buttonVariants } from '@/components/ui/button';
import { Rocket } from 'lucide-react';
import Link from 'next/link';

export const Home = () => {
return (
<div className="flex flex-col justify-center items-center w-full mt-10 ">
<div className="flex place-items-center">
<h1 className="text-4xl font-bold text-center">
Winter Camp 2024 Recruitment Task
</h1>
</div>

<Link
href="/task/1"
className={`${buttonVariants({
variant: 'default',
})} mt-6 text-xl`}
>
Come and meet the crew members!
</Link>
<div className="mt-10 sm:mt-20 hover:scale-150 duration-500 hover:text-primary transition-all ">
<Rocket size={120} />
</div>
</div>
);
};
22 changes: 22 additions & 0 deletions components/layout/Layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import { Nav } from './nav/Nav';
import { Inter } from 'next/font/google';

const inter = Inter({ subsets: ['latin'] });

type Props = {
children: React.ReactNode;
};

export const Layout = ({ children }: Props) => {
return (
<div
className={`flex-col bg-background min-h-screen relative ${inter.className} `}
>
<Nav />
<main className="flex-1 max-w-7xl mx-auto p-4 md:p-6">
{children}
</main>
</div>
);
};
32 changes: 32 additions & 0 deletions components/layout/nav/Nav.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import { ThemeToggle } from './ThemeToggle';
import { buttonVariants } from '@/components/ui/button';
import { GithubIcon } from 'lucide-react';
import Link from 'next/link';

export const Nav = () => {
return (
<nav className="w-full sticky top-0 left-0 border-b border-border/40 bg-background/95 z-50 shadow-sm backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="w-full container flex justify-between items-center h-16">
<Link href="/" className="text-sm sm:text-xl font-semibold">
Winter camp 2024
</Link>
<div className="flex items-center gap-2">
<Link
href="https://github.com/sepetowski/winter-camp-2024"
passHref
target="_blank"
className={buttonVariants({
variant: 'ghost',
size: 'icon',
})}
>
<GithubIcon />
</Link>

<ThemeToggle />
</div>
</div>
</nav>
);
};
39 changes: 39 additions & 0 deletions components/layout/nav/ThemeToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use client';

import * as React from 'react';
import { Moon, Sun } from 'lucide-react';
import { useTheme } from 'next-themes';
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';

export const ThemeToggle = () => {
const { setTheme } = useTheme();

return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon">
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">Toggle theme</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => setTheme('light')}>
Light
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme('dark')}>
Dark
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme('system')}>
System
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
};
Loading