Add reviews to the website

This commit is contained in:
itsfinniii
2026-03-25 22:14:32 +01:00
parent edd6e54859
commit e179aa1743
3 changed files with 123 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
---
import StarRating from './subcomponents/StarRating.astro';
interface Props {
reviews: ReviewListComponent;
}
const reviews = Astro.props.reviews;
let totalStars: number = 0;
const totalReviews: number = reviews.reviews.length;
reviews.reviews.forEach((review) => {
totalStars = totalStars + review.stars;
});
const averageStars = Math.round((totalStars / totalReviews) * 10) / 10;
const reviewsToShow = reviews.reviews.slice(0, 5);
---
<div class="flex flex-row py-12 px-12 lg:container mx-auto gap-y-8 gap-x-18 w-full">
<div class="flex flex-col gap-5 min-w-[32.5%]">
<div class="flex flex-col">
<h2 class="text-3xl font-bold">{reviews.title}</h2>
<div>{reviews.text}</div>
</div>
<div class="flex flex-col gap-1.5 text-lg">
<StarRating size="big" stars={averageStars} />
<p class="italic">{averageStars}</span> stars out of {totalReviews} {totalReviews === 1 ? "review" : "reviews"}.</p>
</div>
</div>
<div class="flex flex-col gap-6.5">
{ reviewsToShow.map((review) => (
<div class="flex flex-col justify-center gap-3 bg-neutral-50 py-4 px-5.5 rounded-2xl shadow-sm">
<div class="flex flex-col justify-center gap-[5px]">
<h4 class="text-2xl font-semibold">{review.name}</h4>
<div>
<StarRating size="small" stars={review.stars} />
</div>
</div>
<div>
{ review.review }
</div>
</div>
)) }
</div>
</div>

View File

@@ -0,0 +1,71 @@
---
interface Props {
stars: number;
size: "big" | "small";
}
function roundToCustomHalf(value: number) {
const whole = Math.floor(value);
const decimal = value - whole;
if (decimal < 0.25) return whole;
if (decimal < 0.75) return whole + 0.5;
return whole + 1;
}
const stars = roundToCustomHalf(Astro.props.stars);
const totalStars = 5;
---
<div class="flex flex-row gap-[4.25px]">
{Array.from({ length: totalStars }).map((_, i) => {
const starValue = i + 1;
let type = "empty";
if (stars >= starValue) {
type = "full";
} else if (stars >= starValue - 0.5) {
type = "half";
}
return (
<svg width={Astro.props.size === "big" ? 32 : 20} height={Astro.props.size === "big" ? 32 : 20} viewBox="3 0 24 24">
{type === "full" && (
<path
fill="gold"
d="M12 2l3.09 6.26L22 9.27l-5 4.87L18.18 22
12 18.56 5.82 22 7 14.14 2 9.27
8.91 8.26 12 2z"
/>
)}
{type === "half" && (
<>
<defs>
<linearGradient id={`half-${i}`}>
<stop offset="50%" stop-color="gold" />
<stop offset="50%" stop-color="lightgray" />
</linearGradient>
</defs>
<path
fill={`url(#half-${i})`}
d="M12 2l3.09 6.26L22 9.27l-5 4.87L18.18 22
12 18.56 5.82 22 7 14.14 2 9.27
8.91 8.26 12 2z"
/>
</>
)}
{type === "empty" && (
<path
fill="lightgray"
d="M12 2l3.09 6.26L22 9.27l-5 4.87L18.18 22
12 18.56 5.82 22 7 14.14 2 9.27
8.91 8.26 12 2z"
/>
)}
</svg>
);
})}
</div>

View File

@@ -5,6 +5,7 @@ import TextWithImage from '../web/TextWithImage.astro';
import UpcomingEvents from '../web/UpcomingEvents.astro';
import WallOfText from '../web/WallOfText.astro';
import EquipmentTable from '../web/EquipmentTable.astro';
import Reviews from '../web/Reviews.astro';
interface Props {
webpage: WebpageComponent[];
@@ -24,6 +25,7 @@ console.log(Astro.props.webpage);
{ component.component === "UpcomingEvents" && <UpcomingEvents upcomingEvents={component} /> }
{ component.component === "FrequentlyAskedQuestions" && <FrequentlyAskedQuestions faq={component} /> }
{ component.component === "EquipmentTable" && <EquipmentTable equipment={component} /> }
{ component.component === "Reviews" && <Reviews reviews={component} /> }
</Fragment>
)) }
</div>