diff --git a/apps/landing/src/app/(frontend)/_components/testimonials.tsx b/apps/landing/src/app/(frontend)/_components/testimonials.tsx index e53dcb4..a85a15f 100644 --- a/apps/landing/src/app/(frontend)/_components/testimonials.tsx +++ b/apps/landing/src/app/(frontend)/_components/testimonials.tsx @@ -1,87 +1,64 @@ 'use client'; import { QuoteIcon } from '@imphnen-frontend-service/shadcn-ui/atoms'; +import { formatCMSImageDataToMedia } from 'apps/landing/src/lib/format'; +import { TestimonialsSection } from 'apps/landing/src/payload-types'; import { motion, useInView } from 'framer-motion'; import Image from 'next/image'; import { useRef } from 'react'; -export function Testimonials() { - const ref = useRef(null); - const isInView = useInView(ref, { once: true, amount: 0.2 }); +export function Testimonials(props: TestimonialsSection) { + const { title, subtitle, items, stats, joinTitle, joinText } = props; - const testimonials = [ - { - quote: - 'Saya yang tadinya tidak mengerti apa-apa tentang programming, sekarang bisa membuat website sendiri dengan mudah. Terima kasih IMPHNEN!', - name: 'Budi Santoso', - role: 'Web Developer Pemula', - avatar: '/placeholder.svg?height=64&width=64', - }, - { - quote: - 'Komunitas yang sangat supportif! Setiap pertanyaan selalu dijawab dengan cepat dan jelas. Diskusi programnya mudah dipahami.', - name: 'Anita Ratna', - role: 'Mobile App Developer', - avatar: '/placeholder.svg?height=64&width=64', - }, - { - quote: - 'Server Discord IMPHNEN adalah tempat belajar terbaik untuk programmer pemula seperti saya. Materinya lengkap dan komunitasnya sangat membantu!', - name: 'Dedi Permana', - role: 'Data Scientist', - avatar: '/placeholder.svg?height=64&width=64', - }, - ]; + const ref = useRef(null); + const isInView = useInView(ref, { once: true, amount: 0.2 }); const containerVariants = { hidden: { opacity: 0 }, - visible: { - opacity: 1, - transition: { - staggerChildren: 0.1, - }, - }, + visible: { opacity: 1, transition: { staggerChildren: 0.1 } }, }; const itemVariants = { hidden: { y: 20, opacity: 0 }, - visible: { - y: 0, - opacity: 1, - transition: { duration: 0.5 }, - }, + visible: { y: 0, opacity: 1, transition: { duration: 0.5 } }, }; return (
- {/* Background Elements */}
-
-
- -

- Testimoni{' '} - - Member - -

-

- Apa kata mereka yang telah bergabung dengan komunitas IMPHNEN? -

-
-
+
+ +

+ {title.split(' ').map((word, i) => + i === title.split(' ').length - 1 ? ( + + {word} + + ) : ( + {word} + ) + )} +

+

+ {subtitle} +

+
- {testimonials.map((testimonial, index) => ( + {items?.map((t, idx) => (
-

- “{testimonial.quote}” + “{t.quote}”

-
- {testimonial.name} + {(() => { + const media = formatCMSImageDataToMedia(t.avatar); + if (!media) return null; + return ( + {media.alt} + ); + })()}
-

{testimonial.name}

-

- {testimonial.role} -

+

{t.name}

+

{t.role}

-
))} - {/* Testimonial Stats */}

- Bergabunglah dengan{' '} - - 10,000+ - {' '} - programmer Indonesia lainnya + {joinTitle.split(' ').map((word, i) => + i === 1 ? ( + + {word} + + ) : ( + {word} + ) + )}

-

- Komunitas kami terus berkembang dengan programmer dari berbagai - latar belakang dan tingkat keahlian. Bersama-sama, kita belajar, - berbagi, dan tumbuh sebagai profesional. -

+

{joinText}

-
-
- 98% + {stats?.map((s, idx) => ( +
+
+ {s.value} +
+
{s.label}
-
- Tingkat Kemalasan -
-
-
-
- 4.9/5 -
-
- Rating Drama -
-
-
-
- 85% -
-
- Mendapat Pekerjaan -
-
-
-
- 24/7 -
-
Yapping
-
+ ))}
diff --git a/apps/landing/src/app/(frontend)/page.tsx b/apps/landing/src/app/(frontend)/page.tsx index 6602ca7..a45ae0d 100644 --- a/apps/landing/src/app/(frontend)/page.tsx +++ b/apps/landing/src/app/(frontend)/page.tsx @@ -2,6 +2,7 @@ import { getGlobalsCommunitiesSection } from '../../services/get-globals-communi import { getGlobalsFeaturesSection } from '../../services/get-globals-features-section'; import { getGlobalsHeroSection } from '../../services/get-globals-hero-section'; import { getGlobalsLearningResourcesSection } from '../../services/get-globals-learning-resources-section'; +import { getGlobalsTestimonialsSection } from '../../services/get-globals-testimonials-section'; import { CallToAction } from './_components/call-to-action'; import { Community } from './_components/community'; import { Features } from './_components/features'; @@ -12,13 +13,19 @@ import { Testimonials } from './_components/testimonials'; export const revalidate = 10; // Seconds export default async function Page() { - const [heroData, featuresData, communitiesData, learningResourcesData] = - await Promise.all([ - getGlobalsHeroSection(), - getGlobalsFeaturesSection(), - getGlobalsCommunitiesSection(), - getGlobalsLearningResourcesSection(), - ]); + const [ + heroData, + featuresData, + communitiesData, + learningResourcesData, + testimonialsData, + ] = await Promise.all([ + getGlobalsHeroSection(), + getGlobalsFeaturesSection(), + getGlobalsCommunitiesSection(), + getGlobalsLearningResourcesSection(), + getGlobalsTestimonialsSection(), + ]); return ( <> @@ -26,7 +33,7 @@ export default async function Page() { - + ); diff --git a/apps/landing/src/collections/TestimonialsSection.ts b/apps/landing/src/collections/TestimonialsSection.ts new file mode 100644 index 0000000..92926b4 --- /dev/null +++ b/apps/landing/src/collections/TestimonialsSection.ts @@ -0,0 +1,93 @@ +import { GlobalConfig } from 'payload'; + +export const TestimonialsSection: GlobalConfig = { + slug: 'testimonials-section', + label: 'Testimonials Section', + fields: [ + { + name: 'title', + label: 'Section Title', + type: 'text', + defaultValue: 'Testimoni Member', + required: true, + }, + { + name: 'subtitle', + label: 'Section Subtitle', + type: 'textarea', + defaultValue: + 'Apa kata mereka yang telah bergabung dengan komunitas IMPHNEN?', + required: true, + }, + { + name: 'items', + label: 'Testimonials Items', + type: 'array', + fields: [ + { name: 'quote', type: 'textarea', label: 'Quote', required: true }, + { name: 'name', type: 'text', label: 'Name', required: true }, + { name: 'role', type: 'text', label: 'Role', required: true }, + { + name: 'avatar', + type: 'upload', + label: 'Avatar', + relationTo: 'media', + required: true, + }, + ], + defaultValue: [ + { + quote: + 'Saya yang tadinya tidak mengerti apa-apa tentang programming, sekarang bisa membuat website sendiri dengan mudah. Terima kasih IMPHNEN!', + name: 'Budi Santoso', + role: 'Web Developer Pemula', + avatar: { id: '', relationTo: 'media' }, + }, + { + quote: + 'Komunitas yang sangat supportif! Setiap pertanyaan selalu dijawab dengan cepat dan jelas. Diskusi programnya mudah dipahami.', + name: 'Anita Ratna', + role: 'Mobile App Developer', + avatar: { id: '', relationTo: 'media' }, + }, + { + quote: + 'Server Discord IMPHNEN adalah tempat belajar terbaik untuk programmer pemula seperti saya. Materinya lengkap dan komunitasnya sangat membantu!', + name: 'Dedi Permana', + role: 'Data Scientist', + avatar: { id: '', relationTo: 'media' }, + }, + ], + }, + { + name: 'stats', + label: 'Statistics', + type: 'array', + fields: [ + { name: 'value', type: 'text', label: 'Value', required: true }, + { name: 'label', type: 'text', label: 'Label', required: true }, + ], + defaultValue: [ + { value: '98%', label: 'Tingkat Kemalasan' }, + { value: '4.9/5', label: 'Rating Drama' }, + { value: '85%', label: 'Mendapat Pekerjaan' }, + { value: '24/7', label: 'Yapping' }, + ], + }, + { + name: 'joinTitle', + label: 'Join Section Title', + type: 'text', + defaultValue: 'Bergabunglah dengan 10,000+ programmer Indonesia lainnya', + required: true, + }, + { + name: 'joinText', + label: 'Join Section Text', + type: 'textarea', + defaultValue: + 'Komunitas kami terus berkembang dengan programmer dari berbagai latar belakang dan tingkat keahlian. Bersama-sama, kita belajar, berbagi, dan tumbuh sebagai profesional.', + required: true, + }, + ], +}; diff --git a/apps/landing/src/payload-types.ts b/apps/landing/src/payload-types.ts index c96a803..ca962da 100644 --- a/apps/landing/src/payload-types.ts +++ b/apps/landing/src/payload-types.ts @@ -89,12 +89,14 @@ export interface Config { 'features-section': FeaturesSection; 'communities-section': CommunitiesSection; 'learning-resources-section': LearningResourcesSection; + 'testimonials-section': TestimonialsSection; }; globalsSelect: { 'hero-section': HeroSectionSelect | HeroSectionSelect; 'features-section': FeaturesSectionSelect | FeaturesSectionSelect; 'communities-section': CommunitiesSectionSelect | CommunitiesSectionSelect; 'learning-resources-section': LearningResourcesSectionSelect | LearningResourcesSectionSelect; + 'testimonials-section': TestimonialsSectionSelect | TestimonialsSectionSelect; }; locale: null; user: User & { @@ -377,6 +379,35 @@ export interface LearningResourcesSection { updatedAt?: string | null; createdAt?: string | null; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "testimonials-section". + */ +export interface TestimonialsSection { + id: number; + title: string; + subtitle: string; + items?: + | { + quote: string; + name: string; + role: string; + avatar: number | Media; + id?: string | null; + }[] + | null; + stats?: + | { + value: string; + label: string; + id?: string | null; + }[] + | null; + joinTitle: string; + joinText: string; + updatedAt?: string | null; + createdAt?: string | null; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "hero-section_select". @@ -483,6 +514,35 @@ export interface LearningResourcesSectionSelect { createdAt?: T; globalType?: T; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "testimonials-section_select". + */ +export interface TestimonialsSectionSelect { + title?: T; + subtitle?: T; + items?: + | T + | { + quote?: T; + name?: T; + role?: T; + avatar?: T; + id?: T; + }; + stats?: + | T + | { + value?: T; + label?: T; + id?: T; + }; + joinTitle?: T; + joinText?: T; + updatedAt?: T; + createdAt?: T; + globalType?: T; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "auth". diff --git a/apps/landing/src/payload.config.ts b/apps/landing/src/payload.config.ts index c53a388..215958e 100644 --- a/apps/landing/src/payload.config.ts +++ b/apps/landing/src/payload.config.ts @@ -13,6 +13,7 @@ import { FeaturesSection } from './collections/FeaturesSection'; import { HeroSection } from './collections/HeroSection'; import { LearningResourcesSection } from './collections/LearningResourcesSection'; import { Media } from './collections/Media'; +import { TestimonialsSection } from './collections/TestimonialsSection'; import { Users } from './collections/Users'; const filename = fileURLToPath(import.meta.url); @@ -37,6 +38,7 @@ export default buildConfig({ FeaturesSection, CommunitiesSection, LearningResourcesSection, + TestimonialsSection, ], editor: lexicalEditor(), secret: process.env.CMS_SECRET || '', diff --git a/apps/landing/src/services/get-globals-testimonials-section.ts b/apps/landing/src/services/get-globals-testimonials-section.ts new file mode 100644 index 0000000..2f9480d --- /dev/null +++ b/apps/landing/src/services/get-globals-testimonials-section.ts @@ -0,0 +1,7 @@ +import { payload } from '../lib/payload'; + +export async function getGlobalsTestimonialsSection() { + return await payload.findGlobal({ + slug: 'testimonials-section', + }); +}