chore: remove cms and fix some styles (#29)

* chore: remove all references to PayloadCMS

* fix: nextjs landing utils

* feat: refactor community, features, hero, learning resources, and testimonials components to use external JSON data

* fix:remove hero logo box

* fix: remove unused image in public

* fix: change logo.png to logo.webp

* fix: update button styles for improved visibility

* chore: restructure component organization

* fix: update import path for global styles in layout component

* fix: adjust alignment of hero stats section for better layout

* fix: update testimonials placeholder sample data

* fix: update header component to use router for navigation and add signin page
This commit is contained in:
Rasyid
2025-05-25 15:13:44 +07:00
committed by GitHub
parent 9a0fee65a5
commit dd03b3da85
61 changed files with 491 additions and 7494 deletions
-43
View File
@@ -1,43 +0,0 @@
import { getGlobalsCallToActionSection } from '../../services/get-globals-call-to-action-section';
import { getGlobalsCommunitiesSection } from '../../services/get-globals-communitues-section';
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';
import { Hero } from './_components/hero';
import { LearningResources } from './_components/learning-resources';
import { Testimonials } from './_components/testimonials';
export const revalidate = 10; // Seconds
export default async function Page() {
const [
heroData,
featuresData,
communitiesData,
learningResourcesData,
testimonialsData,
callToActionData,
] = await Promise.all([
getGlobalsHeroSection(),
getGlobalsFeaturesSection(),
getGlobalsCommunitiesSection(),
getGlobalsLearningResourcesSection(),
getGlobalsTestimonialsSection(),
getGlobalsCallToActionSection(),
]);
return (
<>
<Hero {...heroData} />
<Features {...featuresData} />
<Community {...communitiesData} />
<LearningResources {...learningResourcesData} />
<Testimonials {...testimonialsData} />
<CallToAction {...callToActionData} />
</>
);
}
@@ -1,21 +1,10 @@
'use client';
import { Button } from '@imphnen-frontend-service/shadcn-ui/atoms';
import { CallToActionSection } from 'apps/landing/src/payload-types';
import { Button } from '@components';
import { motion, useInView } from 'framer-motion';
import { useRef } from 'react';
export function CallToAction(props: CallToActionSection) {
const {
title,
highlightedTitle,
subtitle,
primaryButtonLabel,
primaryButtonLink,
secondaryButtonLabel,
secondaryButtonLink,
} = props;
export function CallToAction() {
const ref = useRef(null);
const isInView = useInView(ref, { once: true, amount: 0.2 });
@@ -39,30 +28,38 @@ export function CallToAction(props: CallToActionSection) {
<div className="relative z-10 text-center">
<h2 className="text-3xl md:text-4xl lg:text-5xl font-bold mb-6">
{title}{' '}
Siap Menjadi{' '}
<span className="bg-clip-text text-transparent bg-gradient-to-r from-primary to-blue-400">
{highlightedTitle}
Programmer Handal?
</span>
</h2>
<p className="text-lg text-muted-foreground mb-8 max-w-2xl mx-auto">
{subtitle}
Bergabunglah dengan komunitas IMPHNEN sekarang dan mulai
perjalanan programming mu dengan cara yang menyenangkan!
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<Button
size="lg"
className="bg-gradient-to-r from-primary to-blue-400 hover:from-primary/90 hover:to-blue-400/90 font-bold text-black hover:text-white cursor-pointer"
onClick={() => window.open(primaryButtonLink, '_blank')}
className="group relative w-full sm:w-auto bg-gradient-to-r from-primary to-blue-600 hover:from-primary/90 hover:to-blue-600/90 transition-all duration-300 font-bold text-white hover:text-white/90 shadow-lg cursor-pointer"
onClick={() =>
window.open('https://discord.gg/imphnen', '_blank')
}
>
{primaryButtonLabel}
Gabung Discord
</Button>
<Button
size="lg"
variant="outline"
className="border-primary hover:bg-primary/10"
onClick={() => window.open(secondaryButtonLink, '_blank')}
onClick={() =>
window.open(
'https://facebook.com/groups/programmerhandal',
'_blank'
)
}
>
{secondaryButtonLabel}
Join Facebook Group
</Button>
</div>
</div>
@@ -1,13 +1,13 @@
'use client';
import COMMUNITIES_STATS from '@/data/communities-stats.json';
import COMMUNITIES from '@/data/communities.json';
import { Button } from '@components';
import { Icon } from '@iconify/react';
import { Button } from '@imphnen-frontend-service/shadcn-ui/atoms';
import { CommunitiesSection } from 'apps/landing/src/payload-types';
import { motion, useInView } from 'framer-motion';
import { useRef } from 'react';
export function Community(props: CommunitiesSection) {
const { items, stats } = props;
export function Community() {
const ref = useRef(null);
const isInView = useInView(ref, { once: true, amount: 0.2 });
@@ -61,7 +61,7 @@ export function Community(props: CommunitiesSection) {
initial="hidden"
animate={isInView ? 'visible' : 'hidden'}
>
{items?.map((c, i) => (
{COMMUNITIES.map((c, i) => (
<motion.div
key={i}
className="group relative overflow-hidden rounded-xl border bg-background p-6 transition-all hover:shadow-xl"
@@ -95,7 +95,7 @@ export function Community(props: CommunitiesSection) {
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.5, delay: 0.3 }}
>
{stats?.map((s, i) => (
{COMMUNITIES_STATS.map((s, i) => (
<div key={i} className="space-y-2">
<div className="text-4xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-primary to-blue-400">
{s.value}
@@ -1,13 +1,11 @@
'use client';
import FEATURES from '@/data/features.json';
import { Icon } from '@iconify/react';
import { FeaturesSection } from 'apps/landing/src/payload-types';
import { motion, useInView } from 'framer-motion';
import { useRef } from 'react';
export function Features(props: FeaturesSection) {
const { heading, subheading, features } = props;
export function Features() {
const ref = useRef(null);
const isInView = useInView(ref, { once: true, amount: 0.2 });
@@ -52,10 +50,11 @@ export function Features(props: FeaturesSection) {
Fitur Unggulan
</div>
<h2 className="text-3xl font-bold tracking-tighter md:text-4xl/tight lg:text-5xl">
{heading}
Belajar programming dengan cara yang lebih baik
</h2>
<p className="max-w-[800px] mx-auto text-muted-foreground md:text-lg">
{subheading}
IMPHNEN hadir dengan berbagai fitur untuk membantu kamu menjadi
programmer handal tanpa harus pusing dengan coding.
</p>
</motion.div>
</div>
@@ -66,7 +65,7 @@ export function Features(props: FeaturesSection) {
initial="hidden"
animate={isInView ? 'visible' : 'hidden'}
>
{features?.map((feature, index) => (
{FEATURES.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"
@@ -1,28 +1,12 @@
'use client';
import {
Button,
CodeIcon,
SparklesIcon,
UsersIcon,
} from '@imphnen-frontend-service/shadcn-ui/atoms';
import { formatCMSImageDataToMedia } from 'apps/landing/src/lib/format';
import { HeroSection } from 'apps/landing/src/payload-types';
import HERO_STATS from '@/data/hero-stats.json';
import { Button, SparklesIcon } from '@components';
import { motion } from 'framer-motion';
import Image from 'next/image';
import { Fragment, useEffect, useState } from 'react';
export function Hero(props: HeroSection) {
const {
badgeText,
buttons,
description,
highlight,
title,
stats,
heroImage,
} = props;
export function Hero() {
const [scrollY, setScrollY] = useState(0);
useEffect(() => {
@@ -65,42 +49,51 @@ export function Hero(props: HeroSection) {
>
<div className="inline-flex items-center rounded-full border px-3 py-1 text-sm w-fit">
<SparklesIcon className="mr-1 h-3.5 w-3.5 text-primary" />
<span>{badgeText}</span>
<span>Komunitas Programmer Indonesia</span>
</div>
<div className="space-y-4">
<h1 className="text-4xl md:text-5xl lg:text-6xl xl:text-7xl font-bold tracking-tighter bg-clip-text text-transparent bg-gradient-to-r from-foreground via-foreground to-foreground/70">
{title} <br />
Programmer Handal, <br />
<span className="bg-clip-text text-transparent bg-gradient-to-r from-primary to-blue-400">
{highlight}
Tanpa Ribet
</span>
</h1>
<p className="max-w-[600px] text-muted-foreground md:text-xl">
{description}
Temukan potensi programming Anda bersama komunitas yang
mendukung, tutorial interaktif, dan sumber daya berkualitas
tinggi.
</p>
</div>
<div className="flex flex-col sm:flex-row gap-4 w-full sm:w-auto">
<Button
size="lg"
className="w-full sm:w-auto bg-gradient-to-r from-primary to-blue-400 hover:from-primary/90 hover:to-blue-400/90 transition-all duration-300 font-bold text-black hover:text-white cursor-pointer"
onClick={() => window.open(buttons.primaryUrl, '_blank')}
className="group relative w-full sm:w-auto bg-gradient-to-r from-primary to-blue-600 hover:from-primary/90 hover:to-blue-600/90 transition-all duration-300 font-bold text-white hover:text-white/90 shadow-lg cursor-pointer"
onClick={() =>
window.open(
'https://facebook.com/groups/programmerhandal',
'_blank'
)
}
>
{buttons.primaryLabel}
Mulai Belajar
</Button>
<Button
size="lg"
variant="outline"
className="w-full sm:w-auto group relative overflow-hidden border-primary"
onClick={() => window.open(buttons.secondaryUrl, '_blank')}
onClick={() =>
window.open('https://discord.com/invite/imphnen', '_blank')
}
>
<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>
<span className="relative">Gabung Discord</span>
</Button>
</div>
<div className="flex flex-wrap justify-center gap-6 sm:gap-8">
{stats?.map(({ value, label }, i) => (
<div className="flex flex-wrap justify-center md:justify-start gap-6 sm:gap-8">
{HERO_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">
@@ -108,7 +101,7 @@ export function Hero(props: HeroSection) {
</div>
<div className="text-xs text-muted-foreground">{label}</div>
</div>
{i < stats.length - 1 && (
{i < HERO_STATS.length - 1 && (
<div className="hidden sm:block h-10 border-r border-border mx-4" />
)}
</Fragment>
@@ -122,30 +115,13 @@ export function Hero(props: HeroSection) {
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.5, delay: 0.2 }}
>
<div className="relative">
<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>
<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" />
{(() => {
const media = formatCMSImageDataToMedia(heroImage);
if (!media) return null;
return (
<Image
src={media.url!}
alt={media.alt}
width={600}
height={500}
className="w-full h-auto object-cover"
/>
);
})()}
</div>
</div>
<Image
src="/logo.webp"
alt="logo"
width={600}
height={500}
className="w-full h-auto object-cover"
/>
</motion.div>
</div>
</div>
@@ -1,14 +1,12 @@
'use client';
import LEARNING_RESOURCES from '@/data/learning-resources.json';
import { Button } from '@components';
import { Icon } from '@iconify/react';
import { Button } from '@imphnen-frontend-service/shadcn-ui/atoms';
import { LearningResourcesSection } from 'apps/landing/src/payload-types';
import { motion, useInView } from 'framer-motion';
import { useRef } from 'react';
export function LearningResources(props: LearningResourcesSection) {
const { resources, featured, title, description } = props;
export function LearningResources() {
const ref = useRef(null);
const isInView = useInView(ref, { once: true, amount: 0.2 });
@@ -49,10 +47,11 @@ export function LearningResources(props: LearningResourcesSection) {
transition={{ duration: 0.5 }}
>
<h2 className="text-3xl font-bold tracking-tighter md:text-4xl/tight lg:text-5xl">
{title}
Sumber Belajar
</h2>
<p className="max-w-[800px] mx-auto text-muted-foreground md:text-lg">
{description}
Akses berbagai materi belajar yang akan membantu kamu menguasai
konsep programming dengan cara yang menyenangkan.
</p>
</motion.div>
</div>
@@ -62,7 +61,7 @@ export function LearningResources(props: LearningResourcesSection) {
initial="hidden"
animate={isInView ? 'visible' : 'hidden'}
>
{resources.map((r, i) => (
{LEARNING_RESOURCES.map((r, i) => (
<motion.div
key={i}
className="group relative overflow-hidden rounded-xl border bg-background p-6 hover:shadow-lg"
@@ -101,30 +100,28 @@ export function LearningResources(props: LearningResourcesSection) {
<div className="grid md:grid-cols-2 gap-0">
<div className="p-8 md:p-12 flex flex-col justify-center">
<div className="inline-block rounded-full bg-primary/10 px-4 py-1.5 text-sm font-medium text-primary mb-4 w-fit">
{featured.label}
Rekomendasi Terbaik
</div>
<h3 className="text-2xl md:text-3xl font-bold mb-4">
{featured.title}
Kursus Lengkap Web Development
</h3>
<p className="text-muted-foreground mb-6">
{featured.description}
Pelajari HTML, CSS, JavaScript, React, dan Node.js dalam satu
kursus komprehensif yang dirancang untuk pemula hingga tingkat
menengah.
</p>
<div className="flex flex-wrap gap-4">
<Button
className="bg-gradient-to-r from-primary to-blue-400 hover:from-primary/90 hover:to-blue-400/90 font-bold text-black hover:text-white cursor-pointer"
onClick={() =>
window.open(featured.primaryButtonLink, '_blank')
}
className="group relative w-full sm:w-auto bg-gradient-to-r from-primary to-blue-600 hover:from-primary/90 hover:to-blue-600/90 transition-all duration-300 font-bold text-white hover:text-white/90 shadow-lg cursor-pointer"
onClick={() => window.open('/', '_blank')}
>
{featured.primaryButtonText}
Mulai Kursus
</Button>
<Button
variant="outline"
onClick={() =>
window.open(featured.secondaryButtonLink, '_blank')
}
onClick={() => window.open('/', '_blank')}
>
{featured.secondaryButtonText}
Lihat Silabus
</Button>
</div>
</div>
@@ -1,15 +1,13 @@
'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 TESTIMONIAL_STATS from '@/data/testimonial-stats.json';
import TESTIMONIALS from '@/data/testimonials.json';
import { QuoteIcon } from '@components';
import { motion, useInView } from 'framer-motion';
import Image from 'next/image';
import { useRef } from 'react';
export function Testimonials(props: TestimonialsSection) {
const { title, subtitle, items, stats, joinTitle, joinText } = props;
export function Testimonials() {
const ref = useRef<HTMLDivElement>(null);
const isInView = useInView(ref, { once: true, amount: 0.2 });
@@ -42,21 +40,13 @@ export function Testimonials(props: TestimonialsSection) {
transition={{ duration: 0.5 }}
>
<h2 className="text-3xl font-bold tracking-tighter md:text-4xl lg:text-5xl">
{title.split(' ').map((word, i) =>
i === title.split(' ').length - 1 ? (
<span
key={i}
className="bg-clip-text text-transparent bg-gradient-to-r from-primary to-blue-400"
>
{word}
</span>
) : (
<span key={i}>{word} </span>
)
)}
<span className="bg-clip-text text-transparent bg-gradient-to-r from-primary to-blue-400">
Testimoni
</span>
<span> Member</span>
</h2>
<p className="max-w-[800px] mx-auto text-muted-foreground md:text-lg">
{subtitle}
Apa kata mereka yang telah bergabung dengan komunitas IMPHNEN?
</p>
</motion.div>
@@ -66,7 +56,7 @@ export function Testimonials(props: TestimonialsSection) {
initial="hidden"
animate={isInView ? 'visible' : 'hidden'}
>
{items?.map((t, idx) => (
{TESTIMONIALS.map((t, idx) => (
<motion.div
key={idx}
className="group relative overflow-hidden rounded-xl border bg-background p-6 transition-all hover:shadow-lg"
@@ -81,19 +71,14 @@ export function Testimonials(props: TestimonialsSection) {
</p>
<div className="flex items-center gap-4">
<div className="relative h-12 w-12 overflow-hidden rounded-full border-2 border-primary/20">
{(() => {
const media = formatCMSImageDataToMedia(t.avatar);
if (!media) return null;
return (
<Image
src={media.url!}
alt={media.alt}
width={600}
height={500}
className="object-cover"
/>
);
})()}
<Image
src={t.avatar}
alt={t.name}
width={600}
height={500}
className="object-cover"
unoptimized
/>
</div>
<div>
<h4 className="font-bold">{t.name}</h4>
@@ -115,12 +100,16 @@ export function Testimonials(props: TestimonialsSection) {
<div className="grid md:grid-cols-2 gap-8 items-center">
<div>
<h3 className="text-2xl md:text-3xl font-bold mb-4">
{joinTitle}
Bergabunglah dengan 10,000+ programmer Indonesia lainnya
</h3>
<p className="text-muted-foreground">{joinText}</p>
<p className="text-muted-foreground">
Komunitas kami terus berkembang dengan programmer dari berbagai
latar belakang dan tingkat keahlian. Bersama-sama, kita belajar,
berbagi, dan tumbuh sebagai profesional.
</p>
</div>
<div className="grid grid-cols-2 gap-4">
{stats?.map((s, idx) => (
{TESTIMONIAL_STATS.map((s, idx) => (
<div key={idx} className="rounded-lg border p-4 text-center">
<div className="text-3xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-primary to-blue-400">
{s.value}
+19
View File
@@ -0,0 +1,19 @@
import { CallToAction } from './_components/call-to-action';
import { Community } from './_components/community';
import { Features } from './_components/features';
import { Hero } from './_components/hero';
import { LearningResources } from './_components/learning-resources';
import { Testimonials } from './_components/testimonials';
export default function Page() {
return (
<>
<Hero />
<Features />
<Community />
<LearningResources />
<Testimonials />
<CallToAction />
</>
);
}
@@ -1,27 +0,0 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import type { Metadata } from 'next';
import { NotFoundPage, generatePageMetadata } from '@payloadcms/next/views';
import config from '../../../../payload.config';
import { importMap } from '../importMap';
type Args = {
params: Promise<{
segments: string[];
}>;
searchParams: Promise<{
[key: string]: string | string[];
}>;
};
export const generateMetadata = ({
params,
searchParams,
}: Args): Promise<Metadata> =>
generatePageMetadata({ config, params, searchParams });
const NotFound = ({ params, searchParams }: Args) =>
NotFoundPage({ config, params, searchParams, importMap });
export default NotFound;
@@ -1,27 +0,0 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import type { Metadata } from 'next';
import { RootPage, generatePageMetadata } from '@payloadcms/next/views';
import config from '../../../../payload.config';
import { importMap } from '../importMap';
type Args = {
params: Promise<{
segments: string[];
}>;
searchParams: Promise<{
[key: string]: string | string[];
}>;
};
export const generateMetadata = ({
params,
searchParams,
}: Args): Promise<Metadata> =>
generatePageMetadata({ config, params, searchParams });
const Page = ({ params, searchParams }: Args) =>
RootPage({ config, params, searchParams, importMap });
export default Page;
@@ -1,5 +0,0 @@
import { S3ClientUploadHandler as S3ClientUploadHandler_f97aa6c64367fa259c5bc0567239ef24 } from '@payloadcms/storage-s3/client'
export const importMap = {
"@payloadcms/storage-s3/client#S3ClientUploadHandler": S3ClientUploadHandler_f97aa6c64367fa259c5bc0567239ef24
}
@@ -1,19 +0,0 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import '@payloadcms/next/css';
import {
REST_DELETE,
REST_GET,
REST_OPTIONS,
REST_PATCH,
REST_POST,
REST_PUT,
} from '@payloadcms/next/routes';
import config from '../../../../payload.config';
export const GET = REST_GET(config);
export const POST = REST_POST(config);
export const DELETE = REST_DELETE(config);
export const PATCH = REST_PATCH(config);
export const PUT = REST_PUT(config);
export const OPTIONS = REST_OPTIONS(config);
@@ -1,7 +0,0 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import '@payloadcms/next/css';
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes';
import config from '../../../../payload.config';
export const GET = GRAPHQL_PLAYGROUND_GET(config);
@@ -1,8 +0,0 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { GRAPHQL_POST, REST_OPTIONS } from '@payloadcms/next/routes';
import config from '../../../../payload.config';
export const POST = GRAPHQL_POST(config);
export const OPTIONS = REST_OPTIONS(config);
-35
View File
@@ -1,35 +0,0 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import '@payloadcms/next/css';
import { handleServerFunctions, RootLayout } from '@payloadcms/next/layouts';
import type { ServerFunctionClient } from 'payload';
import React from 'react';
import config from '../../payload.config';
import { importMap } from './admin/importMap.js';
import './custom.scss';
type Args = {
children: React.ReactNode;
};
const serverFunction: ServerFunctionClient = async function (args) {
'use server';
return handleServerFunctions({
...args,
config,
importMap,
});
};
const Layout = ({ children }: Args) => (
<RootLayout
config={config}
importMap={importMap}
serverFunction={serverFunction}
>
{children}
</RootLayout>
);
export default Layout;
@@ -188,8 +188,10 @@ export default function Footer() {
placeholder="Email Anda"
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
/>
<button className="inline-flex items-center justify-center rounded-md bg-primary px-3 py-1 text-sm shadow hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50
font-bold text-black hover:text-white cursor-pointer">
<button
className="inline-flex items-center justify-center rounded-md bg-primary px-3 py-1 text-sm shadow hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50
font-bold text-black hover:text-white cursor-pointer"
>
Daftar
</button>
</div>
@@ -1,17 +1,16 @@
'use client';
import {
Button,
MenuIcon,
XIcon,
} from '@imphnen-frontend-service/shadcn-ui/atoms';
import { cn } from '@imphnen-frontend-service/utils';
import { Button, MenuIcon, XIcon } from '@components';
import { cn } from '@utils';
import Image from 'next/image';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { SimpleThemeToggle } from './simple-theme-toggle';
export function Header() {
const router = useRouter();
const [isScrolled, setIsScrolled] = useState(false);
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
@@ -37,7 +36,7 @@ export function Header() {
<div className="relative overflow-hidden rounded">
<Link href="/">
<Image
src="/logo.png"
src="/logo.webp"
alt="IMPHNEN"
width={64}
height={64}
@@ -82,16 +81,14 @@ export function Header() {
</Link>
</nav>
<div className="flex items-center gap-4">
<div className="flex items-center gap-x-2">
<SimpleThemeToggle />
<Button
className="hidden md:flex bg-gradient-to-r from-primary to-blue-400 hover:from-primary/90 hover:to-blue-400/90 transition-all duration-300 font-bold text-black hover:text-white cursor-pointer"
onClick={() =>
window.open('https://discord.com/invite/imphnen', '_blank')
}
className="group relative w-full sm:w-auto bg-gradient-to-r from-primary to-blue-600 hover:from-primary/90 hover:to-blue-600/90 transition-all duration-300 font-bold text-white hover:text-white/90 shadow-lg cursor-pointer"
onClick={() => router.push('/signin')}
>
Gabung Discord
Login
</Button>
{/* Mobile Menu Button */}
@@ -142,15 +139,13 @@ export function Header() {
>
Testimoni
</Link>
<a
href="https://discord.com/invite/imphnen"
target="_blank"
className="mt-4"
<Button
onClick={() => router.push('/signin')}
className="group relative w-full sm:w-auto bg-gradient-to-r from-primary to-blue-600 hover:from-primary/90 hover:to-blue-600/90 transition-all duration-300 font-bold text-white hover:text-white/90 shadow-lg cursor-pointer"
>
<Button className="mt-4 bg-gradient-to-r from-primary to-blue-400 hover:from-primary/90 hover:to-blue-400/90 font-bold text-black hover:text-white cursor-pointer">
Gabung Discord
</Button>
</a>
Login
</Button>
</nav>
</div>
)}
@@ -1,10 +1,6 @@
'use client';
import {
Button,
MoonIcon,
SunIcon,
} from '@imphnen-frontend-service/shadcn-ui/atoms';
import { Button, MoonIcon, SunIcon } from '@components';
import { useTheme } from 'next-themes';
import { useEffect, useState } from 'react';
@@ -1,6 +1,6 @@
import '@/styles/globals.css';
import { type Metadata } from 'next';
import { Inter } from 'next/font/google';
import '../../styles/globals.css';
import Footer from './_components/footer';
import { Header } from './_components/header';
import { ThemeProvider } from './_components/theme-provider';
+3
View File
@@ -0,0 +1,3 @@
export default function Page() {
return <></>;
}