Making a reusable review card component - How to get the star rating - reactjs

I want to be able to make a dynamic card component that will eventialy call an api to get the data. Right now I will be using dummy data.
I want a name, description and star rating for each card. What I am not to sure about is how I would get the star images in each card component.
export const ReviewCards: React.FC<ReviewCardsProps> = ({ name, star, description }) => {
return (
<>
<Card className="reviewCards sm:h-415">
<CardContent>
<div className="flex p-4 justify-between">
<p className="text-lg">{name}</p>
<div className="flex w-3.5 space-x-1.5 justify-end">
<img src={StarIcon} alt="Ratings icon" />
<img src={StarIcon} alt="Ratings icon" />
<img src={StarIcon} alt="Ratings icon" />
<img src={StarIcon} alt="Ratings icon" />
<img src={EmptyStar} alt="Ratings icon" />
</div>
</div>
<p className="text-sm font-extralight p-7">{description}</p>
</CardContent>
</Card>
)
}
I dont want to manually put in the images. I want to be able to say
<div className="flex w-3.5 space-x-1.5 justify-end">{star}<div>
And then when calling the component I want to be able to say:
<ReviewCard name="Bianca", star={4} description={...} /

You can create a array and assign the images accordingly..
// Array index starts with 0, so added 1 to the index
const HelperStar = (index) =>
index + 1 < star ? StarIcon : index + 1 - 0.5 === star ? HalfStar : EmptyStar;
const imageSource = new Array(5)
.fill(0)
.map((data, index) => HelperStar(index);
return (
// ...
<div className="flex w-3.5 space-x-1.5 justify-end">
{imageSource.map((data) => <img src={data} alt="Ratings icon" />}
</div>
</div>
//..
);

Related

How to find posts.map error when I think its array already

This is error I get:
98 |
99 | <section>
> 100 | {posts.map((post) =>(
| ^
101 | <BlogCard title={post.title}
102 | author={post.author}
103 | coverPhoto={post.coverPhoto}
This is my code, I tried to find why its not array and no luck, this is my first post here so I don't know if I made it like I should. But I had no luck finding an answer..
import Head from 'next/head'
import Navi from '/components/nav.js'
import Header from '/components/header.js'
import Link from 'next/link'
import { GraphQLClient, gql } from 'graphql-request';
import BlogCard from '/components/BlogCard'
const graphcms = new GraphQLClient(
"SomeAPI"
);
const QUERY = gql`
{
posts {
id
title
datepublish
slug
content{
html
}
author{
ime
avatar{
url
}
}
coverPhoto{
url
}
}
}
`;
export async function getStaticProps(){
const {posts} = await graphcms.request(QUERY);
return{
props:{
posts,
},
revalidate:10,
};
}
export default function Home(posts) {
return (
<div >
<Head>
<title>Osnovna Skola Vuk Karadzic</title>
<meta name="description" content="Web sajt OS Vuk Karadzic Rocevic" />
<link rel="icon" href="/Logo,_Os_Vuk_Karadzic_Rocevic.png" />
</Head>
<main >
<section>
<Header />
</section>
<section>
<nav>
<Navi />
</nav>
<div>
<img className='relative -z-10 ' src="naslovnaslika.jpg" alt="slika skolske torbe na stolu" />
</div>
<div className=' text-lg sm:text-2xl sm:opacity-80 cursor-pointer text sm:flex sm:justify-evenly grid place-items-center'>
<Link href="https://e-dnevnik.edu.ba/Account/Login?ReturnUrl=%2F" title="">
<div className=' text-white circle relative sm:-top-32 mt-5 sm:w-96 sm:h-96 w-64 h-64 rounded-full grid place-items-center'>
<img className=' opacity-80 sm:h-72 h-48 ' src='/student-cap-svgrepo-com.svg' alt='next' />
<h1 className='mb-10 opacity-70 '>e-Dnevnik za ucenike</h1>
</div>
</Link>
<Link href="https://e-dnevnik.edu.ba/Account/Login?ReturnUrl=%2F" title="">
<div className=' text-white circle relative sm:-top-32 w-64 max-xl:mt-5 h-64 sm:w-96 sm:h-96 rounded-full grid place-items-center'>
<img className=' opacity-70 sm:h-72 h-48 ' src="family-silhouette-svgrepo-com.svg" alt="" />
<h1 className='mb-10 opacity-70 '>e-Dnevnik za ucenike</h1>
</div>
</Link>
<Link href="https://e-dnevnik.edu.ba/Account/Login?ReturnUrl=%2F" title="">
<div className='text-white circle relative sm:-top-32 w-64 max-xl:mt-5 h-64 sm:w-96 sm:h-96 rounded-full grid place-items-center'>
<img className=' opacity-70 sm:h-72 h-48 ' src="business-person-silhouette-wearing-tie-svgrepo-com.svg" alt="" />
<h1 href="https://e-dnevnik.edu.ba/Account/Login?ReturnUrl=%2F" className='mb-20 opacity-70 '>e-Dnevnik za ucenike
</h1>
</div>
</Link>
</div>
</section>
<section>
{posts.map((post) =>(
<BlogCard title={post.title}
author={post.author}
coverPhoto={post.coverPhoto}
key={post.id}
datepublish={post.datepublish}
slug={post.slug}/>
))}
</section>
</main>
</div>
)
}
I don't get why is that error occurs, when I'm following online tutorial and checked everything multiple times...
This is BlogCard code:
import Link from 'next/link'
import styles from '/styles/BlogCard.module.css'
export default function BlogPost({title, author, coverPhoto, datepublish,slug}){
return(
<div className={styles.card} >
<Link href={'/posts/' + slug}>
<div className={styles.imgContainer}>
<img src="{coverPhoto.url" alt="" />
</div>
</Link>
</div>
)
}
I just had do add {} to posts in Home. Thanks for replays tho

How can I change the class of an element dynamically via url in React?

I have a navbar with several Links that route to sub-pages. I now want to apply a specific className to the element which is currently clicked/visited.
My idea was to store the new className in a const isClicked and apply this to the element which is clicked. I thought about checking the current path window.location.pathname with the name of inner html of the div in that Link for all Link elements but don't know "how to communicate with that div's content"?
const DashboardWrapper = () => {
// Class Toggler for clicked element - check via url
const isClicked = 'link-item flex items-center mb-4 rounded-md p-2 pl-4 shadow-lg bg-sx-purple text-sx-pink';
// Run the check for isClicked
const checkSite = () => {
let current_site = window.location.pathname
}
return (
<div className="body-wrapper w-6/6 h-screen flex">
<div className="sidebar w-1/6 h-full bg-sx-purple-dark-soft p-4">
<div className="sidebar-head p-4 border-b-2 border-sx-white flex flex-col">
<div className="user-name text-2xl text-sx-pink">Jonas</div>
<div className="user-name text-md text-sx-white">Example GmbH</div>
</div>
<div className="sidebar-body p-4">
<div className="link-wrapper">
<Link to="/dashboard/overview/">
<div className={isClicked}>
<img className="link-icon flex w-4 mr-4" src={imageLocation} alt="imageIcon"/>
<div className="link-text">Overview</div>
</div>
</Link>
...
</div>
</div>
</div>
)}
You can do something like that:
function CustomLink({ children, to, ...props }: LinkProps) {
let resolved = useResolvedPath(to);
let match = useMatch({ path: resolved.pathname, end: true });
return (
<div>
<Link
style={{ textDecoration: match ? "underline" : "none" }}
to={to}
{...props}
>
{children}
</Link>
{match && " (active)"}
</div>
);
}

Pass prop to different page in Next JS/ React

I'm trying to pass props to a new page in Next JS.
I'm mapping through an array, each item should link to a page where I can use data from the array as props:
{mentors.map((mentor) => (
<Link href="/developers/developer">
<div
key="name"
className="flex flex-col text-green-50 bg-gray-800 rounded-md p-6 gap-3 border border-gray-800 hover:border-gray-700 cursor-pointer"
>
<div className="flex justify-between items-center ">
<div className="flex items-center gap-2">
<img src={mentor.pic} className="rounded-full w-10" />{" "}
{mentor.name}
</div>
{mentor.timeZone}
</div>
<p className="text-gray-300">
I am a full-stack dev with over 8 years experience in
making software and web-apps.
</p>
<div className="flex gap-2 items-center">
Can teach:
{mentor.languages.map((language) => (
<p
key={language}
className="px-3 py-1 bg-gray-900 rounded-full max-w-max text-sm"
>
{language}
</p>
))}
</div>
</div>
</Link>
))}
This is the page:
export default function DeveloperPage({ name, picture }) {
return (
<>
{name}
<img src={picture}/> </>)}
I need to pass that {name} and {picture} prop from the item in the array to the new page.
What's the most elegant way to do it in Next?
Thanks!
You can pass it trough URL query
Like this
<Link href={`/developers/developer?name=${mentor.name}&picture=${mentor.picture}`}>
and you can use useRouter on your DeveloperPage to get its values
import { useRouter } from 'next/router'
export default function DeveloperPage() {
const router = useRouter()
const {name, picture} = router.query // <-- passed datas are in here
return (
<>
{name}
<img src={picture}/>
</>
)
}

Wrap ever 1 component into a div and display it in a grid column

Hello I'm kinda noob with react and I don't know how to slice every 1 item into a new div and then display it on the page with grid grid-cols-2 gap-4 tailwindcss styling, so I'd like to have 2 colums and 2 items in each row. Right now even I apply this tailwindcss there is 1 column and every 1 item is on a new row ;/ So I got to slice every one item.title, item.coverImage.url in a new div?
export default function BlogPage({ items }) {
console.log(items);
return (
<div className="max-w-lg mx-auto">
{items?.map((item) => (
<div key={item.slug} className="grid grid-cols-2 gap-4">
<Link href={`/portfolio/${item.slug}`}>
<div>
<a>
{item.title}
<Image
src={item.coverImage.url}
height={item.coverImage.height}
width={item.coverImage.width}
/>
</a>
</div>
</Link>
</div>
))}
</div>
);
}
You just need to add class col-span-1 to the div to explicitly tell how much column-span it needs to occupy.
export default function BlogPage({ items }) {
console.log(items);
return (
<div className="max-w-lg mx-auto">
{items?.map((item) => (
<div key={item.slug} className="grid grid-cols-2 gap-4">
<Link href={`/portfolio/${item.slug}`}>
<div className="col-span-1">
<a>
{item.title}
<Image
src={item.coverImage.url}
height={item.coverImage.height}
width={item.coverImage.width}
/>
</a>
</div>
</Link>
</div>
))}
</div>
);
}

React JS returning as HTML

I know that I am horrible at React JS but I couldn't find anything specific for this one
I want to return this function into my tsx file and write it on screen but it should be in a component
function getUsers() {
const Users = ({ users } : { users:any }) => {
return (
<div>
{users.map((user : any, i : any) => (
<div className="card">
<img className="card-img-top" src={userPic} alt="Card image cap" />
<div className="card-body">
<h5 className="card-title">{user.name}</h5>
<h6 className="card-subtitle mb-2 text-muted">{user.email}</h6>
</div>
</div>
))}
</div>
)
};
return Users;
}```
ReactDOMServer has a function called renderToStaticMarkup that can take some JSX and return the static HTML as a string.
The issue you'll likely have is it isn't formatted with indentation etc.
Your function will probably look something like this:
const generateHtml = (users: any[]) => {
const html = (
<div>
{users.map((user: any, i: any) => (
<div className="card" key={i.toString()}>
<img className="card-img-top" src={user.userPic} alt="Card image cap" />
<div className="card-body">
<h5 className="card-title">{user.name}</h5>
<h6 className="card-subtitle mb-2 text-muted">{user.email}</h6>
</div>
</div>
))}
</div>
);
return ReactDOMServer.renderToStaticMarkup(html);
};
The resulting HTML is:
<div><div class="card"><img class="card-img-top" src="1.png" alt="Card image cap"/><div class="card-body"><h5 class="card-title"></h5><h6 class="card-subtitle mb-2 text-muted"></h6></div></div></div>

Resources