feat: Image Slider Blocks

This commit is contained in:
RizqiSyahrendra 2025-02-11 00:17:10 +07:00
parent 88ca451fd9
commit 269a89be71
15 changed files with 11704 additions and 4 deletions

934
.yarn/releases/yarn-4.6.0.cjs vendored Normal file

File diff suppressed because one or more lines are too long

3
.yarnrc.yml Normal file
View File

@ -0,0 +1,3 @@
nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-4.6.0.cjs

View File

@ -4,7 +4,7 @@
"private": true,
"type": "module",
"scripts": {
"dev": "next dev --turbopack",
"dev": "NEXT_DEBUG_TURBOPACK=1 next dev --turbopack",
"dev-old": "next dev",
"build": "next build",
"start": "next start",
@ -60,5 +60,6 @@
"prettier": "^3.4.2",
"tailwindcss": "^3.4.1",
"typescript": "^5"
}
}
},
"packageManager": "yarn@4.6.0"
}

View File

@ -0,0 +1,8 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<g transform="matrix(1, 0, 0, 1, 27.009001, -39.238998)">
<path fill="#4285F4" d="M -3.264 51.509 C -3.264 50.719 -3.334 49.969 -3.454 49.239 L -14.754 49.239 L -14.754 53.749 L -8.284 53.749 C -8.574 55.229 -9.424 56.479 -10.684 57.329 L -10.684 60.329 L -6.824 60.329 C -4.564 58.239 -3.264 55.159 -3.264 51.509 Z"/>
<path fill="#34A853" d="M -14.754 63.239 C -11.514 63.239 -8.804 62.159 -6.824 60.329 L -10.684 57.329 C -11.764 58.049 -13.134 58.489 -14.754 58.489 C -17.884 58.489 -20.534 56.379 -21.484 53.529 L -25.464 53.529 L -25.464 56.619 C -23.494 60.539 -19.444 63.239 -14.754 63.239 Z"/>
<path fill="#FBBC05" d="M -21.484 53.529 C -21.734 52.809 -21.864 52.039 -21.864 51.239 C -21.864 50.439 -21.724 49.669 -21.484 48.949 L -21.484 45.859 L -25.464 45.859 C -26.284 47.479 -26.754 49.299 -26.754 51.239 C -26.754 53.179 -26.284 54.999 -25.464 56.619 L -21.484 53.529 Z"/>
<path fill="#EA4335" d="M -14.754 43.989 C -12.984 43.989 -11.404 44.599 -10.154 45.789 L -6.734 42.369 C -8.804 40.429 -11.514 39.239 -14.754 39.239 C -19.444 39.239 -23.494 41.939 -25.464 45.859 L -21.484 48.949 C -20.534 46.099 -17.884 43.989 -14.754 43.989 Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,9 @@
import { ImageSliderBlock } from "@/components/Blocks/ImageSlider";
export default function CobaPage() {
return (
<>
<ImageSliderBlock />
</>
);
}

22
src/blocks/ImageSlider.ts Normal file
View File

@ -0,0 +1,22 @@
import { Block } from "payload";
export const ImageSliderBlock: Block = {
slug: "imageSliderBlock",
labels: { plural: "Image Slider", singular: "Image Slider" },
fields: [
{
label: "Images",
name: "images",
type: "array",
minRows: 1,
fields: [
{
label: "Image",
name: "image",
type: "upload",
relationTo: "media",
},
],
},
],
};

View File

