Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
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
166 changes: 166 additions & 0 deletions apps/web/components/molecules/commentItem/commentItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import Image from 'next/image';
import { Card } from 'ui';
import { timeDifference } from 'utils/timeDifference/timeDifference';

type CommentItemProps = {
author: string;
avatar: string;
comment: string;
date: number;
};

export const CommentItem = ({
author,
avatar,
comment,
date,
}: CommentItemProps) => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I'd make a comment: Comment prop accepting an object. No need for spreading then and you have everything contained in one object

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I'd make a comment: Comment prop accepting an object. No need for spreading then and you have everything contained in one object

Sure, but you neeed to destructure it later or write redundant object prop calls

comment.timestamp
comment.avatar
...and so on

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I'd rather have a param destructurization than something like that tbh.
As for now the CommentItemProps is redundant as the props could be directly typed as Comment.
I used to spread props but over the time I learned in most of the cases it is better to explicitly pass props. There are some exceptions ofc (for example register function for form libs)

return (
<div className="flex-col w-full py-2 mx-auto bg-white sm:px-4 md:px-4 md:w-2/3">
{/*<Card tag="div">*/}
{/* <div className="flex flex-row">*/}
{/* <Image*/}
{/* className="object-cover w-12 h-12 border-2 border-gray-300 rounded-full"*/}
{/* alt="Noob master's avatar"*/}
{/* src={avatar}*/}
{/* width={48}*/}
{/* height={48}*/}
{/* />*/}
{/* <div className="flex-col mt-1">*/}
{/* <div className="flex items-center flex-1 px-4 font-bold leading-tight">*/}
{/* {author}*/}
{/* <span className="ml-2 text-xs font-normal text-gray-500">*/}
{/* {date} 2 weeks ago*/}
{/* </span>*/}
{/* </div>*/}
{/* <div className="flex-1 px-2 ml-2 text-sm font-medium leading-loose text-gray-600">*/}
{/* {comment}*/}
{/* </div>*/}
{/* <button className="inline-flex items-center px-1 pt-2 ml-1 flex-column">*/}
{/* <svg*/}
{/* className="w-5 h-5 ml-2 text-gray-600 cursor-pointer fill-current hover:text-gray-900"*/}
{/* viewBox="0 0 95 78"*/}
{/* xmlns="http://www.w3.org/2000/svg"*/}
{/* >*/}
{/* <path*/}
{/* d="M29.58 0c1.53.064 2.88 1.47 2.879 3v11.31c19.841.769 34.384 8.902 41.247 20.464 7.212 12.15 5.505 27.83-6.384 40.273-.987 1.088-2.82 1.274-4.005.405-1.186-.868-1.559-2.67-.814-3.936 4.986-9.075 2.985-18.092-3.13-24.214-5.775-5.78-15.377-8.782-26.914-5.53V53.99c-.01 1.167-.769 2.294-1.848 2.744-1.08.45-2.416.195-3.253-.62L.85 30.119c-1.146-1.124-1.131-3.205.032-4.312L27.389.812c.703-.579 1.49-.703 2.19-.812zm-3.13 9.935L7.297 27.994l19.153 18.84v-7.342c-.002-1.244.856-2.442 2.034-2.844 14.307-4.882 27.323-1.394 35.145 6.437 3.985 3.989 6.581 9.143 7.355 14.715 2.14-6.959 1.157-13.902-2.441-19.964-5.89-9.92-19.251-17.684-39.089-17.684-1.573 0-3.004-1.429-3.004-3V9.936z"*/}
{/* fill-rule="nonzero"*/}
{/* />*/}
{/* </svg>*/}
{/* </button>*/}
{/* <button className="inline-flex items-center px-1 -ml-1 flex-column">*/}
{/* <svg*/}
{/* className="w-5 h-5 text-gray-600 cursor-pointer hover:text-gray-700"*/}
{/* fill="none"*/}
{/* stroke="currentColor"*/}
{/* viewBox="0 0 24 24"*/}
{/* xmlns="http://www.w3.org/2000/svg"*/}
{/* >*/}
{/* <path*/}
{/* stroke-linecap="round"*/}
{/* stroke-linejoin="round"*/}
{/* stroke-width="2"*/}
{/* d="M14 10h4.764a2 2 0 011.789 2.894l-3.5 7A2 2 0 0115.263 21h-4.017c-.163 0-.326-.02-.485-.06L7 20m7-10V5a2 2 0 00-2-2h-.095c-.5 0-.905.405-.905.905 0 .714-.211 1.412-.608 2.006L7 11v9m7-10h-2M7 20H5a2 2 0 01-2-2v-6a2 2 0 012-2h2.5"*/}
{/* ></path>*/}
{/* </svg>*/}
{/* </button>*/}
{/* </div>*/}
{/* </div>*/}
{/* <hr className="my-2 ml-16 border-gray-200" />*/}
{/* <div className="flex flex-row pt-1 md-10 md:ml-16">*/}
{/* <Image*/}
{/* className="object-cover w-12 h-12 border-2 border-gray-300 rounded-full"*/}
{/* alt="Noob master's avatar"*/}
{/* src={avatar}*/}
{/* width={48}*/}
{/* height={48}*/}
{/* />*/}
{/* <div className="flex-col mt-1">*/}
{/* <div className="flex items-center flex-1 px-4 font-bold leading-tight">*/}
{/* {author}*/}
{/* <span className="ml-2 text-xs font-normal text-gray-500">*/}
{/* {date} 5 days ago*/}
{/* </span>*/}
{/* </div>*/}
{/* <div className="flex-1 px-2 ml-2 text-sm font-medium leading-loose text-gray-600">*/}
{/* {comment}*/}
{/* </div>*/}
{/* <button className="inline-flex items-center px-1 pt-2 ml-1 flex-column">*/}
{/* <svg*/}
{/* className="w-5 h-5 ml-2 text-gray-600 cursor-pointer fill-current hover:text-gray-900"*/}
{/* viewBox="0 0 95 78"*/}
{/* xmlns="http://www.w3.org/2000/svg"*/}
{/* >*/}
{/* <path*/}
{/* d="M29.58 0c1.53.064 2.88 1.47 2.879 3v11.31c19.841.769 34.384 8.902 41.247 20.464 7.212 12.15 5.505 27.83-6.384 40.273-.987 1.088-2.82 1.274-4.005.405-1.186-.868-1.559-2.67-.814-3.936 4.986-9.075 2.985-18.092-3.13-24.214-5.775-5.78-15.377-8.782-26.914-5.53V53.99c-.01 1.167-.769 2.294-1.848 2.744-1.08.45-2.416.195-3.253-.62L.85 30.119c-1.146-1.124-1.131-3.205.032-4.312L27.389.812c.703-.579 1.49-.703 2.19-.812zm-3.13 9.935L7.297 27.994l19.153 18.84v-7.342c-.002-1.244.856-2.442 2.034-2.844 14.307-4.882 27.323-1.394 35.145 6.437 3.985 3.989 6.581 9.143 7.355 14.715 2.14-6.959 1.157-13.902-2.441-19.964-5.89-9.92-19.251-17.684-39.089-17.684-1.573 0-3.004-1.429-3.004-3V9.936z"*/}
{/* fill-rule="nonzero"*/}
{/* />*/}
{/* </svg>*/}
{/* </button>*/}
{/* <button className="inline-flex items-center px-1 -ml-1 flex-column">*/}
{/* <svg*/}
{/* className="w-5 h-5 text-gray-600 cursor-pointer hover:text-gray-700"*/}
{/* xmlns="http://www.w3.org/2000/svg"*/}
{/* viewBox="0 0 20 20"*/}
{/* fill="currentColor"*/}
{/* >*/}
{/* <path d="M2 10.5a1.5 1.5 0 113 0v6a1.5 1.5 0 01-3 0v-6zM6 10.333v5.43a2 2 0 001.106 1.79l.05.025A4 4 0 008.943 18h5.416a2 2 0 001.962-1.608l1.2-6A2 2 0 0015.56 8H12V4a2 2 0 00-2-2 1 1 0 00-1 1v.667a4 4 0 01-.8 2.4L6.8 7.933a4 4 0 00-.8 2.4z" />*/}
{/* </svg>*/}
{/* </button>*/}
{/* </div>*/}
{/* </div>*/}
{/*</Card>*/}
Comment thread
ssynowiec marked this conversation as resolved.
Outdated

<Card tag="div">
<div className="flex flex-row md-10">
<Image
className="object-cover w-12 h-12 border-2 border-gray-300 rounded-full"
alt="Noob master's avatar"
src={avatar}
width={48}
height={48}
/>
<div className="flex-col mt-1">
<div className="flex items-center flex-1 px-4 font-bold leading-tight">
{author}
<span className="ml-2 text-xs font-normal text-gray-500">
{timeDifference(date)}
</span>
</div>
<div className="flex-1 px-2 ml-2 text-sm font-medium leading-loose text-gray-600">
{comment}
</div>
<button className="inline-flex items-center px-1 pt-2 ml-1 flex-column">
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This 2 Reply and Like buttons share styles. Same with the div inside. I'd make a component out of this.
The only difference is the pt-2 which can be applied on some container or spacer.

<svg
className="w-5 h-5 ml-2 text-gray-600 cursor-pointer fill-current hover:text-gray-900"
viewBox="0 0 95 78"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M29.58 0c1.53.064 2.88 1.47 2.879 3v11.31c19.841.769 34.384 8.902 41.247 20.464 7.212 12.15 5.505 27.83-6.384 40.273-.987 1.088-2.82 1.274-4.005.405-1.186-.868-1.559-2.67-.814-3.936 4.986-9.075 2.985-18.092-3.13-24.214-5.775-5.78-15.377-8.782-26.914-5.53V53.99c-.01 1.167-.769 2.294-1.848 2.744-1.08.45-2.416.195-3.253-.62L.85 30.119c-1.146-1.124-1.131-3.205.032-4.312L27.389.812c.703-.579 1.49-.703 2.19-.812zm-3.13 9.935L7.297 27.994l19.153 18.84v-7.342c-.002-1.244.856-2.442 2.034-2.844 14.307-4.882 27.323-1.394 35.145 6.437 3.985 3.989 6.581 9.143 7.355 14.715 2.14-6.959 1.157-13.902-2.441-19.964-5.89-9.92-19.251-17.684-39.089-17.684-1.573 0-3.004-1.429-3.004-3V9.936z"
fillRule="nonzero"
/>
</svg>
</button>
<button className="inline-flex items-center px-1 -ml-1 flex-column">
<svg
className="w-5 h-5 text-gray-600 cursor-pointer hover:text-gray-700"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M14 10h4.764a2 2 0 011.789 2.894l-3.5 7A2 2 0 0115.263 21h-4.017c-.163 0-.326-.02-.485-.06L7 20m7-10V5a2 2 0 00-2-2h-.095c-.5 0-.905.405-.905.905 0 .714-.211 1.412-.608 2.006L7 11v9m7-10h-2M7 20H5a2 2 0 01-2-2v-6a2 2 0 012-2h2.5"
></path>
Comment thread
ssynowiec marked this conversation as resolved.
Outdated
</svg>
</button>
</div>
</div>
</Card>
</div>
);
};
69 changes: 69 additions & 0 deletions apps/web/components/molecules/comments/comments.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { Heading } from 'ui';
import { ProtectedComponent } from 'organisms/protectedComponent/protectedComponent';
import { AddComment } from 'organisms/addComment/addComment';
import { CommentItem } from 'molecules/commentItem/commentItem';

