feat: implement HeroSection global configuration and integrate with Hero component

This commit is contained in:
ArraysID
2025-05-10 03:32:19 +07:00
parent f31aad8570
commit 7b490702b9
6 changed files with 209 additions and 50 deletions
@@ -6,11 +6,14 @@ import {
SparklesIcon,
UsersIcon,
} from '@imphnen-frontend-service/shadcn-ui/atoms';
import { HeroSection } from 'apps/landing/src/payload-types';
import { motion } from 'framer-motion';
import Image from 'next/image';
import { useEffect, useState } from 'react';
import { Fragment, useEffect, useState } from 'react';
export function Hero(props: HeroSection) {
const { badgeText, buttons, description, highlight, title, stats } = props;
export function Hero() {
const [scrollY, setScrollY] = useState(0);
useEffect(() => {
@@ -24,11 +27,8 @@ export function Hero() {
return (
<section className="relative w-full py-20 md:py-32 lg:py-40 overflow-hidden">
{/* Background Elements */}
<div className="absolute inset-0 -z-10 overflow-hidden">
<div className="absolute top-0 left-0 w-full h-full bg-gradient-to-b from-background to-background/50" />
{/* Animated Gradient Orbs */}
<div
className="absolute top-1/4 -left-20 w-80 h-80 rounded-full bg-gradient-to-r from-primary/20 to-blue-400/20 blur-3xl"
style={{
@@ -43,8 +43,6 @@ export function Hero() {
opacity: Math.max(0.2, 1 - scrollY * 0.001),
}}
/>
{/* Grid Pattern */}
<div className="absolute inset-0 bg-[linear-gradient(rgba(59,130,246,0.05)_1px,transparent_1px),linear-gradient(to_right,rgba(59,130,246,0.05)_1px,transparent_1px)] bg-[size:40px_40px]" />
</div>
@@ -58,63 +56,56 @@ export function Hero() {
>
<div className="inline-flex items-center rounded-full border px-3 py-1 text-sm">
<SparklesIcon className="mr-1 h-3.5 w-3.5 text-primary" />
<span>Komunitas Programmer Indonesia</span>
<span>{badgeText}</span>
</div>
<div className="space-y-4">
<h1 className="text-4xl font-bold tracking-tighter sm:text-5xl md:text-6xl lg:text-7xl bg-clip-text text-transparent bg-gradient-to-r from-foreground via-foreground to-foreground/70">
Programmer Handal, <br />
{title} <br />
<span className="bg-clip-text text-transparent bg-gradient-to-r from-primary to-blue-400">
Tanpa Ribet
{highlight}
</span>
</h1>
<p className="max-w-[600px] text-muted-foreground md:text-xl">
Temukan potensi programming Anda bersama komunitas yang
mendukung, tutorial interaktif, dan sumber daya berkualitas
tinggi.
{description}
</p>
</div>
<div className="flex flex-col sm:flex-row gap-4">
<a href="https://facebook.com">
<a href={buttons.primaryUrl} target="_blank">
<Button
size="lg"
className="bg-gradient-to-r from-primary to-blue-400 hover:from-primary/90 hover:to-blue-400/90 transition-all duration-300"
>
Mulai Belajar
{buttons.primaryLabel}
</Button>
</a>
<a href={buttons.secondaryUrl} target="_blank">
<Button
size="lg"
variant="outline"
className="group relative overflow-hidden border-primary"
>
<span className="absolute inset-0 bg-gradient-to-r from-primary/10 to-blue-400/10 translate-y-full group-hover:translate-y-0 transition-transform duration-300" />
<span className="relative">{buttons.secondaryLabel}</span>
</Button>
</a>
<Button
size="lg"
variant="outline"
className="group relative overflow-hidden border-primary"
>
<span className="absolute inset-0 bg-gradient-to-r from-primary/10 to-blue-400/10 translate-y-full group-hover:translate-y-0 transition-transform duration-300"></span>
<span className="relative">Gabung Discord</span>
</Button>
</div>
<div className="flex items-center gap-8">
<div className="flex flex-col items-center">
<div className="text-2xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-primary to-blue-400">
100K+
</div>
<div className="text-xs text-muted-foreground">Member</div>
</div>
<div className="h-10 border-r border-border"></div>
<div className="flex flex-col items-center">
<div className="text-2xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-primary to-blue-400">
500+
</div>
<div className="text-xs text-muted-foreground">Tutorial</div>
</div>
<div className="h-10 border-r border-border"></div>
<div className="flex flex-col items-center">
<div className="text-2xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-primary to-blue-400">
24/7
</div>
<div className="text-xs text-muted-foreground">Yapping</div>
</div>
{stats?.map(({ value, label }, i) => (
<Fragment key={i}>
<div className="flex flex-col items-center">
<div className="text-2xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-primary to-blue-400">
{value}
</div>
<div className="text-xs text-muted-foreground">{label}</div>
</div>
{i < stats.length - 1 && (
<div className="h-10 border-r border-border mx-4" />
)}
</Fragment>
))}
</div>
</motion.div>
@@ -125,15 +116,12 @@ export function Hero() {
transition={{ duration: 0.5, delay: 0.2 }}
>
<div className="relative">
{/* Decorative Elements */}
<div className="absolute -top-6 -left-6 w-12 h-12 rounded-lg border border-primary/30 bg-background/50 backdrop-blur-sm flex items-center justify-center">
<CodeIcon className="h-6 w-6 text-primary" />
</div>
<div className="absolute -bottom-6 -right-6 w-12 h-12 rounded-lg border border-blue-400/30 bg-background/50 backdrop-blur-sm flex items-center justify-center">
<UsersIcon className="h-6 w-6 text-blue-400" />
</div>
{/* Main Image */}
<div className="relative z-10 rounded-2xl overflow-hidden border shadow-2xl">
<div className="absolute inset-0 bg-gradient-to-tr from-primary/10 to-blue-400/10" />
<Image
+5 -2
View File
@@ -1,3 +1,4 @@
import { getGlobalsHeroSection } from '../../repositories/get-globals-hero-section';
import { CallToAction } from './_components/call-to-action';
import { Community } from './_components/community';
import { Features } from './_components/features';
@@ -7,12 +8,14 @@ import { Hero } from './_components/hero';
import { LearningResources } from './_components/learning-resources';
import { Testimonials } from './_components/testimonials';
export default function Page() {
export default async function Page() {
const heroData = await getGlobalsHeroSection();
return (
<div className="flex min-h-screen flex-col">
<Header />
<main className="flex-1">
<Hero />
<Hero {...heroData} />
<Features />
<Community />
<LearningResources />
+101
View File
@@ -0,0 +1,101 @@
import type { GlobalConfig } from 'payload';
export const HeroSection: GlobalConfig = {
slug: 'hero-section',
label: 'Hero Section',
access: {
read: () => true,
update: () => true,
},
fields: [
{
name: 'badgeText',
label: 'Badge Text',
type: 'text',
required: true,
defaultValue: 'Komunitas Programmer Indonesia',
},
{
name: 'title',
label: 'Main Title',
type: 'text',
required: true,
defaultValue: 'Programmer Handal,',
},
{
name: 'highlight',
label: 'Highlight Text',
type: 'text',
required: true,
defaultValue: 'Tanpa Ribet',
},
{
name: 'description',
label: 'Description',
type: 'textarea',
required: true,
defaultValue:
'Temukan potensi programming Anda bersama komunitas yang mendukung, tutorial interaktif, dan sumber daya berkualitas tinggi.',
},
{
type: 'group',
name: 'buttons',
label: 'Buttons',
fields: [
{
name: 'primaryLabel',
label: 'Primary Button Label',
type: 'text',
required: true,
defaultValue: 'Mulai Belajar',
},
{
name: 'primaryUrl',
label: 'Primary Button URL',
type: 'text',
required: true,
defaultValue: 'https://web.facebook.com/groups/1032515944638255',
},
{
name: 'secondaryLabel',
label: 'Secondary Button Label',
type: 'text',
required: true,
defaultValue: 'Gabung Discord',
},
{
name: 'secondaryUrl',
label: 'Secondary Button URL',
type: 'text',
required: true,
defaultValue: 'https://discord.com/invite/imphnen',
},
],
},
{
name: 'stats',
label: 'Statistics',
type: 'array',
minRows: 1,
fields: [
{
name: 'value',
label: 'Value',
type: 'text',
required: true,
},
{
name: 'label',
label: 'Label',
type: 'text',
required: true,
},
],
defaultValue: [
{ value: '180K+', label: 'Member' },
{ value: '500+', label: 'Tutorial' },
{ value: '24/7', label: 'Yapping' },
],
},
],
};
+60 -2
View File
@@ -84,8 +84,12 @@ export interface Config {
db: {
defaultIDType: number;
};
globals: {};
globalsSelect: {};
globals: {
'hero-section': HeroSection;
};
globalsSelect: {
'hero-section': HeroSectionSelect<false> | HeroSectionSelect<true>;
};
locale: null;
user: User & {
collection: 'users';
@@ -271,6 +275,60 @@ export interface PayloadMigrationsSelect<T extends boolean = true> {
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "hero-section".
*/
export interface HeroSection {
id: number;
badgeText: string;
title: string;
highlight: string;
description: string;
buttons: {
primaryLabel: string;
primaryUrl: string;
secondaryLabel: string;
secondaryUrl: string;
};
stats?:
| {
value: string;
label: string;
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".
*/
export interface HeroSectionSelect<T extends boolean = true> {
badgeText?: T;
title?: T;
highlight?: T;
description?: T;
buttons?:
| T
| {
primaryLabel?: T;
primaryUrl?: T;
secondaryLabel?: T;
secondaryUrl?: T;
};
stats?:
| T
| {
value?: T;
label?: T;
id?: T;
};
updatedAt?: T;
createdAt?: T;
globalType?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "auth".
+2
View File
@@ -5,6 +5,7 @@ import { buildConfig } from 'payload';
import sharp from 'sharp';
import { fileURLToPath } from 'url';
import { HeroSection } from './collections/HeroSection';
import { Media } from './collections/Media';
import { Users } from './collections/Users';
@@ -25,6 +26,7 @@ export default buildConfig({
},
},
collections: [Users, Media],
globals: [HeroSection],
editor: lexicalEditor(),
secret: process.env.CMS_SECRET || '',
typescript: {
@@ -0,0 +1,7 @@
import { payload } from '../lib/payload';
export async function getGlobalsHeroSection() {
return await payload.findGlobal({
slug: 'hero-section',
});
}