feat: add FeaturesSection global configuration and integrate with Features component
This commit is contained in:
@@ -6,40 +6,22 @@ import {
|
||||
LaptopIcon,
|
||||
UsersIcon,
|
||||
} from '@imphnen-frontend-service/shadcn-ui/atoms';
|
||||
import { FeaturesSection } from 'apps/landing/src/payload-types';
|
||||
import { motion, useInView } from 'framer-motion';
|
||||
import { useRef } from 'react';
|
||||
|
||||
export function Features() {
|
||||
export function Features(props: FeaturesSection) {
|
||||
const { heading, subheading, features } = props;
|
||||
|
||||
const featuresFormatted = features?.map((f) => ({
|
||||
icon: IconMapper(String(f.icon)),
|
||||
title: f.title,
|
||||
description: f.description,
|
||||
}));
|
||||
|
||||
const ref = useRef(null);
|
||||
const isInView = useInView(ref, { once: true, amount: 0.2 });
|
||||
|
||||
const features = [
|
||||
{
|
||||
icon: <LaptopIcon className="h-6 w-6 text-primary" />,
|
||||
title: 'Belajar Tanpa Koding',
|
||||
description:
|
||||
'Pelajari konsep programming dengan cara yang mudah dipahami tanpa harus menulis kode yang rumit.',
|
||||
},
|
||||
{
|
||||
icon: <UsersIcon className="h-6 w-6 text-primary" />,
|
||||
title: 'Komunitas Supportif',
|
||||
description:
|
||||
'Bergabunglah dengan komunitas programmer Indonesia yang siap membantu dan berbagi pengalaman.',
|
||||
},
|
||||
{
|
||||
icon: <BookOpenIcon className="h-6 w-6 text-primary" />,
|
||||
title: 'Tutorial Interaktif',
|
||||
description:
|
||||
'Akses tutorial interaktif yang membuat konsep programming lebih mudah untuk dipahami.',
|
||||
},
|
||||
{
|
||||
icon: <CodeIcon className="h-6 w-6 text-primary" />,
|
||||
title: 'Proyek Praktis',
|
||||
description:
|
||||
'Terapkan pengetahuan Anda dalam proyek nyata dengan panduan langkah demi langkah.',
|
||||
},
|
||||
];
|
||||
|
||||
const containerVariants = {
|
||||
hidden: { opacity: 0 },
|
||||
visible: {
|
||||
@@ -64,7 +46,6 @@ export function Features() {
|
||||
id="fitur"
|
||||
className="w-full py-20 md:py-32 relative overflow-hidden"
|
||||
>
|
||||
{/* Background Elements */}
|
||||
<div className="absolute inset-0 -z-10">
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-background via-background to-muted/50" />
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[800px] h-[800px] rounded-full bg-gradient-to-tr from-primary/5 to-blue-400/5 blur-3xl" />
|
||||
@@ -82,14 +63,10 @@ export function Features() {
|
||||
Fitur Unggulan
|
||||
</div>
|
||||
<h2 className="text-3xl font-bold tracking-tighter md:text-4xl/tight lg:text-5xl">
|
||||
Belajar programming dengan{' '}
|
||||
<span className="bg-clip-text text-transparent bg-gradient-to-r from-primary to-blue-400">
|
||||
cara yang lebih baik
|
||||
</span>
|
||||
{heading}
|
||||
</h2>
|
||||
<p className="max-w-[800px] mx-auto text-muted-foreground md:text-lg">
|
||||
IMPHNEN hadir dengan berbagai fitur untuk membantu kamu menjadi
|
||||
programmer handal tanpa harus pusing dengan coding.
|
||||
{subheading}
|
||||
</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
@@ -100,7 +77,7 @@ export function Features() {
|
||||
initial="hidden"
|
||||
animate={isInView ? 'visible' : 'hidden'}
|
||||
>
|
||||
{features.map((feature, index) => (
|
||||
{featuresFormatted?.map((feature, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
className="group relative overflow-hidden rounded-xl border bg-background/50 backdrop-blur-sm p-6 transition-all hover:shadow-md hover:shadow-primary/5 hover:border-primary/50"
|
||||
@@ -124,3 +101,18 @@ export function Features() {
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function IconMapper(name: string) {
|
||||
switch (name) {
|
||||
case 'LaptopIcon':
|
||||
return <LaptopIcon className="h-6 w-6 text-primary" />;
|
||||
case 'UsersIcon':
|
||||
return <UsersIcon className="h-6 w-6 text-primary" />;
|
||||
case 'BookOpenIcon':
|
||||
return <BookOpenIcon className="h-6 w-6 text-primary" />;
|
||||
case 'CodeIcon':
|
||||
return <CodeIcon className="h-6 w-6 text-primary" />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { getGlobalsFeaturesSection } from '../../repositories/get-globals-features-section';
|
||||
import { getGlobalsHeroSection } from '../../repositories/get-globals-hero-section';
|
||||
import { CallToAction } from './_components/call-to-action';
|
||||
import { Community } from './_components/community';
|
||||
@@ -10,13 +11,14 @@ import { Testimonials } from './_components/testimonials';
|
||||
|
||||
export default async function Page() {
|
||||
const heroData = await getGlobalsHeroSection();
|
||||
const featuresData = await getGlobalsFeaturesSection();
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col">
|
||||
<Header />
|
||||
<main className="flex-1">
|
||||
<Hero {...heroData} />
|
||||
<Features />
|
||||
<Features {...featuresData} />
|
||||
<Community />
|
||||
<LearningResources />
|
||||
<Testimonials />
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import { GlobalConfig } from 'payload';
|
||||
|
||||
export const FeaturesSection: GlobalConfig = {
|
||||
slug: 'features-section',
|
||||
label: 'Features Section',
|
||||
access: {
|
||||
read: (): boolean => true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'heading',
|
||||
type: 'text',
|
||||
defaultValue: 'Belajar programming dengan cara yang lebih baik',
|
||||
},
|
||||
{
|
||||
name: 'subheading',
|
||||
type: 'text',
|
||||
defaultValue:
|
||||
'IMPHNEN hadir dengan berbagai fitur untuk membantu kamu menjadi programmer handal tanpa harus pusing dengan coding.',
|
||||
},
|
||||
{
|
||||
name: 'features',
|
||||
type: 'array',
|
||||
defaultValue: [
|
||||
{
|
||||
icon: 'LaptopIcon',
|
||||
title: 'Belajar Tanpa Koding',
|
||||
description:
|
||||
'Pelajari konsep programming dengan cara yang mudah dipahami tanpa harus menulis kode yang rumit.',
|
||||
},
|
||||
{
|
||||
icon: 'UsersIcon',
|
||||
title: 'Komunitas Supportif',
|
||||
description:
|
||||
'Bergabunglah dengan komunitas programmer Indonesia yang siap membantu dan berbagi pengalaman.',
|
||||
},
|
||||
{
|
||||
icon: 'BookOpenIcon',
|
||||
title: 'Tutorial Interaktif',
|
||||
description:
|
||||
'Akses tutorial interaktif yang membuat konsep programming lebih mudah untuk dipahami.',
|
||||
},
|
||||
{
|
||||
icon: 'CodeIcon',
|
||||
title: 'Proyek Praktis',
|
||||
description:
|
||||
'Terapkan pengetahuan Anda dalam proyek nyata dengan panduan langkah demi langkah.',
|
||||
},
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
name: 'icon',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'LaptopIcon', value: 'LaptopIcon' },
|
||||
{ label: 'UsersIcon', value: 'UsersIcon' },
|
||||
{ label: 'BookOpenIcon', value: 'BookOpenIcon' },
|
||||
{ label: 'CodeIcon', value: 'CodeIcon' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
type: 'textarea',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -86,9 +86,11 @@ export interface Config {
|
||||
};
|
||||
globals: {
|
||||
'hero-section': HeroSection;
|
||||
'features-section': FeaturesSection;
|
||||
};
|
||||
globalsSelect: {
|
||||
'hero-section': HeroSectionSelect<false> | HeroSectionSelect<true>;
|
||||
'features-section': FeaturesSectionSelect<false> | FeaturesSectionSelect<true>;
|
||||
};
|
||||
locale: null;
|
||||
user: User & {
|
||||
@@ -301,6 +303,25 @@ export interface HeroSection {
|
||||
updatedAt?: string | null;
|
||||
createdAt?: string | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "features-section".
|
||||
*/
|
||||
export interface FeaturesSection {
|
||||
id: number;
|
||||
heading?: string | null;
|
||||
subheading?: string | null;
|
||||
features?:
|
||||
| {
|
||||
icon?: ('LaptopIcon' | 'UsersIcon' | 'BookOpenIcon' | 'CodeIcon') | null;
|
||||
title?: string | null;
|
||||
description?: string | null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
updatedAt?: string | null;
|
||||
createdAt?: string | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "hero-section_select".
|
||||
@@ -329,6 +350,25 @@ export interface HeroSectionSelect<T extends boolean = true> {
|
||||
createdAt?: T;
|
||||
globalType?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "features-section_select".
|
||||
*/
|
||||
export interface FeaturesSectionSelect<T extends boolean = true> {
|
||||
heading?: T;
|
||||
subheading?: T;
|
||||
features?:
|
||||
| T
|
||||
| {
|
||||
icon?: T;
|
||||
title?: T;
|
||||
description?: T;
|
||||
id?: T;
|
||||
};
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
globalType?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "auth".
|
||||
|
||||
@@ -5,6 +5,7 @@ import { buildConfig } from 'payload';
|
||||
import sharp from 'sharp';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
import { FeaturesSection } from './collections/FeaturesSection';
|
||||
import { HeroSection } from './collections/HeroSection';
|
||||
import { Media } from './collections/Media';
|
||||
import { Users } from './collections/Users';
|
||||
@@ -26,7 +27,7 @@ export default buildConfig({
|
||||
},
|
||||
},
|
||||
collections: [Users, Media],
|
||||
globals: [HeroSection],
|
||||
globals: [HeroSection, FeaturesSection],
|
||||
editor: lexicalEditor(),
|
||||
secret: process.env.CMS_SECRET || '',
|
||||
typescript: {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { payload } from '../lib/payload';
|
||||
|
||||
export async function getGlobalsFeaturesSection() {
|
||||
return await payload.findGlobal({
|
||||
slug: 'features-section',
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user