const arr = [
Comment thread
ssynowiec marked this conversation as resolved.
Outdated
{
author: 'Adam Nowak',
comment:
'To zadanie frontendowe wydaje się bardzo wymagające, ale na pewno pozwoli na rozwinięcie umiejętności i poszerzenie wiedzy. Trzeba będzie zwrócić uwagę na każdy szczegół, aby stworzyć estetyczny i responsywny interfejs.',
avatar:
'https://images.unsplash.com/photo-1633332755192-727a05c4013d?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2960&q=80',
date: 1264938840000,
},

{
author: 'Maria Wójcik',
comment:
'Mam nadzieję, że w tym zadaniu będzie można wykorzystać najnowsze technologie i narzędzia, co pozwoli na jeszcze lepsze efekty. Czekam na wyzwania, które pozwolą mi się rozwijać i poszerzać horyzonty.',
avatar:
'https://images.unsplash.com/photo-1633332755192-727a05c4013d?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2960&q=80',
date: 1678794840000,
},

{
author: 'Piotr Kowalczyk',
comment:
'Zadanie frontendowe to wspaniała okazja, aby pokazać swoje umiejętności projektowania interfejsów użytkownika. Nie mogę się doczekać, aby zacząć pracę i zobaczyć, co uda mi się stworzyć.',
avatar:
'https://images.unsplash.com/photo-1633332755192-727a05c4013d?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2960&q=80',
date: 1672444800000,
},

{
author: 'Karolina Nowakowska',
comment:
'Zadanie frontendowe może być trudne, ale warto podjąć wyzwanie. Będzie to okazja do nauki nowych technologii i rozwoju umiejętności, co na pewno będzie przydatne w dalszej karierze.',
avatar:
'https://images.unsplash.com/photo-1633332755192-727a05c4013d?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2960&q=80',
date: 1678808591856,
},
];

