feat: add communites component and integrate social links from JSON data

This commit is contained in:
arraysid
2025-05-27 06:12:18 +07:00
parent 3b71a97d16
commit 5f97352031
6 changed files with 212 additions and 11 deletions
@@ -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>;
} // }
+44
View File
@@ -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": "#"
}
]
+3 -2
View File
@@ -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;