Compare commits
17
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0bc7d084d5 | ||
|
|
795bcb6ab1 | ||
|
|
2de5fd56d8 | ||
|
|
d078999eec | ||
|
|
a8dd867e1a | ||
|
|
ff65424ff9 | ||
|
|
2fc625e454 | ||
|
|
d1bc9e10d7 | ||
|
|
017b5309a5 | ||
|
|
e5844327a5 | ||
|
|
7874800d5e | ||
|
|
32d482ba4c | ||
|
|
502e45b4ff | ||
|
|
7411fd2a13 | ||
|
|
9df5bf8bbf | ||
|
|
9548272340 | ||
|
|
d1abc3c7d4 |
Binary file not shown.
|
Before Width: | Height: | Size: 3.2 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 75 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 88 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 8.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
@@ -0,0 +1,5 @@
|
|||||||
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
|
export default function Layout({ children }: { children: ReactNode }) {
|
||||||
|
return <>{children}</>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default function Page() {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default function Page() {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Button } from '@imphnen-frontend-service/shadcn-ui/atoms';
|
import { CallToActionSection } from '@/payload-types';
|
||||||
import { CallToActionSection } from 'apps/landing/src/payload-types';
|
import { Button } from '@components/atoms';
|
||||||
import { motion, useInView } from 'framer-motion';
|
import { motion, useInView } from 'framer-motion';
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
|
|
||||||
+3
-3
@@ -1,8 +1,8 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { CommunitiesSection } from '@/payload-types';
|
||||||
|
import { Button } from '@components/atoms';
|
||||||
import { Icon } from '@iconify/react';
|
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 { motion, useInView } from 'framer-motion';
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ export function Community(props: CommunitiesSection) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
id="komunitas"
|
id="community"
|
||||||
className="w-full py-20 md:py-32 bg-muted relative overflow-hidden"
|
className="w-full py-20 md:py-32 bg-muted relative overflow-hidden"
|
||||||
>
|
>
|
||||||
<div className="absolute inset-0 -z-10">
|
<div className="absolute inset-0 -z-10">
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { FeaturesSection } from '@/payload-types';
|
||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
import { FeaturesSection } from 'apps/landing/src/payload-types';
|
|
||||||
import { motion, useInView } from 'framer-motion';
|
import { motion, useInView } from 'framer-motion';
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
|
|
||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { LearningResourcesSection } from '@/payload-types';
|
||||||
|
import { Button } from '@components/atoms';
|
||||||
import { Icon } from '@iconify/react';
|
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 { motion, useInView } from 'framer-motion';
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
|
|
||||||
+3
-3
@@ -1,8 +1,8 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { QuoteIcon } from '@imphnen-frontend-service/shadcn-ui/atoms';
|
import { formatCMSImageDataToMedia } from '@/lib/format';
|
||||||
import { formatCMSImageDataToMedia } from 'apps/landing/src/lib/format';
|
import { TestimonialsSection } from '@/payload-types';
|
||||||
import { TestimonialsSection } from 'apps/landing/src/payload-types';
|
import { QuoteIcon } from '@components/atoms';
|
||||||
import { motion, useInView } from 'framer-motion';
|
import { motion, useInView } from 'framer-motion';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { Button } from '@components/atoms';
|
||||||
|
import { motion, useInView } from 'framer-motion';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { useRef } from 'react';
|
||||||
|
|
||||||
|
export function CallToAction() {
|
||||||
|
const ref = useRef(null);
|
||||||
|
const isInView = useInView(ref, { once: true, amount: 0.2 });
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section 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 to-primary/20" />
|
||||||
|
<div className="absolute inset-0 bg-[radial-gradient(circle_at_center,rgba(59,130,246,0.2),transparent_70%)]" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="container px-4 md:px-6" ref={ref}>
|
||||||
|
<motion.div
|
||||||
|
className="max-w-4xl mx-auto rounded-2xl overflow-hidden border shadow-lg"
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 20 }}
|
||||||
|
transition={{ duration: 0.5 }}
|
||||||
|
>
|
||||||
|
<div className="relative p-8 md:p-12 lg:p-16 bg-background">
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-br from-primary/5 via-transparent to-blue-400/5" />
|
||||||
|
|
||||||
|
<div className="relative z-10 text-center">
|
||||||
|
<h2 className="text-3xl md:text-4xl lg:text-5xl font-bold mb-6">
|
||||||
|
Siap Menjadi{' '}
|
||||||
|
<span className="bg-clip-text text-transparent bg-gradient-to-r from-primary to-blue-400">
|
||||||
|
Programmer Handal?
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
<p className="text-lg text-muted-foreground mb-8 max-w-2xl mx-auto">
|
||||||
|
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="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"
|
||||||
|
onClick={() => router.push('#community')}
|
||||||
|
>
|
||||||
|
Gabung Komunitas
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="lg"
|
||||||
|
variant="outline"
|
||||||
|
className="border-primary hover:bg-primary/10"
|
||||||
|
onClick={() => router.push('/events')}
|
||||||
|
>
|
||||||
|
Explore Event
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Decorative Elements */}
|
||||||
|
<div className="absolute -top-12 -left-12 w-24 h-24 rounded-full bg-primary/10 blur-2xl" />
|
||||||
|
<div className="absolute -bottom-12 -right-12 w-24 h-24 rounded-full bg-blue-400/10 blur-2xl" />
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,247 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { Button } from '@components/atoms';
|
||||||
|
import { motion, useInView } from 'framer-motion';
|
||||||
|
import { useRef } from 'react';
|
||||||
|
import {
|
||||||
|
FaDiscord,
|
||||||
|
FaFacebook,
|
||||||
|
FaInstagram,
|
||||||
|
FaLinkedin,
|
||||||
|
FaTiktok,
|
||||||
|
} from 'react-icons/fa';
|
||||||
|
|
||||||
|
export function Communities() {
|
||||||
|
const ref = useRef(null);
|
||||||
|
const isInView = useInView(ref, { once: true, amount: 0.2 });
|
||||||
|
|
||||||
|
const communities = [
|
||||||
|
{
|
||||||
|
icon: FaFacebook,
|
||||||
|
title: 'Facebook Group',
|
||||||
|
description:
|
||||||
|
'Komunitas aktif dengan 180K+ anggota berdiskusi seputar programming dan sharing meme',
|
||||||
|
buttonText: 'Gabung Sekarang',
|
||||||
|
buttonLink: 'https://facebook.com/groups/programmerhandal',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: FaDiscord,
|
||||||
|
title: 'Discord Server',
|
||||||
|
description:
|
||||||
|
'Diskusi real-time dengan developer berbagai level, mulai dari pemula sampai expert!',
|
||||||
|
buttonText: 'Join Server',
|
||||||
|
buttonLink: 'https://discord.com/invite/imphnen',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: FaInstagram,
|
||||||
|
title: 'Instagram',
|
||||||
|
description:
|
||||||
|
'Temukan visual tutorial coding & tech trends terkini setiap harinya',
|
||||||
|
buttonText: 'Follow Kami',
|
||||||
|
buttonLink: 'https://www.instagram.com/imphnen.dev',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: FaTiktok,
|
||||||
|
title: 'TikTok',
|
||||||
|
description:
|
||||||
|
'Tips coding singkat & trik development praktis dalam 60 detik',
|
||||||
|
buttonText: 'Follow Sekarang',
|
||||||
|
buttonLink: 'https://www.tiktok.com/@imphnen',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: FaLinkedin,
|
||||||
|
title: 'LinkedIn',
|
||||||
|
description:
|
||||||
|
'Bangun jaringan profesional dengan perusahaan teknologi ternama & recruiter IT',
|
||||||
|
buttonText: 'Segera Hadir',
|
||||||
|
buttonLink: '#',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const stats = [
|
||||||
|
{ value: '180K+', label: 'Komunitas Aktif' },
|
||||||
|
{ value: '25K+', label: 'Postingan/Bulan' },
|
||||||
|
{ value: '95%', label: 'Respon Cepat' },
|
||||||
|
{ value: '10K+', label: 'Problem Solved' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const containerVariants = {
|
||||||
|
hidden: { opacity: 0 },
|
||||||
|
visible: {
|
||||||
|
opacity: 1,
|
||||||
|
transition: {
|
||||||
|
staggerChildren: 0.15,
|
||||||
|
delayChildren: 0.2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const cardVariants = {
|
||||||
|
hidden: { y: 40, opacity: 0, scale: 0.95 },
|
||||||
|
visible: {
|
||||||
|
y: 0,
|
||||||
|
opacity: 1,
|
||||||
|
scale: 1,
|
||||||
|
transition: {
|
||||||
|
type: 'spring',
|
||||||
|
stiffness: 100,
|
||||||
|
damping: 20,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const statVariants = {
|
||||||
|
hidden: { opacity: 0, y: 20 },
|
||||||
|
visible: (i: number) => ({
|
||||||
|
opacity: 1,
|
||||||
|
y: 0,
|
||||||
|
transition: {
|
||||||
|
delay: i * 0.1 + 0.5,
|
||||||
|
duration: 0.6,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
id="community"
|
||||||
|
className="w-full py-20 md:py-32 relative overflow-hidden container"
|
||||||
|
>
|
||||||
|
<div className="absolute inset-0 -z-10">
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-b from-background via-background to-background" />
|
||||||
|
<div className="absolute top-0 right-0 w-1/2 h-1/2 bg-gradient-to-bl from-primary/5 to-transparent blur-3xl" />
|
||||||
|
<div className="absolute bottom-0 left-0 w-1/2 h-1/2 bg-gradient-to-tr from-primary/5 to-transparent blur-3xl" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full px-4 md:px-6 lg:px-0" ref={ref}>
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||||
|
transition={{ duration: 0.8 }}
|
||||||
|
>
|
||||||
|
<h2 className="text-4xl font-bold tracking-tighter md:text-5xl/tight lg:text-6xl text-center mb-6">
|
||||||
|
<span className="bg-clip-text text-transparent bg-gradient-to-r from-primary to-primary/80">
|
||||||
|
Komunitas Programmer
|
||||||
|
</span>{' '}
|
||||||
|
<br className="hidden md:block" />
|
||||||
|
Terbesar di Indonesia
|
||||||
|
</h2>
|
||||||
|
<p className="max-w-4xl mx-auto text-muted-foreground text-center md:text-xl text-balance">
|
||||||
|
Bergabung dengan jaringan developer profesional untuk berkolaborasi,
|
||||||
|
belajar, dan berkembang bersama komunitas yang aktif dan suportif
|
||||||
|
</p>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
{/* First Row - Facebook & Discord */}
|
||||||
|
<motion.div
|
||||||
|
className="grid gap-4 grid-cols-1 md:grid-cols-2 w-full mt-16"
|
||||||
|
variants={containerVariants}
|
||||||
|
initial="hidden"
|
||||||
|
animate={isInView ? 'visible' : 'hidden'}
|
||||||
|
>
|
||||||
|
{communities.slice(0, 2).map((community, index) => (
|
||||||
|
<motion.div
|
||||||
|
key={index}
|
||||||
|
variants={cardVariants}
|
||||||
|
className="group relative overflow-hidden rounded-2xl border bg-background p-8 hover:shadow-xl transition-shadow h-full"
|
||||||
|
whileHover={{
|
||||||
|
y: -5,
|
||||||
|
transition: { duration: 0.2 },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="absolute top-0 left-0 h-1.5 w-full bg-gradient-to-r from-primary to-primary/50 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||||
|
|
||||||
|
<div className="relative z-10">
|
||||||
|
<div className="mb-6 inline-flex h-14 w-14 items-center justify-center rounded-xl bg-primary/10">
|
||||||
|
<community.icon className="h-7 w-7 text-primary" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 className="mb-4 text-2xl font-bold">{community.title}</h3>
|
||||||
|
<p className="mb-8 text-muted-foreground text-lg">
|
||||||
|
{community.description}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="w-full group-hover:bg-primary/10 group-hover:border-primary/20 group-hover:text-foreground transition-all duration-300 hover:scale-[1.02] border-primary/10 text-base h-12"
|
||||||
|
onClick={() => window.open(community.buttonLink, '_blank')}
|
||||||
|
>
|
||||||
|
{community.buttonText}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="absolute -bottom-40 -right-40 w-80 h-80 bg-gradient-to-tl from-primary/10 to-transparent rounded-full opacity-0 group-hover:opacity-100 transition-all duration-500 group-hover:-translate-y-20 group-hover:-translate-x-20" />
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
{/* Second Row - Instagram, TikTok, LinkedIn */}
|
||||||
|
<motion.div
|
||||||
|
className="grid gap-4 grid-cols-1 md:grid-cols-3 w-full mt-8"
|
||||||
|
variants={containerVariants}
|
||||||
|
initial="hidden"
|
||||||
|
animate={isInView ? 'visible' : 'hidden'}
|
||||||
|
>
|
||||||
|
{communities.slice(2).map((community, index) => (
|
||||||
|
<motion.div
|
||||||
|
key={index + 2}
|
||||||
|
variants={cardVariants}
|
||||||
|
className="group relative overflow-hidden rounded-2xl border bg-background p-6 hover:shadow-xl transition-shadow h-full"
|
||||||
|
whileHover={{
|
||||||
|
y: -5,
|
||||||
|
transition: { duration: 0.2 },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="absolute top-0 left-0 h-1.5 w-full bg-gradient-to-r from-primary/50 to-primary/30 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||||
|
|
||||||
|
<div className="relative z-10">
|
||||||
|
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-xl bg-primary/10">
|
||||||
|
<community.icon className="h-6 w-6 text-primary" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 className="mb-3 text-xl font-bold">{community.title}</h3>
|
||||||
|
<p className="mb-6 text-muted-foreground">
|
||||||
|
{community.description}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="w-full group-hover:bg-primary/10 group-hover:border-primary/20 group-hover:text-foreground transition-all duration-300 hover:scale-[1.02] border-primary/10 h-11"
|
||||||
|
onClick={() => window.open(community.buttonLink, '_blank')}
|
||||||
|
disabled={community.title === 'LinkedIn'}
|
||||||
|
>
|
||||||
|
{community.buttonText}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="absolute -bottom-32 -right-32 w-64 h-64 bg-gradient-to-tl from-primary/10 to-transparent rounded-full opacity-0 group-hover:opacity-100 transition-all duration-500 group-hover:-translate-y-10 group-hover:-translate-x-10" />
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
{/* Stats Section */}
|
||||||
|
<motion.div
|
||||||
|
className="mt-20 grid grid-cols-2 md:grid-cols-4 gap-8 text-center"
|
||||||
|
initial="hidden"
|
||||||
|
animate={isInView ? 'visible' : 'hidden'}
|
||||||
|
>
|
||||||
|
{stats.map((stat, index) => (
|
||||||
|
<motion.div
|
||||||
|
key={index}
|
||||||
|
variants={statVariants}
|
||||||
|
custom={index}
|
||||||
|
className="space-y-3 p-6 rounded-xl bg-background border"
|
||||||
|
>
|
||||||
|
<div className="text-5xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-primary to-primary/80">
|
||||||
|
{stat.value}
|
||||||
|
</div>
|
||||||
|
<div className="text-sm font-medium text-muted-foreground uppercase tracking-wide">
|
||||||
|
{stat.label}
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { Button } from '@components/atoms';
|
||||||
|
import { motion, useInView } from 'framer-motion';
|
||||||
|
import { useRef } from 'react';
|
||||||
|
|
||||||
|
export function Events() {
|
||||||
|
const ref = useRef(null);
|
||||||
|
const isInView = useInView(ref, { once: true, amount: 0.2 });
|
||||||
|
|
||||||
|
const events = [
|
||||||
|
{
|
||||||
|
image: '/images/event-1.jpg',
|
||||||
|
title: 'Tech Summit 2024',
|
||||||
|
description:
|
||||||
|
'Konferensi teknologi terbesar tahun ini dengan pembicara expert dari perusahaan unicorn',
|
||||||
|
type: 'offline',
|
||||||
|
price: 'berbayar',
|
||||||
|
link: '#',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
image: '/images/event-2.jpg',
|
||||||
|
title: 'Web Development Bootcamp',
|
||||||
|
description:
|
||||||
|
'Pelatihan intensif full-stack development selama 2 minggu secara online',
|
||||||
|
type: 'online',
|
||||||
|
price: 'gratis',
|
||||||
|
link: '#',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
image: '/images/event-3.jpg',
|
||||||
|
title: 'UI/UX Workshop',
|
||||||
|
description:
|
||||||
|
'Workshop praktis membuat prototype aplikasi dengan Figma dan Framer',
|
||||||
|
type: 'hybrid',
|
||||||
|
price: 'berbayar',
|
||||||
|
link: '#',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const containerVariants = {
|
||||||
|
hidden: { opacity: 0 },
|
||||||
|
visible: {
|
||||||
|
opacity: 1,
|
||||||
|
transition: {
|
||||||
|
staggerChildren: 0.15,
|
||||||
|
delayChildren: 0.2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const cardVariants = {
|
||||||
|
hidden: { y: 40, opacity: 0, scale: 0.95 },
|
||||||
|
visible: {
|
||||||
|
y: 0,
|
||||||
|
opacity: 1,
|
||||||
|
scale: 1,
|
||||||
|
transition: {
|
||||||
|
type: 'spring',
|
||||||
|
stiffness: 100,
|
||||||
|
damping: 20,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
id="events"
|
||||||
|
className="w-full py-20 md:py-32 relative overflow-hidden container"
|
||||||
|
>
|
||||||
|
<div className="absolute inset-0 -z-10">
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-b from-background via-background to-background" />
|
||||||
|
<div className="absolute top-0 right-0 w-1/2 h-1/2 bg-gradient-to-bl from-primary/5 to-transparent blur-3xl" />
|
||||||
|
<div className="absolute bottom-0 left-0 w-1/2 h-1/2 bg-gradient-to-tr from-primary/5 to-transparent blur-3xl" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full px-4 md:px-6 lg:px-0" ref={ref}>
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||||
|
transition={{ duration: 0.8 }}
|
||||||
|
className="text-center mb-16"
|
||||||
|
>
|
||||||
|
<h2 className="text-4xl font-bold tracking-tighter md:text-5xl/tight lg:text-6xl mb-6">
|
||||||
|
<span className="bg-clip-text text-transparent bg-gradient-to-r from-primary to-primary/80">
|
||||||
|
Event Terbaru
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
<p className="max-w-2xl mx-auto text-muted-foreground text-center md:text-xl text-balance">
|
||||||
|
Lihat dan ikuti event terbaru dari IMPHNEN
|
||||||
|
</p>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<motion.div
|
||||||
|
className="grid gap-8 grid-cols-1 md:grid-cols-2 lg:grid-cols-3 w-full"
|
||||||
|
variants={containerVariants}
|
||||||
|
initial="hidden"
|
||||||
|
animate={isInView ? 'visible' : 'hidden'}
|
||||||
|
>
|
||||||
|
{events.map((event, index) => (
|
||||||
|
<motion.div
|
||||||
|
key={index}
|
||||||
|
variants={cardVariants}
|
||||||
|
className="group relative overflow-hidden rounded-2xl border bg-background hover:shadow-xl transition-shadow h-full"
|
||||||
|
whileHover={{
|
||||||
|
y: -5,
|
||||||
|
transition: { duration: 0.2 },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="absolute top-0 left-0 h-1.5 w-full bg-gradient-to-r from-primary to-primary/50 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||||
|
|
||||||
|
<div className="relative z-10">
|
||||||
|
<div className="h-48 bg-muted/20 relative overflow-hidden">
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-t from-background to-transparent z-10" />
|
||||||
|
<div className="absolute top-4 right-4 flex gap-2">
|
||||||
|
<span
|
||||||
|
className={`px-3 py-1 rounded-full text-sm font-medium ${
|
||||||
|
event.type === 'online'
|
||||||
|
? 'bg-green-500/10 text-green-500'
|
||||||
|
: event.type === 'offline'
|
||||||
|
? 'bg-purple-500/10 text-purple-500'
|
||||||
|
: 'bg-yellow-500/10 text-yellow-500'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{event.type === 'hybrid'
|
||||||
|
? 'Hybrid'
|
||||||
|
: event.type.toUpperCase()}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className={`px-3 py-1 rounded-full text-sm font-medium ${
|
||||||
|
event.price === 'gratis'
|
||||||
|
? 'bg-blue-500/10 text-blue-500'
|
||||||
|
: 'bg-orange-500/10 text-orange-500'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{event.price.toUpperCase()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-6">
|
||||||
|
<h3 className="text-2xl font-bold mb-3">{event.title}</h3>
|
||||||
|
<p className="text-muted-foreground mb-6">
|
||||||
|
{event.description}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="w-full group-hover:bg-primary/10 group-hover:border-primary/20 group-hover:text-foreground transition-all duration-300 hover:scale-[1.02] border-primary/10 text-base h-12"
|
||||||
|
onClick={() => window.open(event.link, '_blank')}
|
||||||
|
>
|
||||||
|
Lihat Detail
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="absolute -bottom-40 -right-40 w-80 h-80 bg-gradient-to-tl from-primary/10 to-transparent rounded-full opacity-0 group-hover:opacity-100 transition-all duration-500 group-hover:-translate-y-20 group-hover:-translate-x-20" />
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { Button } from '@components/atoms';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { TbCalendarStar } from 'react-icons/tb';
|
||||||
|
|
||||||
|
export function ButtonExploreEvent() {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
size="lg"
|
||||||
|
variant="outline"
|
||||||
|
className="w-full sm:w-auto group relative overflow-hidden border-primary flex items-center justify-center gap-2"
|
||||||
|
onClick={() => router.push('/events')}
|
||||||
|
>
|
||||||
|
<TbCalendarStar className="size-7" />
|
||||||
|
<span className="relative">Explore Event</span>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { Button } from '@components/atoms';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { TbLocationFilled } from 'react-icons/tb';
|
||||||
|
|
||||||
|
export function ButtonJoinCommunity() {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="default"
|
||||||
|
size="lg"
|
||||||
|
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"
|
||||||
|
onClick={() => router.push('#community')}
|
||||||
|
>
|
||||||
|
<TbLocationFilled className="size-6" />
|
||||||
|
Gabung Komunitas
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export function HeroBadge() {
|
||||||
|
return (
|
||||||
|
<div className="inline-flex items-center rounded-full border px-3 py-1 text-sm w-fit truncate">
|
||||||
|
<span>🇮🇩 Komunitas Programmer Indonesia</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
import Image from 'next/image';
|
||||||
|
|
||||||
|
export function HeroImage() {
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
className="relative w-full lg:w-auto mx-auto lg:ml-auto max-w-[600px] mt-8 lg:mt-0"
|
||||||
|
initial={{ opacity: 0, scale: 0.9 }}
|
||||||
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
|
transition={{ duration: 0.5, delay: 0.2 }}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src="/logo.png"
|
||||||
|
alt=""
|
||||||
|
width={800}
|
||||||
|
height={600}
|
||||||
|
className="w-full h-auto object-cover"
|
||||||
|
priority
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
export function HeroMainText() {
|
||||||
|
return (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<h1 className="text-4xl md:text-5xl lg:text-6xl font-bold tracking-tighter bg-clip-text text-transparent bg-gradient-to-r from-foreground via-foreground to-foreground/70 leading-tight">
|
||||||
|
Programmer Handal <br />
|
||||||
|
<span className="bg-clip-text text-transparent bg-gradient-to-r from-primary to-blue-400">
|
||||||
|
Namun Enggan Ngoding
|
||||||
|
</span>
|
||||||
|
</h1>
|
||||||
|
<p className="max-w-[600px] text-muted-foreground md:text-xl lg:text-lg">
|
||||||
|
Tempat para programmer struggle bersama, berbagi meme, pengalaman,
|
||||||
|
tutorial, dan tempat yapping yang nyaman
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { Avatar, AvatarFallback, AvatarImage } from '@components/atoms';
|
||||||
|
|
||||||
|
export function HeroStat() {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center rounded-full border border-border bg-background p-1 shadow shadow-black/5 md:w-fit">
|
||||||
|
<div className="flex -space-x-4">
|
||||||
|
<Avatar>
|
||||||
|
<AvatarImage src="/maulana.webp" />
|
||||||
|
<AvatarFallback />
|
||||||
|
</Avatar>
|
||||||
|
<Avatar>
|
||||||
|
<AvatarImage src="/rasyid.webp" />
|
||||||
|
<AvatarFallback />
|
||||||
|
</Avatar>
|
||||||
|
<Avatar>
|
||||||
|
<AvatarImage src="/ega.webp" />
|
||||||
|
<AvatarFallback />
|
||||||
|
</Avatar>
|
||||||
|
</div>
|
||||||
|
<p className="px-2 text-xs text-muted-foreground">
|
||||||
|
<strong className="font-medium text-foreground">180K+</strong>{' '}
|
||||||
|
Programmer Indonesia telah bergabung
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { ReactNode, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export function HeroWrapper({ children }: { children: ReactNode }) {
|
||||||
|
const [scrollY, setScrollY] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleScroll = () => {
|
||||||
|
setScrollY(window.scrollY);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('scroll', handleScroll);
|
||||||
|
return () => window.removeEventListener('scroll', handleScroll);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="relative w-full py-16 md:py-24 lg:py-32 xl:py-40 overflow-hidden">
|
||||||
|
<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" />
|
||||||
|
<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={{
|
||||||
|
transform: `translate(${scrollY * 0.1}px, ${scrollY * -0.05}px)`,
|
||||||
|
opacity: Math.max(0.2, 1 - scrollY * 0.0005),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className="absolute bottom-1/3 -right-20 w-80 h-80 rounded-full bg-gradient-to-r from-blue-400/20 to-primary/20 blur-3xl"
|
||||||
|
style={{
|
||||||
|
transform: `translate(${scrollY * -0.1}px, ${scrollY * 0.05}px)`,
|
||||||
|
opacity: Math.max(0.2, 1 - scrollY * 0.0005),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<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>
|
||||||
|
{children}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
import { ButtonExploreEvent } from './button-explore-event';
|
||||||
|
import { ButtonJoinCommunity } from './button-join-community';
|
||||||
|
import { HeroBadge } from './hero-badge';
|
||||||
|
import { HeroImage } from './hero-image';
|
||||||
|
import { HeroMainText } from './hero-main-text';
|
||||||
|
import { HeroStat } from './hero-stat';
|
||||||
|
import { HeroWrapper } from './hero-wrapper';
|
||||||
|
|
||||||
|
export function Hero() {
|
||||||
|
return (
|
||||||
|
<HeroWrapper>
|
||||||
|
<div className="container px-4 md:px-8 relative">
|
||||||
|
<div className="grid gap-8 lg:grid-cols-2 lg:gap-16 items-center">
|
||||||
|
<motion.div
|
||||||
|
className="flex flex-col justify-center space-y-4"
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ duration: 0.5 }}
|
||||||
|
>
|
||||||
|
<HeroBadge />
|
||||||
|
<HeroMainText />
|
||||||
|
|
||||||
|
<div className="flex flex-col sm:flex-row gap-4 w-full sm:w-auto">
|
||||||
|
<ButtonJoinCommunity />
|
||||||
|
<ButtonExploreEvent />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<HeroStat />
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<HeroImage />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</HeroWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { Button } from '@components/atoms';
|
||||||
|
import { motion, useInView } from 'framer-motion';
|
||||||
|
import Image from 'next/image';
|
||||||
|
import { useRef } from 'react';
|
||||||
|
import { ImPencil2 } from 'react-icons/im';
|
||||||
|
|
||||||
|
const testimonials = [
|
||||||
|
{
|
||||||
|
author: {
|
||||||
|
name: 'Ega',
|
||||||
|
role: 'Backend Engineer',
|
||||||
|
avatar: '/ega.webp',
|
||||||
|
},
|
||||||
|
quote:
|
||||||
|
'Tralalero Tralala, IMPHENOTRUNASA merupakan anomali yang sering datang ketika melihat orang ngoding',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
author: {
|
||||||
|
name: 'Rasyid',
|
||||||
|
role: 'Fullsnack Engineer',
|
||||||
|
avatar: '/rasyid.webp',
|
||||||
|
},
|
||||||
|
quote:
|
||||||
|
'Dapet job dari meme yang di-share disini. Gak nyangka yapping random bisa jadi koneksi kerja akwokaowkoak',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
author: {
|
||||||
|
name: 'Maulana Sodiqin',
|
||||||
|
role: 'Backend Engineer',
|
||||||
|
avatar: '/maulana.webp',
|
||||||
|
},
|
||||||
|
quote: 'Anjay aku bohong? Admin yang bohong',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function Testimonials() {
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
const isInView = useInView(ref, { once: true, amount: 0.2 });
|
||||||
|
|
||||||
|
// Double the testimonials for seamless loop
|
||||||
|
const duplicatedTestimonials = [...testimonials, ...testimonials];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
id="testimoni"
|
||||||
|
className="w-full py-20 md:py-32 bg-muted relative overflow-hidden"
|
||||||
|
ref={ref}
|
||||||
|
>
|
||||||
|
<div className="absolute inset-0 -z-10">
|
||||||
|
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top,rgba(var(--primary)/0.1),transparent_50%)]" />
|
||||||
|
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_bottom,rgba(var(--primary)/0.1),transparent_50%)]" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="container px-4 md:px-6">
|
||||||
|
<motion.div
|
||||||
|
className="flex flex-col items-center justify-center space-y-4 text-center mb-16"
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||||
|
transition={{ duration: 0.5 }}
|
||||||
|
>
|
||||||
|
<h2 className="text-3xl font-bold tracking-tighter md:text-4xl lg:text-5xl">
|
||||||
|
<span className="bg-clip-text text-transparent bg-gradient-to-r from-primary to-blue-400">
|
||||||
|
Testimoni{' '}
|
||||||
|
</span>
|
||||||
|
Member
|
||||||
|
</h2>
|
||||||
|
<p className="max-w-[800px] mx-auto text-muted-foreground md:text-lg">
|
||||||
|
Apa kata mereka yang telah bergabung dengan komunitas IMPHNEN?
|
||||||
|
</p>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
{/* Infinite Marquee Section */}
|
||||||
|
<div className="relative w-full overflow-hidden py-4 marquee-container">
|
||||||
|
<motion.div
|
||||||
|
className="flex"
|
||||||
|
animate={{
|
||||||
|
x: ['0%', '-100%'],
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
duration: 40,
|
||||||
|
ease: 'linear',
|
||||||
|
repeat: Infinity,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{duplicatedTestimonials.map((testimonial, i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className="w-[300px] md:w-[400px] shrink-0 mx-4 p-6 rounded-xl bg-background border border-border/20 hover:border-primary/30 transition-colors group flex flex-col"
|
||||||
|
style={{ minHeight: '200px' }} // Set a fixed minimum height
|
||||||
|
>
|
||||||
|
<div className="flex flex-col gap-4 flex-grow">
|
||||||
|
<p className="text-muted-foreground italic leading-relaxed line-clamp-4">
|
||||||
|
“{testimonial.quote}”
|
||||||
|
</p>
|
||||||
|
<div className="mt-auto">
|
||||||
|
{' '}
|
||||||
|
{/* This pushes the author info to the bottom */}
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className="relative h-12 w-12 rounded-full overflow-hidden border-2 border-primary/20 group-hover:border-primary/50 transition-colors">
|
||||||
|
<Image
|
||||||
|
src={testimonial.author.avatar}
|
||||||
|
alt={testimonial.author.name}
|
||||||
|
width={48}
|
||||||
|
height={48}
|
||||||
|
className="object-cover"
|
||||||
|
unoptimized
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="font-bold">{testimonial.author.name}</h4>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
{testimonial.author.role}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Call to Action */}
|
||||||
|
<motion.div
|
||||||
|
className="mt-16 text-center"
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||||
|
transition={{ delay: 0.2 }}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
size="lg"
|
||||||
|
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"
|
||||||
|
>
|
||||||
|
<ImPencil2 className="size-5" />
|
||||||
|
Tulis dan Tampilkan Disini
|
||||||
|
</Button>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { CallToAction } from './_components/call-to-action';
|
||||||
|
import { Communities } from './_components/communites';
|
||||||
|
import { Events } from './_components/events';
|
||||||
|
import { Hero } from './_components/hero';
|
||||||
|
import { Testimonials } from './_components/testimonials';
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Hero />
|
||||||
|
<Events />
|
||||||
|
<Communities />
|
||||||
|
<Testimonials />
|
||||||
|
<CallToAction />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default function Page() {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default function Page() {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { ReactNode } from 'react';
|
||||||
|
import Footer from '../_components/footer';
|
||||||
|
import { Header } from '../_components/header';
|
||||||
|
|
||||||
|
export default function Layout({ children }: { children: ReactNode }) {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen flex-col">
|
||||||
|
<Header />
|
||||||
|
<main className="flex-1">{children}</main>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default function Page() {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
MenuIcon,
|
|
||||||
XIcon,
|
|
||||||
} from '@imphnen-frontend-service/shadcn-ui/atoms';
|
|
||||||
import { cn } from '@imphnen-frontend-service/utils';
|
|
||||||
import Image from 'next/image';
|
|
||||||
import Link from 'next/link';
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { SimpleThemeToggle } from './simple-theme-toggle';
|
|
||||||
|
|
||||||
export function Header() {
|
|
||||||
const [isScrolled, setIsScrolled] = useState(false);
|
|
||||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleScroll = () => {
|
|
||||||
setIsScrolled(window.scrollY > 10);
|
|
||||||
};
|
|
||||||
window.addEventListener('scroll', handleScroll);
|
|
||||||
return () => window.removeEventListener('scroll', handleScroll);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<header
|
|
||||||
className={cn(
|
|
||||||
'sticky top-0 z-50 w-full transition-all duration-300',
|
|
||||||
isScrolled
|
|
||||||
? 'bg-background/80 backdrop-blur-md border-b shadow-sm'
|
|
||||||
: 'bg-transparent'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className="container flex h-16 items-center justify-between">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<div className="relative overflow-hidden rounded">
|
|
||||||
<Link href="/">
|
|
||||||
<Image
|
|
||||||
src="/logo.png"
|
|
||||||
alt="IMPHNEN"
|
|
||||||
width={64}
|
|
||||||
height={64}
|
|
||||||
className="object-cover"
|
|
||||||
/>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Desktop Navigation */}
|
|
||||||
<nav className="hidden md:flex items-center gap-8">
|
|
||||||
<Link href="#fitur" className="text-sm font-medium relative group">
|
|
||||||
<span className="transition-colors hover:text-primary">Fitur</span>
|
|
||||||
<span className="absolute -bottom-1 left-0 w-0 h-0.5 bg-primary transition-all duration-300 group-hover:w-full"></span>
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
href="#komunitas"
|
|
||||||
className="text-sm font-medium relative group"
|
|
||||||
>
|
|
||||||
<span className="transition-colors hover:text-primary">
|
|
||||||
Komunitas
|
|
||||||
</span>
|
|
||||||
<span className="absolute -bottom-1 left-0 w-0 h-0.5 bg-primary transition-all duration-300 group-hover:w-full"></span>
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
href="#sumber-belajar"
|
|
||||||
className="text-sm font-medium relative group"
|
|
||||||
>
|
|
||||||
<span className="transition-colors hover:text-primary">
|
|
||||||
Sumber Belajar
|
|
||||||
</span>
|
|
||||||
<span className="absolute -bottom-1 left-0 w-0 h-0.5 bg-primary transition-all duration-300 group-hover:w-full"></span>
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
href="#testimoni"
|
|
||||||
className="text-sm font-medium relative group"
|
|
||||||
>
|
|
||||||
<span className="transition-colors hover:text-primary">
|
|
||||||
Testimoni
|
|
||||||
</span>
|
|
||||||
<span className="absolute -bottom-1 left-0 w-0 h-0.5 bg-primary transition-all duration-300 group-hover:w-full"></span>
|
|
||||||
</Link>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div className="flex items-center gap-4">
|
|
||||||
<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')
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Gabung Discord
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
{/* Mobile Menu Button */}
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
className="md:hidden"
|
|
||||||
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
|
||||||
>
|
|
||||||
{mobileMenuOpen ? (
|
|
||||||
<XIcon className="h-6 w-6" />
|
|
||||||
) : (
|
|
||||||
<MenuIcon className="h-6 w-6" />
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Mobile Menu */}
|
|
||||||
{mobileMenuOpen && (
|
|
||||||
<div className="md:hidden border-t bg-background/95 backdrop-blur-md">
|
|
||||||
<nav className="container flex flex-col py-4 text-center">
|
|
||||||
<Link
|
|
||||||
href="#fitur"
|
|
||||||
className="py-3 text-sm font-medium border-b border-border/50"
|
|
||||||
onClick={() => setMobileMenuOpen(false)}
|
|
||||||
>
|
|
||||||
Fitur
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
href="#komunitas"
|
|
||||||
className="py-3 text-sm font-medium border-b border-border/50"
|
|
||||||
onClick={() => setMobileMenuOpen(false)}
|
|
||||||
>
|
|
||||||
Komunitas
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
href="#sumber-belajar"
|
|
||||||
className="py-3 text-sm font-medium border-b border-border/50"
|
|
||||||
onClick={() => setMobileMenuOpen(false)}
|
|
||||||
>
|
|
||||||
Sumber Belajar
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
href="#testimoni"
|
|
||||||
className="py-3 text-sm font-medium"
|
|
||||||
onClick={() => setMobileMenuOpen(false)}
|
|
||||||
>
|
|
||||||
Testimoni
|
|
||||||
</Link>
|
|
||||||
<a
|
|
||||||
href="https://discord.com/invite/imphnen"
|
|
||||||
target="_blank"
|
|
||||||
className="mt-4"
|
|
||||||
>
|
|
||||||
<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>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</header>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { buttonVariants } from '@components/atoms';
|
||||||
|
import { cn } from '@utils/ui';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
export function ButtonSignin() {
|
||||||
|
return (
|
||||||
|
<Link href="/signin" className={cn(buttonVariants(), 'hidden lg:flex')}>
|
||||||
|
Masuk
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { buttonVariants } from '@components/atoms';
|
||||||
|
import { cn } from '@utils/ui';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
export function ButtonSignup() {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
href="/signup"
|
||||||
|
className={cn(buttonVariants({ variant: 'outline' }), 'hidden lg:flex')}
|
||||||
|
>
|
||||||
|
Daftar
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
export function DesktopNavigation() {
|
||||||
|
return (
|
||||||
|
<nav className="hidden md:flex items-center gap-8">
|
||||||
|
<Link href="/about" className="text-sm font-medium relative group">
|
||||||
|
<span className="transition-colors hover:text-primary">About</span>
|
||||||
|
<span className="absolute -bottom-1 left-0 w-0 h-0.5 bg-primary transition-all duration-300 group-hover:w-full"></span>
|
||||||
|
</Link>
|
||||||
|
<Link href="/events" className="text-sm font-medium relative group">
|
||||||
|
<span className="transition-colors hover:text-primary">Event</span>
|
||||||
|
<span className="absolute -bottom-1 left-0 w-0 h-0.5 bg-primary transition-all duration-300 group-hover:w-full"></span>
|
||||||
|
</Link>
|
||||||
|
<Link href="/testimonials" className="text-sm font-medium relative group">
|
||||||
|
<span className="transition-colors hover:text-primary">
|
||||||
|
Testimonial
|
||||||
|
</span>
|
||||||
|
<span className="absolute -bottom-1 left-0 w-0 h-0.5 bg-primary transition-all duration-300 group-hover:w-full"></span>
|
||||||
|
</Link>
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { cn } from '@utils/ui';
|
||||||
|
import { ReactNode, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export function HeaderWrapper({ children }: { children: ReactNode }) {
|
||||||
|
const [isScrolled, setIsScrolled] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleScroll = () => {
|
||||||
|
setIsScrolled(window.scrollY > 10);
|
||||||
|
};
|
||||||
|
window.addEventListener('scroll', handleScroll);
|
||||||
|
return () => window.removeEventListener('scroll', handleScroll);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<header
|
||||||
|
className={cn(
|
||||||
|
'sticky top-0 z-50 w-full transition-all duration-300',
|
||||||
|
isScrolled
|
||||||
|
? 'bg-background/80 backdrop-blur-md border-b shadow-sm'
|
||||||
|
: 'bg-transparent'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useMobileMenuStore } from '@/stores/mobile-menu-store';
|
||||||
|
import { ButtonSignin } from './button-signin';
|
||||||
|
import { ButtonSignup } from './button-signup';
|
||||||
|
import { DesktopNavigation } from './desktop-navigation';
|
||||||
|
import { HeaderWrapper } from './header-wrapper';
|
||||||
|
import { Logo } from './logo';
|
||||||
|
import { MobileMenuHamburger } from './mobile-menu-hamburger';
|
||||||
|
import { MobileNavigation } from './mobile-navigation';
|
||||||
|
import { SimpleThemeToggle } from './simple-theme-toggle';
|
||||||
|
|
||||||
|
export function Header() {
|
||||||
|
const mobileMenuOpen = useMobileMenuStore((s) => s.mobileMenuOpen);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<HeaderWrapper>
|
||||||
|
<div className="container flex h-16 items-center justify-between">
|
||||||
|
<Logo />
|
||||||
|
|
||||||
|
<DesktopNavigation />
|
||||||
|
|
||||||
|
<div className="flex items-center gap-x-2">
|
||||||
|
<ButtonSignin />
|
||||||
|
<ButtonSignup />
|
||||||
|
<SimpleThemeToggle />
|
||||||
|
<MobileMenuHamburger />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{mobileMenuOpen && <MobileNavigation />}
|
||||||
|
</HeaderWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import Image from 'next/image';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
export function Logo() {
|
||||||
|
return (
|
||||||
|
<Link href="/">
|
||||||
|
<Image
|
||||||
|
src="/logo.png"
|
||||||
|
alt="IMPHNEN"
|
||||||
|
width={64}
|
||||||
|
height={64}
|
||||||
|
className="object-cover"
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useMobileMenuStore } from '@/stores/mobile-menu-store';
|
||||||
|
import { Button, MenuIcon, XIcon } from '@components/atoms';
|
||||||
|
|
||||||
|
export function MobileMenuHamburger() {
|
||||||
|
const mobileMenuOpen = useMobileMenuStore((s) => s.mobileMenuOpen);
|
||||||
|
const toggleMobileMenu = useMobileMenuStore((s) => s.toggleMobileMenu);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="md:hidden"
|
||||||
|
onClick={toggleMobileMenu}
|
||||||
|
>
|
||||||
|
{mobileMenuOpen ? (
|
||||||
|
<XIcon className="h-6 w-6" />
|
||||||
|
) : (
|
||||||
|
<MenuIcon className="h-6 w-6" />
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
export function MobileNavigation() {
|
||||||
|
return (
|
||||||
|
<div className="md:hidden border-t bg-background/95 backdrop-blur-md">
|
||||||
|
<nav className="container flex flex-col py-4 text-center">
|
||||||
|
<Link
|
||||||
|
href="/about"
|
||||||
|
className="py-3 text-sm font-medium border-b border-border/50"
|
||||||
|
>
|
||||||
|
About
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/events"
|
||||||
|
className="py-3 text-sm font-medium border-b border-border/50"
|
||||||
|
>
|
||||||
|
Event
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/testimonias"
|
||||||
|
className="py-3 text-sm font-medium border-b border-border/50"
|
||||||
|
>
|
||||||
|
Testimonial
|
||||||
|
</Link>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
+1
-5
@@ -1,10 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import {
|
import { Button, MoonIcon, SunIcon } from '@components/atoms';
|
||||||
Button,
|
|
||||||
MoonIcon,
|
|
||||||
SunIcon,
|
|
||||||
} from '@imphnen-frontend-service/shadcn-ui/atoms';
|
|
||||||
import { useTheme } from 'next-themes';
|
import { useTheme } from 'next-themes';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
'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 { 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;
|
|
||||||
|
|
||||||
const [scrollY, setScrollY] = useState(0);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleScroll = () => {
|
|
||||||
setScrollY(window.scrollY);
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('scroll', handleScroll);
|
|
||||||
return () => window.removeEventListener('scroll', handleScroll);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section className="relative w-full py-20 md:py-32 lg:py-40 overflow-hidden">
|
|
||||||
<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" />
|
|
||||||
<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={{
|
|
||||||
transform: `translate(${scrollY * 0.1}px, ${scrollY * -0.05}px)`,
|
|
||||||
opacity: Math.max(0.2, 1 - scrollY * 0.001),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
className="absolute bottom-1/3 -right-20 w-80 h-80 rounded-full bg-gradient-to-r from-blue-400/20 to-primary/20 blur-3xl"
|
|
||||||
style={{
|
|
||||||
transform: `translate(${scrollY * -0.1}px, ${scrollY * 0.05}px)`,
|
|
||||||
opacity: Math.max(0.2, 1 - scrollY * 0.001),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<div className="container px-4 md:px-6 relative">
|
|
||||||
<div className="grid gap-6 lg:grid-cols-2 lg:gap-12 items-center">
|
|
||||||
<motion.div
|
|
||||||
className="flex flex-col justify-center space-y-8"
|
|
||||||
initial={{ opacity: 0, y: 20 }}
|
|
||||||
animate={{ opacity: 1, y: 0 }}
|
|
||||||
transition={{ duration: 0.5 }}
|
|
||||||
>
|
|
||||||
<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>
|
|
||||||
</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 />
|
|
||||||
<span className="bg-clip-text text-transparent bg-gradient-to-r from-primary to-blue-400">
|
|
||||||
{highlight}
|
|
||||||
</span>
|
|
||||||
</h1>
|
|
||||||
<p className="max-w-[600px] text-muted-foreground md:text-xl">
|
|
||||||
{description}
|
|
||||||
</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')}
|
|
||||||
>
|
|
||||||
{buttons.primaryLabel}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
size="lg"
|
|
||||||
variant="outline"
|
|
||||||
className="w-full sm:w-auto group relative overflow-hidden border-primary"
|
|
||||||
onClick={() => window.open(buttons.secondaryUrl, '_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>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-wrap justify-center gap-6 sm:gap-8">
|
|
||||||
{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="hidden sm:block h-10 border-r border-border mx-4" />
|
|
||||||
)}
|
|
||||||
</Fragment>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</motion.div>
|
|
||||||
|
|
||||||
<motion.div
|
|
||||||
className="relative w-full lg:w-auto mx-auto lg:ml-auto"
|
|
||||||
initial={{ opacity: 0, scale: 0.9 }}
|
|
||||||
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>
|
|
||||||
</motion.div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,12 @@
|
|||||||
|
import { poppins } from '@/lib/fonts';
|
||||||
|
import '@/styles/globals.css';
|
||||||
import { type Metadata } from 'next';
|
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';
|
import { ThemeProvider } from './_components/theme-provider';
|
||||||
|
|
||||||
const inter = Inter({ subsets: ['latin'] });
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: 'IMPHNEN - Ingin Menjadi Programmer Handal?',
|
title: 'IMPHNEN - Ingin Menjadi Programmer Handal Namung Enggan Ngonding',
|
||||||
description: 'Komunitas belajar programming untuk semua level',
|
description:
|
||||||
|
'Komunitas Ingin Menjadi Programmer Handal Namung Enggan Ngonding',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
@@ -19,18 +16,14 @@ export default function RootLayout({
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<html lang="id" suppressHydrationWarning>
|
<html lang="id" suppressHydrationWarning>
|
||||||
<body className={inter.className}>
|
<body className={poppins.className}>
|
||||||
<ThemeProvider
|
<ThemeProvider
|
||||||
attribute="class"
|
attribute="class"
|
||||||
defaultTheme="system"
|
defaultTheme="system"
|
||||||
enableSystem
|
enableSystem
|
||||||
disableTransitionOnChange
|
disableTransitionOnChange
|
||||||
>
|
>
|
||||||
<div className="flex min-h-screen flex-col">
|
{children}
|
||||||
<Header />
|
|
||||||
<main className="flex-1">{children}</main>
|
|
||||||
<Footer />
|
|
||||||
</div>
|
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -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} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
|
|
||||||
|
import config from '@/payload.config';
|
||||||
import { NotFoundPage, generatePageMetadata } from '@payloadcms/next/views';
|
import { NotFoundPage, generatePageMetadata } from '@payloadcms/next/views';
|
||||||
import config from '../../../../payload.config';
|
|
||||||
import { importMap } from '../importMap';
|
import { importMap } from '../importMap';
|
||||||
|
|
||||||
type Args = {
|
type Args = {
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
|
|
||||||
|
import config from '@/payload.config';
|
||||||
import { RootPage, generatePageMetadata } from '@payloadcms/next/views';
|
import { RootPage, generatePageMetadata } from '@payloadcms/next/views';
|
||||||
import config from '../../../../payload.config';
|
|
||||||
import { importMap } from '../importMap';
|
import { importMap } from '../importMap';
|
||||||
|
|
||||||
type Args = {
|
type Args = {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
||||||
|
import config from '@/payload.config';
|
||||||
import '@payloadcms/next/css';
|
import '@payloadcms/next/css';
|
||||||
import {
|
import {
|
||||||
REST_DELETE,
|
REST_DELETE,
|
||||||
@@ -9,7 +10,6 @@ import {
|
|||||||
REST_POST,
|
REST_POST,
|
||||||
REST_PUT,
|
REST_PUT,
|
||||||
} from '@payloadcms/next/routes';
|
} from '@payloadcms/next/routes';
|
||||||
import config from '../../../../payload.config';
|
|
||||||
|
|
||||||
export const GET = REST_GET(config);
|
export const GET = REST_GET(config);
|
||||||
export const POST = REST_POST(config);
|
export const POST = REST_POST(config);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
||||||
|
import config from '@/payload.config';
|
||||||
import '@payloadcms/next/css';
|
import '@payloadcms/next/css';
|
||||||
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes';
|
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes';
|
||||||
import config from '../../../../payload.config';
|
|
||||||
|
|
||||||
export const GET = GRAPHQL_PLAYGROUND_GET(config);
|
export const GET = GRAPHQL_PLAYGROUND_GET(config);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
||||||
|
import config from '@/payload.config';
|
||||||
import { GRAPHQL_POST, REST_OPTIONS } from '@payloadcms/next/routes';
|
import { GRAPHQL_POST, REST_OPTIONS } from '@payloadcms/next/routes';
|
||||||
import config from '../../../../payload.config';
|
|
||||||
|
|
||||||
export const POST = GRAPHQL_POST(config);
|
export const POST = GRAPHQL_POST(config);
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
||||||
|
import config from '@/payload.config';
|
||||||
import '@payloadcms/next/css';
|
import '@payloadcms/next/css';
|
||||||
import { handleServerFunctions, RootLayout } from '@payloadcms/next/layouts';
|
import { handleServerFunctions, RootLayout } from '@payloadcms/next/layouts';
|
||||||
import type { ServerFunctionClient } from 'payload';
|
import type { ServerFunctionClient } from 'payload';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import config from '../../payload.config';
|
|
||||||
|
|
||||||
import { importMap } from './admin/importMap.js';
|
import { importMap } from './admin/importMap.js';
|
||||||
import './custom.scss';
|
import './custom.scss';
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// fonts.ts
|
||||||
|
import { Poppins } from 'next/font/google';
|
||||||
|
|
||||||
|
export const poppins = Poppins({
|
||||||
|
subsets: ['latin'],
|
||||||
|
weight: ['300', '400', '500', '600', '700'],
|
||||||
|
display: 'swap',
|
||||||
|
});
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import type { paths } from '@/openapi-types';
|
||||||
|
import createClient from 'openapi-fetch';
|
||||||
|
|
||||||
|
export const fetcher = createClient<paths>({
|
||||||
|
baseUrl: 'https://api.imphnen.dev',
|
||||||
|
});
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
import { create } from 'zustand';
|
||||||
|
|
||||||
|
interface MobileMenuState {
|
||||||
|
mobileMenuOpen: boolean;
|
||||||
|
toggleMobileMenu: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useMobileMenuStore = create<MobileMenuState>((set) => ({
|
||||||
|
mobileMenuOpen: false,
|
||||||
|
toggleMobileMenu: () =>
|
||||||
|
set((state) => ({ mobileMenuOpen: !state.mobileMenuOpen })),
|
||||||
|
}));
|
||||||
@@ -1,3 +1,22 @@
|
|||||||
@import 'tailwindcss';
|
@import 'tailwindcss';
|
||||||
@import "../../../../libs/shadcn-ui/src/index.css";
|
@import '../../../../libs/shadcn-ui/src/index.css';
|
||||||
@source "../../../../libs/shadcn-ui/src/atoms/**/*.{ts,tsx}";
|
@source "../../../../libs/shadcn-ui/src/atoms/**/*.{ts,tsx}";
|
||||||
|
|
||||||
|
@layer utilities {
|
||||||
|
.marquee-container {
|
||||||
|
mask-image: linear-gradient(
|
||||||
|
to right,
|
||||||
|
transparent 0%,
|
||||||
|
black 5%,
|
||||||
|
black 95%,
|
||||||
|
transparent 100%
|
||||||
|
);
|
||||||
|
-webkit-mask-image: linear-gradient(
|
||||||
|
to right,
|
||||||
|
transparent 0%,
|
||||||
|
black 5%,
|
||||||
|
black 95%,
|
||||||
|
transparent 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,11 +11,7 @@
|
|||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"lib": [
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
"dom",
|
|
||||||
"dom.iterable",
|
|
||||||
"esnext"
|
|
||||||
],
|
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
@@ -24,7 +20,13 @@
|
|||||||
{
|
{
|
||||||
"name": "next"
|
"name": "next"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@components/atoms": ["../../libs/shadcn-ui/src/atoms/index.ts"],
|
||||||
|
"@utils/ui": ["../../libs/utils/src/index.ts"],
|
||||||
|
"@/*": ["src/*"]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"**/*.js",
|
"**/*.js",
|
||||||
@@ -36,10 +38,5 @@
|
|||||||
"next-env.d.ts",
|
"next-env.d.ts",
|
||||||
".next/types/**/*.ts"
|
".next/types/**/*.ts"
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": ["node_modules", "jest.config.ts", "**/*.spec.ts", "**/*.test.ts"]
|
||||||
"node_modules",
|
|
||||||
"jest.config.ts",
|
|
||||||
"**/*.spec.ts",
|
|
||||||
"**/*.test.ts"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+146
-3
@@ -8,6 +8,7 @@
|
|||||||
"name": "@imphnen-frontend-service/shadcn-ui",
|
"name": "@imphnen-frontend-service/shadcn-ui",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@radix-ui/react-avatar": "^1.1.9",
|
||||||
"@radix-ui/react-slot": "^1.2.0",
|
"@radix-ui/react-slot": "^1.2.0",
|
||||||
"@tailwindcss/vite": "^4.1.4",
|
"@tailwindcss/vite": "^4.1.4",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
@@ -446,6 +447,33 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-avatar": {
|
||||||
|
"version": "1.1.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.9.tgz",
|
||||||
|
"integrity": "sha512-10tQokfvZdFvnvDkcOJPjm2pWiP8A0R4T83MoD7tb15bC/k2GU7B1YBuzJi8lNQ8V1QqhP8ocNqp27ByZaNagQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-context": "1.1.2",
|
||||||
|
"@radix-ui/react-primitive": "2.1.2",
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.1.1",
|
||||||
|
"@radix-ui/react-use-is-hydrated": "0.1.0",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-compose-refs": {
|
"node_modules/@radix-ui/react-compose-refs": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
||||||
@@ -461,10 +489,48 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-context": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-primitive": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": "1.2.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-slot": {
|
"node_modules/@radix-ui/react-slot": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.2.tgz",
|
||||||
"integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==",
|
"integrity": "sha512-y7TBO4xN4Y94FvcWIOIh18fM4R1A8S4q1jhoz4PNzOoHsFcN8pogcFmZrTYAm4F9VRUrWP/Mw7xSKybIeRI+CQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-compose-refs": "1.1.2"
|
"@radix-ui/react-compose-refs": "1.1.2"
|
||||||
@@ -479,6 +545,54 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-use-callback-ref": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-use-is-hydrated": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.0.tgz",
|
||||||
|
"integrity": "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"use-sync-external-store": "^1.5.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-use-layout-effect": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||||
"version": "4.40.0",
|
"version": "4.40.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz",
|
||||||
@@ -1464,6 +1578,19 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-dom": {
|
||||||
|
"version": "19.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
|
||||||
|
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"scheduler": "^0.26.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^19.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-icons": {
|
"node_modules/react-icons": {
|
||||||
"version": "5.5.0",
|
"version": "5.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz",
|
||||||
@@ -1513,6 +1640,13 @@
|
|||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/scheduler": {
|
||||||
|
"version": "0.26.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
|
||||||
|
"integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
"node_modules/source-map-js": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
@@ -1575,6 +1709,15 @@
|
|||||||
"url": "https://github.com/sponsors/Wombosvideo"
|
"url": "https://github.com/sponsors/Wombosvideo"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/use-sync-external-store": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "6.3.3",
|
"version": "6.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.3.tgz",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@radix-ui/react-avatar": "^1.1.9",
|
||||||
"@radix-ui/react-slot": "^1.2.0",
|
"@radix-ui/react-slot": "^1.2.0",
|
||||||
"@tailwindcss/vite": "^4.1.4",
|
"@tailwindcss/vite": "^4.1.4",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { cn } from './className';
|
||||||
|
|
||||||
|
const Avatar = React.forwardRef<
|
||||||
|
React.ElementRef<typeof AvatarPrimitive.Root>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<AvatarPrimitive.Root
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
'relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
Avatar.displayName = AvatarPrimitive.Root.displayName;
|
||||||
|
|
||||||
|
const AvatarImage = React.forwardRef<
|
||||||
|
React.ElementRef<typeof AvatarPrimitive.Image>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<AvatarPrimitive.Image
|
||||||
|
ref={ref}
|
||||||
|
className={cn('aspect-square h-full w-full', className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
|
||||||
|
|
||||||
|
const AvatarFallback = React.forwardRef<
|
||||||
|
React.ElementRef<typeof AvatarPrimitive.Fallback>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<AvatarPrimitive.Fallback
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
'flex h-full w-full items-center justify-center rounded-[inherit] bg-secondary text-xs',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
|
||||||
|
|
||||||
|
export { Avatar, AvatarFallback, AvatarImage };
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { cn } from '@imphnen-frontend-service/utils';
|
|
||||||
import { Slot } from '@radix-ui/react-slot';
|
import { Slot } from '@radix-ui/react-slot';
|
||||||
import { cva, type VariantProps } from 'class-variance-authority';
|
import { cva, type VariantProps } from 'class-variance-authority';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { cn } from './className';
|
||||||
|
|
||||||
const buttonVariants = cva(
|
const buttonVariants = cva(
|
||||||
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { ClassValue, clsx } from 'clsx';
|
||||||
|
import { twMerge } from 'tailwind-merge';
|
||||||
|
|
||||||
|
export const cn = (...inputs: ClassValue[]) => {
|
||||||
|
return twMerge(clsx(inputs));
|
||||||
|
};
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
|
export * from './avatar';
|
||||||
export * from './button';
|
export * from './button';
|
||||||
export * from './icons';
|
export * from './icons';
|
||||||
|
|||||||
Generated
+165
-5
@@ -16,6 +16,7 @@
|
|||||||
"@payloadcms/next": "^3.37.0",
|
"@payloadcms/next": "^3.37.0",
|
||||||
"@payloadcms/richtext-lexical": "^3.37.0",
|
"@payloadcms/richtext-lexical": "^3.37.0",
|
||||||
"@payloadcms/storage-s3": "^3.37.0",
|
"@payloadcms/storage-s3": "^3.37.0",
|
||||||
|
"@radix-ui/react-avatar": "^1.1.9",
|
||||||
"@radix-ui/react-slot": "^1.2.0",
|
"@radix-ui/react-slot": "^1.2.0",
|
||||||
"@smithy/node-http-handler": "^4.0.4",
|
"@smithy/node-http-handler": "^4.0.4",
|
||||||
"@tanstack/react-query": "^5.74.4",
|
"@tanstack/react-query": "^5.74.4",
|
||||||
@@ -29,6 +30,7 @@
|
|||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"next": "~15.2.4",
|
"next": "~15.2.4",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
|
"openapi-fetch": "^0.14.0",
|
||||||
"payload": "^3.37.0",
|
"payload": "^3.37.0",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
@@ -38,7 +40,8 @@
|
|||||||
"sharp": "^0.34.1",
|
"sharp": "^0.34.1",
|
||||||
"sonner": "^2.0.3",
|
"sonner": "^2.0.3",
|
||||||
"tailwind-merge": "^3.0.2",
|
"tailwind-merge": "^3.0.2",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.24.2",
|
||||||
|
"zustand": "^5.0.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.14.5",
|
"@babel/core": "^7.14.5",
|
||||||
@@ -9954,6 +9957,33 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-avatar": {
|
||||||
|
"version": "1.1.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.9.tgz",
|
||||||
|
"integrity": "sha512-10tQokfvZdFvnvDkcOJPjm2pWiP8A0R4T83MoD7tb15bC/k2GU7B1YBuzJi8lNQ8V1QqhP8ocNqp27ByZaNagQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-context": "1.1.2",
|
||||||
|
"@radix-ui/react-primitive": "2.1.2",
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.1.1",
|
||||||
|
"@radix-ui/react-use-is-hydrated": "0.1.0",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-compose-refs": {
|
"node_modules/@radix-ui/react-compose-refs": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
||||||
@@ -9969,10 +9999,48 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-context": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-primitive": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": "1.2.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-slot": {
|
"node_modules/@radix-ui/react-slot": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.2.tgz",
|
||||||
"integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==",
|
"integrity": "sha512-y7TBO4xN4Y94FvcWIOIh18fM4R1A8S4q1jhoz4PNzOoHsFcN8pogcFmZrTYAm4F9VRUrWP/Mw7xSKybIeRI+CQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-compose-refs": "1.1.2"
|
"@radix-ui/react-compose-refs": "1.1.2"
|
||||||
@@ -9987,6 +10055,54 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-use-callback-ref": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-use-is-hydrated": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.0.tgz",
|
||||||
|
"integrity": "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"use-sync-external-store": "^1.5.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-use-layout-effect": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rc-component/util": {
|
"node_modules/@rc-component/util": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rc-component/util/-/util-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rc-component/util/-/util-1.2.1.tgz",
|
||||||
@@ -13972,7 +14088,7 @@
|
|||||||
"version": "19.1.2",
|
"version": "19.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.2.tgz",
|
||||||
"integrity": "sha512-XGJkWF41Qq305SKWEILa1O8vzhb3aOo3ogBlSmiqNko/WmRb6QIaweuZCXjKygVDXpzXb5wyxKTSOsmkuqj+Qw==",
|
"integrity": "sha512-XGJkWF41Qq305SKWEILa1O8vzhb3aOo3ogBlSmiqNko/WmRb6QIaweuZCXjKygVDXpzXb5wyxKTSOsmkuqj+Qw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^19.0.0"
|
"@types/react": "^19.0.0"
|
||||||
@@ -30932,6 +31048,21 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/openapi-fetch": {
|
||||||
|
"version": "0.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/openapi-fetch/-/openapi-fetch-0.14.0.tgz",
|
||||||
|
"integrity": "sha512-PshIdm1NgdLvb05zp8LqRQMNSKzIlPkyMxYFxwyHR+UlKD4t2nUjkDhNxeRbhRSEd3x5EUNh2w5sJYwkhOH4fg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"openapi-typescript-helpers": "^0.0.15"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/openapi-typescript-helpers": {
|
||||||
|
"version": "0.0.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/openapi-typescript-helpers/-/openapi-typescript-helpers-0.0.15.tgz",
|
||||||
|
"integrity": "sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/opener": {
|
"node_modules/opener": {
|
||||||
"version": "1.5.2",
|
"version": "1.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
|
||||||
@@ -40387,6 +40518,35 @@
|
|||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/zustand": {
|
||||||
|
"version": "5.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.4.tgz",
|
||||||
|
"integrity": "sha512-39VFTN5InDtMd28ZhjLyuTnlytDr9HfwO512Ai4I8ZABCoyAj4F1+sr7sD1jP/+p7k77Iko0Pb5NhgBFDCX0kQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.20.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": ">=18.0.0",
|
||||||
|
"immer": ">=9.0.6",
|
||||||
|
"react": ">=18.0.0",
|
||||||
|
"use-sync-external-store": ">=1.2.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"immer": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"use-sync-external-store": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/zwitch": {
|
"node_modules/zwitch": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
|
||||||
|
|||||||
+5
-1
@@ -18,6 +18,7 @@
|
|||||||
"landing:dev": "nx serve landing",
|
"landing:dev": "nx serve landing",
|
||||||
"landing:build": "nx build landing",
|
"landing:build": "nx build landing",
|
||||||
"landing:prod": "cd ./dist/apps/landing && npx next start",
|
"landing:prod": "cd ./dist/apps/landing && npx next start",
|
||||||
|
"landing:openapi": "npx openapi-typescript https://api.imphnen.dev/openapi.json -o ./apps/landing/src/openapi-types.ts",
|
||||||
"landing:generate:importmap": "PAYLOAD_CONFIG_PATH=apps/landing/src/payload.config.ts payload generate:importmap",
|
"landing:generate:importmap": "PAYLOAD_CONFIG_PATH=apps/landing/src/payload.config.ts payload generate:importmap",
|
||||||
"landing:generate:types": "PAYLOAD_CONFIG_PATH=apps/landing/src/payload.config.ts payload generate:types",
|
"landing:generate:types": "PAYLOAD_CONFIG_PATH=apps/landing/src/payload.config.ts payload generate:types",
|
||||||
"ui:test": "nx test ui",
|
"ui:test": "nx test ui",
|
||||||
@@ -33,6 +34,7 @@
|
|||||||
"@payloadcms/next": "^3.37.0",
|
"@payloadcms/next": "^3.37.0",
|
||||||
"@payloadcms/richtext-lexical": "^3.37.0",
|
"@payloadcms/richtext-lexical": "^3.37.0",
|
||||||
"@payloadcms/storage-s3": "^3.37.0",
|
"@payloadcms/storage-s3": "^3.37.0",
|
||||||
|
"@radix-ui/react-avatar": "^1.1.9",
|
||||||
"@radix-ui/react-slot": "^1.2.0",
|
"@radix-ui/react-slot": "^1.2.0",
|
||||||
"@smithy/node-http-handler": "^4.0.4",
|
"@smithy/node-http-handler": "^4.0.4",
|
||||||
"@tanstack/react-query": "^5.74.4",
|
"@tanstack/react-query": "^5.74.4",
|
||||||
@@ -46,6 +48,7 @@
|
|||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"next": "~15.2.4",
|
"next": "~15.2.4",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
|
"openapi-fetch": "^0.14.0",
|
||||||
"payload": "^3.37.0",
|
"payload": "^3.37.0",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
@@ -55,7 +58,8 @@
|
|||||||
"sharp": "^0.34.1",
|
"sharp": "^0.34.1",
|
||||||
"sonner": "^2.0.3",
|
"sonner": "^2.0.3",
|
||||||
"tailwind-merge": "^3.0.2",
|
"tailwind-merge": "^3.0.2",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.24.2",
|
||||||
|
"zustand": "^5.0.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.14.5",
|
"@babel/core": "^7.14.5",
|
||||||
|
|||||||
Reference in New Issue
Block a user