const comments = arr.sort(
(
a: { author: string; comment: string; avatar: string; date: number },
b: { author: string; comment: string; avatar: string; date: number },
Comment thread
ssynowiec marked this conversation as resolved.
Outdated
) => b.date - a.date,
);

export const Comments = () => {
return (
<div>
<div className="flex-col w-full py-2 mx-auto bg-white sm:px-4 md:px-4 md:w-2/3">
<div className="pb-2">
<Heading tag="h3" size="medium">
Komentarze ({comments.length})
</Heading>
</div>
<ProtectedComponent info="Aby dodać komentarz musisz być zalogowany">
<AddComment />
</ProtectedComponent>
</div>
{comments.map((comment, id) => (
<CommentItem {...comment} key={id} />
))}
</div>
);
};
57 changes: 57 additions & 0 deletions apps/web/components/molecules/opinionItem/opinionItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Card, Heading, Text } from 'ui';
import Image from 'next/image';
import clsx from 'clsx';
import type { Opinion } from '../../../types/types';

type OpinionItemProps = Opinion;

export const OpinionItem = ({
author,
avatar,
rating,
comment,
}: OpinionItemProps) => {
return (
<div className="p-2">
<Card tag="div">
<div className="flex flex-col md:flex-row justify-center items-center">
<Image
src={avatar}
alt=""
width={1000}
height={1000}
className="w-full h-full rounded-full w-24 h-24 object-cover"
Comment thread
ssynowiec marked this conversation as resolved.
Outdated
aria-hidden="true"
/>
<div className="md:pl-5">
<Heading tag="h3" size="large" className="font-bold">
{author}
</Heading>
<div className="flex items-center">
{Array.from({ length: 5 }, (_, i) => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Would extract this 5 to some constant. Also I'd create an array with some meaningful name and put it outside of the component as it is static.

const starClass =
i < Math.round(rating) ? 'text-yellow-400' : 'text-gray-300';
return (
<svg
aria-hidden="true"
className={clsx('w-5 h-5', starClass)}
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
key={i}
>
{/*<title>First star</title>*/}
Comment thread
ssynowiec marked this conversation as resolved.
Outdated
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"></path>
</svg>
);
})}
</div>
<Text size="medium" tag="p" variant="default" position="left">
{comment}
</Text>
</div>
</div>
</Card>
</div>
);
};
50 changes: 50 additions & 0 deletions apps/web/components/molecules/opinions/opinions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { OpinionItem } from 'molecules/opinionItem/opinionItem';
import type { Opinion } from '../../../types/types';
import { NewOpinionForm } from 'organisms/newOpinionForm/newOpinionForm';
import { OpinionStats } from 'molecules/opinonStats/opinionStats';
import { ProtectedComponent } from 'organisms/protectedComponent/protectedComponent';

