feat: add communites component and integrate social links from JSON data
This commit is contained in:
@@ -0,0 +1,154 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import SOCIALS from '@/data/socials.json';
|
||||||
|
import { motion, useInView } from 'framer-motion';
|
||||||
|
import { useRef } from 'react';
|
||||||
|
import {
|
||||||
|
FaArrowRight,
|
||||||
|
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 containerVariants = {
|
||||||
|
hidden: { opacity: 0 },
|
||||||
|
visible: {
|
||||||
|
opacity: 1,
|
||||||
|
transition: {
|
||||||
|
staggerChildren: 0.1,
|
||||||
|
delayChildren: 0.2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const itemVariants = {
|
||||||
|
hidden: { y: 20, opacity: 0 },
|
||||||
|
visible: {
|
||||||
|
y: 0,
|
||||||
|
opacity: 1,
|
||||||
|
transition: {
|
||||||
|
duration: 0.4,
|
||||||
|
ease: [0.25, 0.46, 0.45, 0.94],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const getIconComponent = (iconName: string) => {
|
||||||
|
switch (iconName) {
|
||||||
|
case 'FaFacebook':
|
||||||
|
return FaFacebook;
|
||||||
|
case 'FaDiscord':
|
||||||
|
return FaDiscord;
|
||||||
|
case 'FaInstagram':
|
||||||
|
return FaInstagram;
|
||||||
|
case 'FaTiktok':
|
||||||
|
return FaTiktok;
|
||||||
|
case 'FaLinkedin':
|
||||||
|
return FaLinkedin;
|
||||||
|
default:
|
||||||
|
return FaArrowRight;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPlatformColors = (iconName: string) => {
|
||||||
|
switch (iconName) {
|
||||||
|
case 'FaFacebook':
|
||||||
|
return {
|
||||||
|
iconColor: 'text-[#1877F2]',
|
||||||
|
};
|
||||||
|
case 'FaDiscord':
|
||||||
|
return {
|
||||||
|
iconColor: 'text-[#5865F2]',
|
||||||
|
};
|
||||||
|
case 'FaInstagram':
|
||||||
|
return {
|
||||||
|
iconColor: 'text-[#E4405F]',
|
||||||
|
};
|
||||||
|
case 'FaTiktok':
|
||||||
|
return {
|
||||||
|
iconColor: 'text-[#000000]',
|
||||||
|
};
|
||||||
|
case 'FaLinkedin':
|
||||||
|
return {
|
||||||
|
iconColor: 'text-[#0A66C2]',
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return {
|
||||||
|
iconColor: 'text-primary-500',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section id="komunitas" className="w-full py-20 md:py-28 bg-gray-50">
|
||||||
|
<div className="container" ref={ref}>
|
||||||
|
<div className="flex flex-col items-center justify-center space-y-4 text-center mb-16">
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||||
|
transition={{ duration: 0.4 }}
|
||||||
|
>
|
||||||
|
<h2 className="text-3xl font-semibold tracking-tight md:text-4xl">
|
||||||
|
Bergabung dengan Komunitas Kami di
|
||||||
|
<span className="block mt-2 text-primary-500">
|
||||||
|
Berbagai Platform
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
<p className="max-w-[600px] mx-auto text-gray-600 md:text-lg/relaxed mt-4">
|
||||||
|
Terhubung dengan sesama developer di komunitas kami
|
||||||
|
</p>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<motion.div
|
||||||
|
className="grid gap-6 md:gap-8 grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
|
||||||
|
variants={containerVariants}
|
||||||
|
initial="hidden"
|
||||||
|
animate={isInView ? 'visible' : 'hidden'}
|
||||||
|
>
|
||||||
|
{SOCIALS.map((community, index) => {
|
||||||
|
const IconComponent = getIconComponent(community.icon);
|
||||||
|
const colors = getPlatformColors(community.icon);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
key={index}
|
||||||
|
variants={itemVariants}
|
||||||
|
className="group relative p-8 bg-white border border-gray-200 rounded-xl hover:border-gray-300 transition-all duration-300"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col items-start gap-5">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<IconComponent
|
||||||
|
className={`w-8 h-8 ${colors.iconColor} transition-colors`}
|
||||||
|
/>
|
||||||
|
<h3 className="text-xl font-semibold text-gray-900">
|
||||||
|
{community.name}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-600 text-sm/relaxed">
|
||||||
|
{community.description}
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
href={community.link}
|
||||||
|
className={`inline-flex items-center gap-2 mt-2 text-sm font-medium transition-colors`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<span>Jelajahi Komunitas</span>
|
||||||
|
<FaArrowRight className="w-4 h-4 transition-transform duration-300 group-hover:translate-x-1" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -60,7 +60,7 @@ export function Hero() {
|
|||||||
|
|
||||||
<div className="space-y-4">
|
<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">
|
<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">
|
||||||
{headingLine1} <br />
|
{headingLine1}{' '}
|
||||||
<span className="bg-clip-text bg-gradient-to-r text-primary-500">
|
<span className="bg-clip-text bg-gradient-to-r text-primary-500">
|
||||||
{headingLine2}
|
{headingLine2}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
import { Communities } from './_components/communities';
|
||||||
import { Hero } from './_components/hero';
|
import { Hero } from './_components/hero';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Hero />
|
<Hero />
|
||||||
|
<Communities />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,20 +2,20 @@
|
|||||||
|
|
||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
import type { ThemeProviderProps } from 'next-themes';
|
import type { ThemeProviderProps } from 'next-themes';
|
||||||
import { ThemeProvider as NextThemesProvider } from 'next-themes';
|
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
// import { ThemeProvider as NextThemesProvider } from 'next-themes';
|
||||||
|
|
||||||
export function Providers({ children }: ThemeProviderProps) {
|
export function Providers({ children }: ThemeProviderProps) {
|
||||||
return (
|
return (
|
||||||
<QueryProvider>
|
<QueryProvider>
|
||||||
<ThemeProvider
|
{/* <ThemeProvider
|
||||||
attribute="class"
|
attribute="class"
|
||||||
defaultTheme="light"
|
defaultTheme="light"
|
||||||
enableSystem
|
enableSystem
|
||||||
disableTransitionOnChange
|
disableTransitionOnChange
|
||||||
>
|
> */}
|
||||||
{children}
|
{children}
|
||||||
</ThemeProvider>
|
{/* </ThemeProvider> */}
|
||||||
</QueryProvider>
|
</QueryProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -28,6 +28,6 @@ function QueryProvider({ children }: { children: ReactNode }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ThemeProvider({ children, ...props }: ThemeProviderProps) {
|
// function ThemeProvider({ children, ...props }: ThemeProviderProps) {
|
||||||
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
|
// return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Facebook Group",
|
||||||
|
"icon": "FaFacebook",
|
||||||
|
"color": "#1877F2",
|
||||||
|
"description": "Join discussions and share knowledge",
|
||||||
|
"link": "#"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Facebook Group II",
|
||||||
|
"icon": "FaFacebook",
|
||||||
|
"color": "#1877F2",
|
||||||
|
"description": "Join discussions and share knowledge",
|
||||||
|
"link": "#"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Discord Server",
|
||||||
|
"icon": "FaDiscord",
|
||||||
|
"color": "#5865F2",
|
||||||
|
"description": "Real-time collaboration chat",
|
||||||
|
"link": "#"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Instagram",
|
||||||
|
"icon": "FaInstagram",
|
||||||
|
"color": "#E4405F",
|
||||||
|
"description": "Daily coding tips & showcases",
|
||||||
|
"link": "#"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TikTok",
|
||||||
|
"icon": "FaTiktok",
|
||||||
|
"color": "#000000",
|
||||||
|
"description": "Short coding tutorials",
|
||||||
|
"link": "#"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LinkedIn",
|
||||||
|
"icon": "FaLinkedin",
|
||||||
|
"color": "#0A66C2",
|
||||||
|
"description": "Professional networking",
|
||||||
|
"link": "#"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -140,7 +140,7 @@
|
|||||||
--sidebar-ring: oklch(0.623 0.214 259.815);
|
--sidebar-ring: oklch(0.623 0.214 259.815);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
/* .dark {
|
||||||
--background: oklch(0.141 0.005 285.823);
|
--background: oklch(0.141 0.005 285.823);
|
||||||
--foreground: oklch(0.985 0 0);
|
--foreground: oklch(0.985 0 0);
|
||||||
--card: oklch(0.21 0.006 285.885);
|
--card: oklch(0.21 0.006 285.885);
|
||||||
@@ -172,7 +172,7 @@
|
|||||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||||
--sidebar-border: oklch(1 0 0 / 10%);
|
--sidebar-border: oklch(1 0 0 / 10%);
|
||||||
--sidebar-ring: oklch(0.488 0.243 264.376);
|
--sidebar-ring: oklch(0.488 0.243 264.376);
|
||||||
}
|
} */
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
* {
|
* {
|
||||||
@@ -191,6 +191,7 @@
|
|||||||
padding-right: 2rem;
|
padding-right: 2rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1536px) {
|
@media (min-width: 1536px) {
|
||||||
.container {
|
.container {
|
||||||
max-width: 1400px;
|
max-width: 1400px;
|
||||||
|
|||||||
Reference in New Issue
Block a user