@ -1,6 +1,7 @@
import { BeforeFooterBlock } from "@/blocks/BeforeFooter";
import { ContentBlock } from "@/blocks/Content";
import { HorizontalImageContentBlock } from "@/blocks/HorizontalImageContent";
import { ImageSliderBlock } from "@/blocks/ImageSlider";
import { OurTeamBlock } from "@/blocks/OurTeam";
import formatSlug from "@/utils/formatSlug";
import { CollectionConfig } from "payload";
@ -36,7 +37,7 @@ export const Pages: CollectionConfig = {
label: "Page Layout",
type: "blocks",
minRows: 1,
blocks: [ContentBlock, BeforeFooterBlock, OurTeamBlock, HorizontalImageContentBlock],
blocks: [ContentBlock, BeforeFooterBlock, OurTeamBlock, HorizontalImageContentBlock, ImageSliderBlock],
},
{
name: "meta",

View File

@ -0,0 +1,84 @@
import { CardStarRating } from "@/components/Ratings/CardStarRating";
import StarRating from "@/components/Ratings/StarRating";
import { teamMembers } from "@/data/team";
import Image from "next/image";
import { Navigation } from "swiper/modules";
import { Swiper, SwiperSlide } from "swiper/react";
export function GoogleReviewsBlock() {
return (
<section className="bg-scroll relative py-20">
<div className="decoration-14" />
<div className="decoration-15" />
<div className="decoration-16 opacity-035 d-none d-md-block">
<Image src="/assets/images/demo-slick/decoration-2.svg" alt="Image Description" width={128} height={228} />
</div>
<div className="decoration-17 opacity-035 d-none d-md-block">
<Image src="/assets/images/demo-slick/decoration-2.svg" alt="Image Description" width={128} height={228} />
</div>
<div className="container px-6">
<h2 className="text-3xl font-bold text-center">Reviews</h2>
<div className="flex justify-center space-x-2">
<h2 className="text-xl text-center font-bold">4.8</h2>
<div className="mt-1">
<StarRating size={20} value={3} />
</div>
<div className="text-xs font-semibold mt-1 text-neutral-500">Over 200 Reviews</div>
</div>
<div className="mt-20 relative">
<Swiper
loop
spaceBetween={0}
slidesPerView={3}
breakpoints={{
900: {
slidesPerView: 3,
},
500: {
slidesPerView: 2, // When window width is <= 480px
},
0: {
slidesPerView: 1,
},
}}
modules={[Navigation]}
navigation={{
prevEl: ".snbp1",
nextEl: ".snbn1",
}}
watchSlidesProgress
resizeObserver
className="team-carousel owl-carousel owl-theme overflow-hidden position-static"
style={{
opacity: 1,
display: "block",
}}
>
{/* Group item */}
{teamMembers.map((member, index) => (
<SwiperSlide className="owl-item py-2 mt-5" key={index}>
<div>
<CardStarRating />
</div>
</SwiperSlide>
))}
<div className="owl-controls clickable">
<div className="owl-buttons">
<div className="owl-prev snbp1 lef" role="button" tabIndex={0}>
<span className="visually-hidden">Previous Slide</span>
<i className="mi-arrow-left" aria-hidden="true"></i>
</div>
<div className="owl-next snbn1" role="button" tabIndex={0}>
<span className="visually-hidden">Next Slide</span>
<i className="mi-arrow-right" aria-hidden="true"></i>
</div>
</div>
</div>
{/* End Group item */}
</Swiper>
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,75 @@
"use client";
import Image from "next/image";
import { Navigation } from "swiper/modules";
import { Swiper, SwiperSlide } from "swiper/react";
interface ImageSliderBlockProps {
images: { id: string; image: { url: string; alt: string } }[];
}
export function ImageSliderBlock({ images }: ImageSliderBlockProps) {
return (
<div className="container px-6 py-4">
<div className="mt-20 relative">
<Swiper
loop
spaceBetween={0}
slidesPerView={3}
breakpoints={{
900: {
slidesPerView: 3,
},
500: {
slidesPerView: 2, // When window width is <= 480px
},
0: {
slidesPerView: 1,
},
}}
modules={[Navigation]}
navigation={{
prevEl: ".snbp1",
nextEl: ".snbn1",
}}
watchSlidesProgress
resizeObserver
className="team-carousel owl-carousel owl-theme overflow-hidden position-static"
style={{
opacity: 1,
display: "block",
}}
>
{/* Group item */}
{images.map((item, index: number) => (
<SwiperSlide className="owl-item py-2 mt-5" key={index}>
<div className="w-full h-[250px]">
<Image
src={item?.image?.url ?? ""}
width={0}
height={0}
sizes="100vw"
className="w-full h-auto rounded-md"
alt={item?.image?.alt ?? ""}
/>
</div>
</SwiperSlide>
))}
<div className="owl-controls clickable">
<div className="owl-buttons">
<div className="owl-prev snbp1 lef" role="button" tabIndex={0}>
<span className="visually-hidden">Previous Slide</span>
<i className="mi-arrow-left" aria-hidden="true"></i>
</div>
<div className="owl-next snbn1" role="button" tabIndex={0}>
<span className="visually-hidden">Next Slide</span>
<i className="mi-arrow-right" aria-hidden="true"></i>
</div>
</div>
</div>
{/* End Group item */}
</Swiper>
</div>
</div>
);
}

View File

@ -5,12 +5,14 @@ import { ContentBlock } from "./Content";
import { BeforeFooterBlock } from "./BeforeFooter";
import { OurTeamBlock } from "./OurTeam";
import { HorizontalImageContentBlock } from "./HorizontalImageContent";
import { ImageSliderBlock } from "./ImageSlider";
const blockComponents = {
contentBlock: ContentBlock,
beforeFooterBlock: BeforeFooterBlock,
ourTeamBlock: OurTeamBlock,
horizontalImageContentBlock: HorizontalImageContentBlock,
imageSliderBlock: ImageSliderBlock,
};
export const RenderBlocks: React.FC<{

View File

@ -0,0 +1,14 @@
import Image from "next/image";
import StarRating from "./StarRating";
export function CardStarRating() {
return (
<div className="bg-white p-2 rounded-lg shadow-md">
<div className="flex justify-between">
<StarRating size={20} value={3} />
<Image src="/assets/images/google-provider.svg" width={20} height={20} alt="" />
</div>
<p className="text-xs pl-1 mt-4">Today my cat scan</p>
</div>
);
}

View File

@ -0,0 +1,67 @@
import React from "react";
interface StarRatingProps {
value: number;
baseColor?: string;
filledColor?: string;
size?: number;
}
const StarRating: React.FC<StarRatingProps> = ({
value,
baseColor = "#F2EFE7",
filledColor = "#FFD95F",
size = 24,
}) => {
const stars = Array.from({ length: 5 }, (_, i) => {
if (value >= i + 1) {
return (
<svg
key={i}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill={filledColor}
width={size}
height={size}
>
<path
d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
} else if (value > i && value < i + 1) {
return (
<svg key={i} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width={size} height={size}>
<defs>
<linearGradient id={`half-grad-${i}`}>
<stop offset="50%" stopColor={filledColor} />
<stop offset="50%" stopColor={baseColor} />
</linearGradient>
</defs>
<path
fill={`url(#half-grad-${i})`}
d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
} else {
return (
<svg key={i} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill={baseColor} width={size} height={size}>
<path
d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
});
return <div className="flex">{stars}</div>;
};
export default StarRating;

0
src/components/Stars Normal file
View File

View File

@ -197,6 +197,17 @@ export interface Page {
blockName?: string | null;
blockType: 'horizontalImageContentBlock';
}
| {
images?:
| {
image?: (number | null) | Media;
id?: string | null;
}[]
| null;
id?: string | null;
blockName?: string | null;
blockType: 'imageSliderBlock';
}
)[]
| null;
meta?: {
@ -569,6 +580,18 @@ export interface PagesSelect<T extends boolean = true> {
id?: T;
blockName?: T;
};
imageSliderBlock?:
| T
| {
images?:
| T
| {
image?: T;
id?: T;
};
id?: T;
blockName?: T;
};
};
meta?:
| T

10457
yarn.lock Normal file

File diff suppressed because it is too large Load Diff