diff --git a/apps/landing/src/app/(frontend)/_components/header.tsx b/apps/landing/src/app/(frontend)/_components/header.tsx deleted file mode 100644 index b27daa0..0000000 --- a/apps/landing/src/app/(frontend)/_components/header.tsx +++ /dev/null @@ -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 ( -
-
-
-
- - IMPHNEN - -
-
- - {/* Desktop Navigation */} - - -
- - - - - {/* Mobile Menu Button */} - -
-
- - {/* Mobile Menu */} - {mobileMenuOpen && ( -
- -
- )} -
- ); -} diff --git a/apps/landing/src/app/(frontend)/_components/header/desktop-navigation.tsx b/apps/landing/src/app/(frontend)/_components/header/desktop-navigation.tsx new file mode 100644 index 0000000..ed79e27 --- /dev/null +++ b/apps/landing/src/app/(frontend)/_components/header/desktop-navigation.tsx @@ -0,0 +1,22 @@ +import Link from 'next/link'; + +export function DesktopNavigation() { + return ( + + ); +} diff --git a/apps/landing/src/app/(frontend)/_components/header/header-wrapper.tsx b/apps/landing/src/app/(frontend)/_components/header/header-wrapper.tsx new file mode 100644 index 0000000..b6b0016 --- /dev/null +++ b/apps/landing/src/app/(frontend)/_components/header/header-wrapper.tsx @@ -0,0 +1,29 @@ +'use client'; + +import { cn } from '@imphnen-frontend-service/utils'; +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 ( +
+ {children} +
+ ); +} diff --git a/apps/landing/src/app/(frontend)/_components/header/index.tsx b/apps/landing/src/app/(frontend)/_components/header/index.tsx new file mode 100644 index 0000000..701026e --- /dev/null +++ b/apps/landing/src/app/(frontend)/_components/header/index.tsx @@ -0,0 +1,30 @@ +'use client'; + +import { useMobileMenuStore } from '../../../../stores/mobile-menu-store'; +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 ( + +
+ + + + +
+ + +
+
+ + {mobileMenuOpen && } +
+ ); +} diff --git a/apps/landing/src/app/(frontend)/_components/header/logo.tsx b/apps/landing/src/app/(frontend)/_components/header/logo.tsx new file mode 100644 index 0000000..3aeee27 --- /dev/null +++ b/apps/landing/src/app/(frontend)/_components/header/logo.tsx @@ -0,0 +1,16 @@ +import Image from 'next/image'; +import Link from 'next/link'; + +export function Logo() { + return ( + + IMPHNEN + + ); +} diff --git a/apps/landing/src/app/(frontend)/_components/header/mobile-menu-hamburger.tsx b/apps/landing/src/app/(frontend)/_components/header/mobile-menu-hamburger.tsx new file mode 100644 index 0000000..acee8a0 --- /dev/null +++ b/apps/landing/src/app/(frontend)/_components/header/mobile-menu-hamburger.tsx @@ -0,0 +1,28 @@ +'use client'; + +import { + Button, + MenuIcon, + XIcon, +} from '@imphnen-frontend-service/shadcn-ui/atoms'; +import { useMobileMenuStore } from '../../../../stores/mobile-menu-store'; + +export function MobileMenuHamburger() { + const mobileMenuOpen = useMobileMenuStore((s) => s.mobileMenuOpen); + const toggleMobileMenu = useMobileMenuStore((s) => s.toggleMobileMenu); + + return ( + + ); +} diff --git a/apps/landing/src/app/(frontend)/_components/header/mobile-navigation.tsx b/apps/landing/src/app/(frontend)/_components/header/mobile-navigation.tsx new file mode 100644 index 0000000..e9e0a1d --- /dev/null +++ b/apps/landing/src/app/(frontend)/_components/header/mobile-navigation.tsx @@ -0,0 +1,28 @@ +import Link from 'next/link'; + +export function MobileNavigation() { + return ( +
+ +
+ ); +} diff --git a/apps/landing/src/app/(frontend)/_components/simple-theme-toggle.tsx b/apps/landing/src/app/(frontend)/_components/header/simple-theme-toggle.tsx similarity index 100% rename from apps/landing/src/app/(frontend)/_components/simple-theme-toggle.tsx rename to apps/landing/src/app/(frontend)/_components/header/simple-theme-toggle.tsx diff --git a/apps/landing/src/app/(frontend)/layout.tsx b/apps/landing/src/app/(frontend)/layout.tsx index 8cbe8a6..5356b87 100644 --- a/apps/landing/src/app/(frontend)/layout.tsx +++ b/apps/landing/src/app/(frontend)/layout.tsx @@ -8,8 +8,9 @@ import { ThemeProvider } from './_components/theme-provider'; const inter = Inter({ subsets: ['latin'] }); export const metadata: Metadata = { - title: 'IMPHNEN - Ingin Menjadi Programmer Handal?', - description: 'Komunitas belajar programming untuk semua level', + title: 'IMPHNEN - Ingin Menjadi Programmer Handal Namung Enggan Ngonding', + description: + 'Komunitas Ingin Menjadi Programmer Handal Namung Enggan Ngonding', }; export default function RootLayout({ diff --git a/apps/landing/src/app/(frontend)/page.tsx b/apps/landing/src/app/(frontend)/page.tsx index e50e601..470e7ad 100644 --- a/apps/landing/src/app/(frontend)/page.tsx +++ b/apps/landing/src/app/(frontend)/page.tsx @@ -1,41 +1,27 @@ 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(), - ]); + const [heroData, communitiesData, testimonialsData, callToActionData] = + await Promise.all([ + getGlobalsHeroSection(), + getGlobalsCommunitiesSection(), + getGlobalsTestimonialsSection(), + getGlobalsCallToActionSection(), + ]); return ( <> - - diff --git a/apps/landing/src/stores/mobile-menu-store.ts b/apps/landing/src/stores/mobile-menu-store.ts new file mode 100644 index 0000000..7c39b1f --- /dev/null +++ b/apps/landing/src/stores/mobile-menu-store.ts @@ -0,0 +1,12 @@ +import { create } from 'zustand'; + +interface MobileMenuState { + mobileMenuOpen: boolean; + toggleMobileMenu: () => void; +} + +export const useMobileMenuStore = create((set) => ({ + mobileMenuOpen: false, + toggleMobileMenu: () => + set((state) => ({ mobileMenuOpen: !state.mobileMenuOpen })), +})); diff --git a/package-lock.json b/package-lock.json index c5d83b2..8699fd4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,8 @@ "sharp": "^0.34.1", "sonner": "^2.0.3", "tailwind-merge": "^3.0.2", - "zod": "^3.24.2" + "zod": "^3.24.2", + "zustand": "^5.0.4" }, "devDependencies": { "@babel/core": "^7.14.5", @@ -40387,6 +40388,35 @@ "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": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/package.json b/package.json index 6069068..010156d 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,8 @@ "sharp": "^0.34.1", "sonner": "^2.0.3", "tailwind-merge": "^3.0.2", - "zod": "^3.24.2" + "zod": "^3.24.2", + "zustand": "^5.0.4" }, "devDependencies": { "@babel/core": "^7.14.5",