diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 6e31a38..fcb8309 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -4,25 +4,34 @@ import '@mantine/core/styles.css'
import type { Metadata } from 'next'
import React from 'react'
import { ColorSchemeScript, MantineProvider } from '@mantine/core'
-
+import { Montserrat } from 'next/font/google'
+import Navbar from '@/components/Navbar'
export const metadata: Metadata = {
- title: 'Matz Hilven',
+ title: 'Matz Hilven | Software Engineer',
description: 'todo',
}
+const font = Montserrat({
+ subsets: ['latin'],
+ display: 'swap',
+})
+
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
-
+
-
-
{children}
+
diff --git a/src/app/page.tsx b/src/app/page.tsx
index ce71969..5449e07 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -1,99 +1,5 @@
-'use client'
-
-import React, { useState, useEffect, useMemo } from 'react'
-import { ProjectCard } from '@/components/ProjectCard'
-import projectsData from '../../public/projects.json'
-import { projectsSchema, Project } from '@/typings/project'
-import { useInView } from 'react-intersection-observer'
-
const Page = () => {
- const parsedProjects = projectsSchema.safeParse(projectsData)
-
- if (!parsedProjects.success) {
- console.error('Invalid project data', parsedProjects.error)
- return
Error loading projects
- }
-
- const projects: Project[] = parsedProjects.data
-
- const [currentPage, setCurrentPage] = useState(1)
- const [itemsPerPage] = useState
(12)
- const [filterTag, setFilterTag] = useState(null)
- const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('asc')
-
- const { ref, inView } = useInView({
- threshold: 1.0,
- })
-
- const handleFilterChange = (event: React.ChangeEvent) => {
- setFilterTag(event.target.value === 'All' ? null : event.target.value)
- setCurrentPage(1)
- }
-
- const handleSortChange = (event: React.ChangeEvent) => {
- setSortOrder(event.target.value as 'asc' | 'desc')
- setCurrentPage(1)
- }
-
- const filteredAndSortedProjects = useMemo(() => {
- let updatedProjects = filterTag
- ? projects.filter(project => project.tags.includes(filterTag))
- : projects
-
- updatedProjects = [...updatedProjects].sort((a, b) => {
- if (sortOrder === 'asc') {
- return a.name.localeCompare(b.name)
- } else {
- return b.name.localeCompare(a.name)
- }
- })
-
- return updatedProjects
- }, [projects, filterTag, sortOrder])
-
- const visibleProjects = useMemo(() => {
- return filteredAndSortedProjects.slice(0, currentPage * itemsPerPage)
- }, [filteredAndSortedProjects, currentPage, itemsPerPage])
-
- useEffect(() => {
- if (
- inView &&
- currentPage * itemsPerPage < filteredAndSortedProjects.length
- ) {
- setCurrentPage(prevPage => prevPage + 1)
- }
- }, [inView, currentPage, itemsPerPage, filteredAndSortedProjects.length])
-
- return (
-
-
-
-
-
-
- {visibleProjects.map((project, index) => (
-
- ))}
-
-
-
- )
+ return hey
}
export default Page
diff --git a/src/app/projects/page.tsx b/src/app/projects/page.tsx
new file mode 100644
index 0000000..d5aff24
--- /dev/null
+++ b/src/app/projects/page.tsx
@@ -0,0 +1,99 @@
+'use client'
+
+import React, { useState, useEffect, useMemo } from 'react'
+import { ProjectCard } from '@/components/ProjectCard'
+import projectsData from '../../../public/projects.json'
+import { projectsSchema, Project } from '@/typings/project'
+import { useInView } from 'react-intersection-observer'
+
+const Page = () => {
+ const parsedProjects = projectsSchema.safeParse(projectsData)
+
+ if (!parsedProjects.success) {
+ console.error('Invalid project data', parsedProjects.error)
+ return Error loading projects
+ }
+
+ const projects: Project[] = parsedProjects.data
+
+ const [currentPage, setCurrentPage] = useState(1)
+ const [itemsPerPage] = useState(12)
+ const [filterTag, setFilterTag] = useState(null)
+ const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('asc')
+
+ const { ref, inView } = useInView({
+ threshold: 1.0,
+ })
+
+ const handleFilterChange = (event: React.ChangeEvent) => {
+ setFilterTag(event.target.value === 'All' ? null : event.target.value)
+ setCurrentPage(1)
+ }
+
+ const handleSortChange = (event: React.ChangeEvent) => {
+ setSortOrder(event.target.value as 'asc' | 'desc')
+ setCurrentPage(1)
+ }
+
+ const filteredAndSortedProjects = useMemo(() => {
+ let updatedProjects = filterTag
+ ? projects.filter(project => project.tags.includes(filterTag))
+ : projects
+
+ updatedProjects = [...updatedProjects].sort((a, b) => {
+ if (sortOrder === 'asc') {
+ return a.name.localeCompare(b.name)
+ } else {
+ return b.name.localeCompare(a.name)
+ }
+ })
+
+ return updatedProjects
+ }, [projects, filterTag, sortOrder])
+
+ const visibleProjects = useMemo(() => {
+ return filteredAndSortedProjects.slice(0, currentPage * itemsPerPage)
+ }, [filteredAndSortedProjects, currentPage, itemsPerPage])
+
+ useEffect(() => {
+ if (
+ inView &&
+ currentPage * itemsPerPage < filteredAndSortedProjects.length
+ ) {
+ setCurrentPage(prevPage => prevPage + 1)
+ }
+ }, [inView, currentPage, itemsPerPage, filteredAndSortedProjects.length])
+
+ return (
+
+
+
+
+
+
+ {visibleProjects.map((project, index) => (
+
+ ))}
+
+
+
+ )
+}
+
+export default Page
diff --git a/src/components/NavLink.tsx b/src/components/NavLink.tsx
new file mode 100644
index 0000000..de0c6aa
--- /dev/null
+++ b/src/components/NavLink.tsx
@@ -0,0 +1,18 @@
+import Link from 'next/link'
+
+type NavLinkProps = {
+ href: string
+ isActive: boolean
+ label: string
+}
+
+const NavLink = ({ href, isActive, label }: NavLinkProps) => {
+ return (
+
+ {label}
+
+
+ )
+}
+
+export default NavLink
diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx
new file mode 100644
index 0000000..b07144a
--- /dev/null
+++ b/src/components/Navbar.tsx
@@ -0,0 +1,39 @@
+'use client'
+
+import React from 'react'
+import { usePathname } from 'next/navigation'
+import NavLink from './NavLink'
+import Link from 'next/link'
+
+const routes = [
+ { path: '/', label: 'About' },
+ { path: '/projects', label: 'Projects' },
+ { path: '/homelab', label: 'Homelab' },
+ { path: '/contact', label: 'Contact' },
+]
+
+const Navbar = () => {
+ const pathname = usePathname()
+
+ return (
+
+ )
+}
+
+export default Navbar
diff --git a/tailwind.config.ts b/tailwind.config.ts
index 70c2aeb..c039606 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -2,21 +2,9 @@ import type { Config } from 'tailwindcss'
const config = {
darkMode: ['class'],
- content: [
- './pages/**/*.{ts,tsx}',
- './components/**/*.{ts,tsx}',
- './app/**/*.{ts,tsx}',
- './src/**/*.{ts,tsx}',
- ],
+ content: ['./src/**/*.{ts,tsx}'],
prefix: '',
theme: {
- container: {
- center: true,
- padding: '2rem',
- screens: {
- '2xl': '1400px',
- },
- },
extend: {
dropShadow: {
glow: [
@@ -25,6 +13,12 @@ const config = {
],
},
keyframes: {
+ gradient: {
+ '0%, 100%': { backgroundPosition: '0% 50%' },
+ '25%': { backgroundPosition: '100% 0%' },
+ '50%': { backgroundPosition: '100% 100%' },
+ '75%': { backgroundPosition: '0% 100%' },
+ },
jump: {
'0%, 100%': { transform: 'translateY(0)' },
'50%': { transform: 'translateY(-10px)' },
@@ -32,6 +26,7 @@ const config = {
},
animation: {
jump: 'jump 0.4s ease-in-out',
+ gradient: 'gradient 10s ease infinite',
},
},
},