diff --git a/package-lock.json b/package-lock.json
index 93ccab3..9c38d94 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,7 @@
"name": "nibmcs.org",
"version": "0.1.0",
"dependencies": {
+ "@emailjs/browser": "^3.11.0",
"@nextui-org/react": "^2.1.13",
"@sanity/image-url": "^1.0.2",
"@sanity/vision": "^3.16.7",
@@ -16,8 +17,11 @@
"framer-motion": "^10.16.4",
"next": "^13.5.6",
"next-sanity": "^5.5.4",
+ "next-themes": "^0.2.1",
"react": "latest",
"react-dom": "latest",
+ "react-icons": "^4.11.0",
+ "react-toastify": "^9.1.3",
"sanity": "^3.16.7",
"styled-components": "5.2"
},
@@ -744,6 +748,14 @@
"react": ">=16.8.0"
}
},
+ "node_modules/@emailjs/browser": {
+ "version": "3.11.0",
+ "resolved": "https://registry.npmjs.org/@emailjs/browser/-/browser-3.11.0.tgz",
+ "integrity": "sha512-RkY3FKZ3fPdK++OeF46mRTFpmmQWCHUVHZH209P3NE4D5sg2Atg7S2wa3gw5062Gl4clt4Wn5SyC4WhlVZC5pA==",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
"node_modules/@emotion/is-prop-valid": {
"version": "0.8.8",
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
@@ -10200,6 +10212,16 @@
"styled-components": "^5.2.0 || ^6.0.0"
}
},
+ "node_modules/next-themes": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.2.1.tgz",
+ "integrity": "sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==",
+ "peerDependencies": {
+ "next": "*",
+ "react": "*",
+ "react-dom": "*"
+ }
+ },
"node_modules/node-abi": {
"version": "3.47.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.47.0.tgz",
@@ -11406,6 +11428,14 @@
}
}
},
+ "node_modules/react-icons": {
+ "version": "4.11.0",
+ "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.11.0.tgz",
+ "integrity": "sha512-V+4khzYcE5EBk/BvcuYRq6V/osf11ODUM2J8hg2FDSswRrGvqiYUYPRy4OdrWaQOBj4NcpJfmHZLNaD+VH0TyA==",
+ "peerDependencies": {
+ "react": "*"
+ }
+ },
"node_modules/react-is": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
@@ -11542,6 +11572,18 @@
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
+ "node_modules/react-toastify": {
+ "version": "9.1.3",
+ "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz",
+ "integrity": "sha512-fPfb8ghtn/XMxw3LkxQBk3IyagNpF/LIKjOBflbexr2AWxAH1MJgvnESwEwBn9liLFXgTKWgBSdZpw9m4OTHTg==",
+ "dependencies": {
+ "clsx": "^1.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16",
+ "react-dom": ">=16"
+ }
+ },
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
diff --git a/package.json b/package.json
index 9bb0f52..ba1e3cd 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,7 @@
"lint": "next lint"
},
"dependencies": {
+ "@emailjs/browser": "^3.11.0",
"@nextui-org/react": "^2.1.13",
"@sanity/image-url": "^1.0.2",
"@sanity/vision": "^3.16.7",
@@ -17,8 +18,11 @@
"framer-motion": "^10.16.4",
"next": "^13.5.6",
"next-sanity": "^5.5.4",
+ "next-themes": "^0.2.1",
"react": "latest",
"react-dom": "latest",
+ "react-icons": "^4.11.0",
+ "react-toastify": "^9.1.3",
"sanity": "^3.16.7",
"styled-components": "5.2"
},
diff --git a/public/NCS-logo-color.png b/public/NCS-logo-color.png
new file mode 100644
index 0000000..328f1bd
Binary files /dev/null and b/public/NCS-logo-color.png differ
diff --git a/public/logo.png b/public/logo.png
new file mode 100644
index 0000000..a104f02
Binary files /dev/null and b/public/logo.png differ
diff --git a/src/app/about/layout.tsx b/src/app/about/layout.tsx
new file mode 100644
index 0000000..d5f6271
--- /dev/null
+++ b/src/app/about/layout.tsx
@@ -0,0 +1,13 @@
+export default function AboutLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+
+ );
+}
diff --git a/src/app/about/page.tsx b/src/app/about/page.tsx
new file mode 100644
index 0000000..9e4b155
--- /dev/null
+++ b/src/app/about/page.tsx
@@ -0,0 +1,7 @@
+export default function AboutPage() {
+ return (
+
+
About
+
+ );
+}
diff --git a/src/app/blog/layout.tsx b/src/app/blog/layout.tsx
new file mode 100644
index 0000000..45deef1
--- /dev/null
+++ b/src/app/blog/layout.tsx
@@ -0,0 +1,13 @@
+export default function BlogLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+
+ );
+}
diff --git a/src/app/blog/page.tsx b/src/app/blog/page.tsx
new file mode 100644
index 0000000..9990b69
--- /dev/null
+++ b/src/app/blog/page.tsx
@@ -0,0 +1,7 @@
+export default function BlogPage() {
+ return (
+
+
Blog
+
+ );
+}
diff --git a/src/app/contact/layout.tsx b/src/app/contact/layout.tsx
new file mode 100644
index 0000000..c3c01b7
--- /dev/null
+++ b/src/app/contact/layout.tsx
@@ -0,0 +1,13 @@
+export default function ContactLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+
+ );
+}
diff --git a/src/app/contact/page.tsx b/src/app/contact/page.tsx
new file mode 100644
index 0000000..6c5cb02
--- /dev/null
+++ b/src/app/contact/page.tsx
@@ -0,0 +1,156 @@
+'use client';
+
+import React, { useRef } from 'react';
+import { motion } from 'framer-motion';
+import { BsArrowRight } from 'react-icons/bs';
+import {
+ HiOutlineMapPin,
+ HiOutlineEnvelope,
+ HiOutlinePhone,
+} from 'react-icons/hi2';
+import { ToastContainer, toast } from 'react-toastify';
+import { fadeIn } from '../../utils/variants';
+import emailjs from '@emailjs/browser';
+import 'react-toastify/dist/ReactToastify.css';
+import Map from '../../components/map';
+import Box from '../../components/box';
+
+export default function ContactPage() {
+ const ref: any = useRef();
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+
+ const formData = new FormData(ref.current);
+ const requiredFields = ['user_name', 'user_email', 'subject', 'message'];
+ let isFormValid = true;
+
+ requiredFields.forEach((field) => {
+ const value = formData.get(field);
+ if (!value || '') {
+ toast.error(`Please fill in ${field} field.`, {
+ theme: 'dark',
+ });
+ isFormValid = false;
+ }
+ });
+
+ if (!isFormValid) {
+ return;
+ }
+
+ emailjs
+ .sendForm(
+ 'service_ap4wbtw',
+ 'template_grf4xdq',
+ ref.current,
+ 'dStWJXq6rdaw4xHWs'
+ )
+ .then(
+ () => {
+ toast.success('Message sent successfully!', {
+ theme: 'dark',
+ });
+
+ ref.current.reset();
+ },
+ () => {
+ toast.error('Something went wrong!', {
+ theme: 'dark',
+ });
+ }
+ );
+ };
+
+ return (
+ <>
+
+
+
+
+
+
+ }
+ name={'Address'}
+ para={'120/5 Vidya Mawatha, Colombo 00700'}
+ />
+ }
+ name={'Email'}
+ para={'info@nibmcs.org'}
+ />
+ } name={'Phone'} para={'+94712691003'} />
+
+
+ >
+ );
+}
diff --git a/src/app/events/layout.tsx b/src/app/events/layout.tsx
new file mode 100644
index 0000000..e119668
--- /dev/null
+++ b/src/app/events/layout.tsx
@@ -0,0 +1,13 @@
+export default function EventsLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+
+ );
+}
diff --git a/src/app/events/page.tsx b/src/app/events/page.tsx
new file mode 100644
index 0000000..da87c79
--- /dev/null
+++ b/src/app/events/page.tsx
@@ -0,0 +1,7 @@
+export default function EventsPage() {
+ return (
+
+
Events
+
+ );
+}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 8c106d8..049bee4 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,12 +1,25 @@
-import "./globals.css";
-import type { Metadata } from "next";
-import { Providers } from "./providers";
-import { Analytics } from "@vercel/analytics/react";
+import './globals.css';
+import type { Metadata } from 'next';
+import { Providers } from './providers';
+import { siteConfig } from '@/config/site';
+import clsx from 'clsx';
+import { fontSans } from '@/config/fonts';
+import { Navbar } from '@/components/navbar';
+import Footer from '@/components/Footer';
+import Social from '@/components/social';
export const metadata: Metadata = {
- title: "NIBM Computing Society",
- description:
- "We are a student community organization for the NIBM - Sri Lanka, established with the aim of enabling us to learn, share, and build our professions.",
+ title: siteConfig.name,
+ description: siteConfig.description,
+ themeColor: [
+ { media: '(prefers-color-scheme: light)', color: 'white' },
+ { media: '(prefers-color-scheme: dark)', color: 'black' },
+ ],
+ icons: {
+ icon: '/favicon.ico',
+ shortcut: '/favicon-16x16.png',
+ apple: '/apple-touch-icon.png',
+ },
};
export default function RootLayout({
@@ -15,10 +28,23 @@ export default function RootLayout({
children: React.ReactNode;
}) {
return (
-
-
- {children}
-
+
+
+
+
+
+
+
+ {children}
+
+
+
+
);
diff --git a/src/app/leaderboard/layout.tsx b/src/app/leaderboard/layout.tsx
new file mode 100644
index 0000000..953bc91
--- /dev/null
+++ b/src/app/leaderboard/layout.tsx
@@ -0,0 +1,13 @@
+export default function LeaderBoardLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+
+ );
+}
diff --git a/src/app/leaderboard/page.tsx b/src/app/leaderboard/page.tsx
new file mode 100644
index 0000000..c7bbde0
--- /dev/null
+++ b/src/app/leaderboard/page.tsx
@@ -0,0 +1,7 @@
+export default function LeaderBoardPage() {
+ return (
+
+
Leaderboard
+
+ );
+}
diff --git a/src/app/membership/layout.tsx b/src/app/membership/layout.tsx
new file mode 100644
index 0000000..465fb25
--- /dev/null
+++ b/src/app/membership/layout.tsx
@@ -0,0 +1,13 @@
+export default function MembershipLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+
+ );
+}
diff --git a/src/app/membership/page.tsx b/src/app/membership/page.tsx
new file mode 100644
index 0000000..22c803d
--- /dev/null
+++ b/src/app/membership/page.tsx
@@ -0,0 +1,7 @@
+export default function MembershipPage() {
+ return (
+
+
Membership
+
+ );
+}
diff --git a/src/app/page.tsx b/src/app/page.tsx
index e165fb3..d0f6866 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -1,9 +1,13 @@
import React from "react";
+import { siteConfig } from "@/config/site";
+import { Logo } from "@/components/icons";
export default function Home() {
return (
-
-
Coming Soon
+
+
{siteConfig.name}
+
+
Home Page
);
-}
\ No newline at end of file
+}
diff --git a/src/app/providers.tsx b/src/app/providers.tsx
index be02117..08e75ea 100644
--- a/src/app/providers.tsx
+++ b/src/app/providers.tsx
@@ -1,8 +1,28 @@
-'use client';
+// "use client";
-import './globals.css';
-import { NextUIProvider } from '@nextui-org/react';
+// import "./globals.css";
+// import { NextUIProvider } from '@nextui-org/react';
-export function Providers({ children }: { children: React.ReactNode }) {
- return
{children};
+// export function Providers({ children }: { children: React.ReactNode }) {
+// return
{children};
+// }
+
+"use client";
+
+import * as React from "react";
+import { NextUIProvider } from "@nextui-org/system";
+import { ThemeProvider as NextThemesProvider } from "next-themes";
+import { ThemeProviderProps } from "next-themes/dist/types";
+
+export interface ProvidersProps {
+ children: React.ReactNode;
+ themeProps?: ThemeProviderProps;
+}
+
+export function Providers({ children, themeProps }: ProvidersProps) {
+ return (
+
+ {children}
+
+ );
}
diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx
new file mode 100644
index 0000000..edbedfc
--- /dev/null
+++ b/src/components/Footer.tsx
@@ -0,0 +1,11 @@
+import { siteConfig } from "@/config/site";
+
+export default function Footer() {
+ return (
+
+ );
+}
diff --git a/src/components/box.tsx b/src/components/box.tsx
new file mode 100644
index 0000000..5be63a8
--- /dev/null
+++ b/src/components/box.tsx
@@ -0,0 +1,17 @@
+import React from 'react';
+
+const Box = ({ icon, name, para }: any) => {
+ return (
+
+
+
{icon}
+
+ {name}
+
+
{para}
+
+
+ );
+};
+
+export default Box;
diff --git a/src/components/icons.tsx b/src/components/icons.tsx
new file mode 100644
index 0000000..5697f69
--- /dev/null
+++ b/src/components/icons.tsx
@@ -0,0 +1,76 @@
+import {
+ RiFacebookLine,
+ RiGithubLine,
+ RiInstagramLine,
+ RiLinkedinLine,
+ RiTwitterXLine,
+} from 'react-icons/ri';
+import { MdDarkMode, MdLightMode } from 'react-icons/md';
+import Image from 'next/image';
+
+const iconSize = 18;
+const className = 'text-default-500 hover:text-[#1E50FF] transition-all duration-300 ease-in-out';
+
+interface ThemedImageProps {
+ className?: string;
+ width?: number;
+ height?: number;
+ priority?: boolean;
+}
+
+export const Logo = ({
+ className,
+ width = 200,
+ height = 112.5,
+ priority = false,
+}: ThemedImageProps) => (
+ <>
+ {/* When the theme is dark */}
+
+
+ {/* When the theme is light */}
+
+ >
+);
+
+export const FacebookIcon = () => {
+ return
;
+};
+
+export const InstagramIcon = () => {
+ return
;
+};
+
+export const TwitterIcon = () => {
+ return
;
+};
+
+export const GithubIcon = () => {
+ return
;
+};
+
+export const LinkedinIcon = () => {
+ return
;
+};
+
+export const DarkModeIcon = () => {
+ return
;
+};
+
+export const LightModeIcon = () => {
+ return
;
+};
diff --git a/src/components/map.tsx b/src/components/map.tsx
new file mode 100644
index 0000000..1f85dd3
--- /dev/null
+++ b/src/components/map.tsx
@@ -0,0 +1,23 @@
+import { motion } from 'framer-motion';
+import { fadeIn } from '../utils/variants';
+
+export default function Map() {
+ return (
+
+
+
+ );
+}
diff --git a/src/components/navbar.tsx b/src/components/navbar.tsx
new file mode 100644
index 0000000..fd0444e
--- /dev/null
+++ b/src/components/navbar.tsx
@@ -0,0 +1,136 @@
+'use client';
+
+import { useEffect, useState } from 'react';
+import {
+ Navbar as NextUINavbar,
+ NavbarContent,
+ NavbarMenu,
+ NavbarMenuToggle,
+ NavbarBrand,
+ NavbarItem,
+ NavbarMenuItem,
+} from '@nextui-org/navbar';
+import { Button } from '@nextui-org/react';
+import { Link } from '@nextui-org/link';
+import { link as linkStyles } from '@nextui-org/theme';
+import { siteConfig } from '@/config/site';
+import NextLink from 'next/link';
+import clsx from 'clsx';
+import { usePathname } from 'next/navigation';
+import { Logo } from '@/components/icons';
+
+export const Navbar = () => {
+ const [scrolling, setScrolling] = useState(false);
+ const pathname = usePathname();
+
+ useEffect(() => {
+ const handleScroll = () => {
+ if (window.scrollY > 50) {
+ setScrolling(true);
+ } else {
+ setScrolling(false);
+ }
+ };
+
+ window.addEventListener('scroll', handleScroll);
+
+ return () => {
+ window.removeEventListener('scroll', handleScroll);
+ };
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ {siteConfig.navItems.map((item) => (
+
+
+ {item.label}
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {siteConfig.navItems.map((item, index) => (
+
+
+ {item.label}
+
+
+ ))}
+
+
+
+ Membership
+
+
+
+
+
+ );
+};
diff --git a/src/components/social.tsx b/src/components/social.tsx
new file mode 100644
index 0000000..bf7cc91
--- /dev/null
+++ b/src/components/social.tsx
@@ -0,0 +1,43 @@
+'use client';
+
+import {
+ RiInstagramLine,
+ RiFacebookLine,
+ RiGithubLine,
+ RiLinkedinLine,
+ RiTwitterXLine,
+} from 'react-icons/ri';
+import Link from 'next/link';
+
+export const socialData = [
+ { path: 'https://instagram.com/nibmcs', icon:
},
+ { path: 'https://facebook.com/nibmcs', icon:
},
+ { path: 'https://github.com/nibmcs', icon:
},
+ { path: 'https://linkedin.com/company/nibmcs', icon:
},
+ { path: 'https://twitter.com/nibmcs', icon:
},
+];
+
+const Social = () => {
+ return (
+
+
+ {socialData.map((link, index) => {
+ return (
+
+
+ {link.icon}
+
+
+ );
+ })}
+
+
+ );
+};
+
+export default Social;
diff --git a/src/components/theme-switch.tsx b/src/components/theme-switch.tsx
new file mode 100644
index 0000000..e4089bc
--- /dev/null
+++ b/src/components/theme-switch.tsx
@@ -0,0 +1,79 @@
+"use client";
+
+import { FC } from "react";
+import { VisuallyHidden } from "@react-aria/visually-hidden";
+import { SwitchProps, useSwitch } from "@nextui-org/switch";
+import { useTheme } from "next-themes";
+import { useIsSSR } from "@react-aria/ssr";
+import clsx from "clsx";
+
+import { DarkModeIcon, LightModeIcon } from "@/components/icons";
+
+export interface ThemeSwitchProps {
+ className?: string;
+ classNames?: SwitchProps["classNames"];
+}
+
+export const ThemeSwitch: FC
= ({
+ className,
+ classNames,
+}) => {
+ const { theme, setTheme } = useTheme();
+ const isSSR = useIsSSR();
+
+ const onChange = () => {
+ theme === "light" ? setTheme("dark") : setTheme("light");
+ };
+
+ const {
+ Component,
+ slots,
+ isSelected,
+ getBaseProps,
+ getInputProps,
+ getWrapperProps,
+ } = useSwitch({
+ isSelected: theme === "light" || isSSR,
+ "aria-label": `Switch to ${
+ theme === "light" || isSSR ? "dark" : "light"
+ } mode`,
+ onChange,
+ });
+
+ return (
+
+
+
+
+
+ {!isSelected || isSSR ? : }
+
+
+ );
+};
diff --git a/src/config/fonts.ts b/src/config/fonts.ts
new file mode 100644
index 0000000..43a02f4
--- /dev/null
+++ b/src/config/fonts.ts
@@ -0,0 +1,8 @@
+import { Poppins } from 'next/font/google';
+
+export const fontSans = Poppins({
+ subsets: ['latin'],
+ weight: ['400', '500', '600', '700', '800'],
+ variable: '--font-sans',
+ style: 'normal',
+});
diff --git a/src/config/site.ts b/src/config/site.ts
new file mode 100644
index 0000000..c09a802
--- /dev/null
+++ b/src/config/site.ts
@@ -0,0 +1,41 @@
+export type SiteConfig = typeof siteConfig;
+
+export const siteConfig = {
+ name: 'NIBM Computing Society',
+ description:
+ 'We are a student community organization for the NIBM - Sri Lanka, established with the aim of enabling us to learn, share, and build our professions.',
+ navItems: [
+ {
+ label: 'Events',
+ href: '/events',
+ active: false,
+ },
+ {
+ label: 'Leaderboard',
+ href: '/leaderboard',
+ active: false,
+ },
+ {
+ label: 'Blog',
+ href: '/blog',
+ active: false,
+ },
+ {
+ label: 'About',
+ href: '/about',
+ active: false,
+ },
+ {
+ label: 'Contact',
+ href: '/contact',
+ active: false,
+ },
+ ],
+ // links: {
+ // facebook: 'https://facebook.com/nibmcs',
+ // instagram: 'https://instagram.com/nibmcs',
+ // twitter: 'https://twitter.com/nibmcs',
+ // linkedin: 'https://linkedin.com/company/nibmcs',
+ // github: 'https://github.com/nibmcs',
+ // },
+};
diff --git a/src/utils/variants.ts b/src/utils/variants.ts
new file mode 100644
index 0000000..d1c8af1
--- /dev/null
+++ b/src/utils/variants.ts
@@ -0,0 +1,26 @@
+export const fadeIn = (direction: any, delay: any) => {
+ return {
+ hidden: {
+ y: direction === 'up' ? 80 : direction === 'down' ? -80 : 0,
+ opacity: 0,
+ x: direction === 'left' ? 80 : direction === 'right' ? -80 : 0,
+ transition: {
+ type: 'tween',
+ duration: 1.5,
+ delay: delay,
+ ease: [0.25, 0.6, 0.3, 0.8],
+ },
+ },
+ show: {
+ y: 0,
+ x: 0,
+ opacity: 1,
+ transition: {
+ type: 'tween',
+ duration: 1.4,
+ delay: delay,
+ ease: [0.25, 0.25, 0.25, 0.75],
+ },
+ },
+ };
+};