const opinions: Opinion[] = [
{
author: 'Jan Kowalski',
avatar:
'https://images.unsplash.com/photo-1633332755192-727a05c4013d?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2960&q=80',
rating: 5,
comment:
'Zadanie programistyczne, które otrzymałem, było dobrze sformułowane i precyzyjnie opisywało wymagania dotyczące tworzenia aplikacji internetowej. Zadanie miało na celu zaimplementowanie funkcjonalności wyszukiwania produktów w bazie danych i wyświetlenia wyników na stronie internetowej.',
},
{
author: 'Jan Kowalski',
avatar:
'https://images.unsplash.com/photo-1633332755192-727a05c4013d?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2960&q=80',
rating: 5,
comment:
'Zadanie programistyczne, które otrzymałem, było dobrze sformułowane i precyzyjnie opisywało wymagania dotyczące tworzenia aplikacji internetowej. Zadanie miało na celu zaimplementowanie funkcjonalności wyszukiwania produktów w bazie danych i wyświetlenia wyników na stronie internetowej.',
},
{
author: 'Jan Kowalski',
avatar:
'https://images.unsplash.com/photo-1633332755192-727a05c4013d?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2960&q=80',
rating: 4,
comment:
'Zadanie programistyczne, które otrzymałem, było dobrze sformułowane i precyzyjnie opisywało wymagania dotyczące tworzenia aplikacji internetowej. Zadanie miało na celu zaimplementowanie funkcjonalności wyszukiwania produktów w bazie danych i wyświetlenia wyników na stronie internetowej.',
},
];

export const Opinions = () => {
return (
<>
<div className="flex flex-col md:flex-row">
<div className="p-2 w-full md:w-1/2">
<ProtectedComponent info="Zaloguj się aby dodać opinię.">
<NewOpinionForm />
</ProtectedComponent>
</div>
<OpinionStats opinions={opinions} />
</div>
{opinions.map((opinion, id) => (
<OpinionItem key={id} {...opinion} />
))}
</>
);
};
Loading