From 6d9974c70d37fd94ca5797887a0bb1e38f3cbc22 Mon Sep 17 00:00:00 2001 From: phibkro <71797726+phibkro@users.noreply.github.com> Date: Thu, 10 Apr 2025 11:30:56 +0200 Subject: [PATCH 01/15] fix: :bug: hide navbar on desktop --- src/hooks/use-mobile.tsx | 19 ++++++ src/routes/_home.tsx | 128 ++++++++++++++++++++------------------- 2 files changed, 86 insertions(+), 61 deletions(-) create mode 100644 src/hooks/use-mobile.tsx diff --git a/src/hooks/use-mobile.tsx b/src/hooks/use-mobile.tsx new file mode 100644 index 00000000..6c79753e --- /dev/null +++ b/src/hooks/use-mobile.tsx @@ -0,0 +1,19 @@ +import { useEffect, useState } from "react"; + +const MOBILE_BREAKPOINT = 768; + +export function useIsMobile() { + const [isMobile, setIsMobile] = useState(undefined); + + useEffect(() => { + const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`); + const onChange = () => { + setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); + }; + mql.addEventListener("change", onChange); + setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); + return () => mql.removeEventListener("change", onChange); + }, []); + + return !!isMobile; +} diff --git a/src/routes/_home.tsx b/src/routes/_home.tsx index 2cd09e41..6379d5df 100644 --- a/src/routes/_home.tsx +++ b/src/routes/_home.tsx @@ -15,6 +15,7 @@ import { DrawerTrigger, } from "~/components/ui/drawer"; import "~/home.css"; +import { useIsMobile } from "~/hooks/use-mobile"; import { navRoutes } from "~/routes"; // biome-ignore lint/style/noDefaultExport: Route Modules require default export https://reactrouter.com/start/framework/route-module @@ -30,23 +31,30 @@ export default function Layout() { } function AppHeader() { + const isMobile = useIsMobile(); + return (
-
-
- vektorprogrammet logo - -
-
-
- -
- + {isMobile ? ( + + ) : ( + <> +
+
+ vektorprogrammet logo + +
+
+
+ +
+ + )}
); } @@ -109,53 +117,51 @@ const MobileMenu = ({ routes, }: { routes: Array<{ name: string; path: To }> }) => { return ( -
- - -
- -
-
- - - -
-
    - {routes.map((route) => ( -
  • - - {route.name} - -
  • - ))} -
-
- -
+ + +
+ +
+
+ + + +
+
    + {routes.map((route) => ( +
  • + + {route.name} + +
  • + ))} +
+
+
- - - - - - - - -
+
+
+ + + + + +
+
); }; From 6d33931e01d963d38065dacd4209383f5c411eab Mon Sep 17 00:00:00 2001 From: phibkro <71797726+phibkro@users.noreply.github.com> Date: Thu, 10 Apr 2025 13:46:24 +0200 Subject: [PATCH 02/15] fix: :bug: fix navbar flickering on mobile navigation --- src/hooks/use-mobile.tsx | 19 ------------------- src/lib/utils.ts | 13 +++++++++++++ src/routes/_home.tsx | 6 ++++-- 3 files changed, 17 insertions(+), 21 deletions(-) delete mode 100644 src/hooks/use-mobile.tsx diff --git a/src/hooks/use-mobile.tsx b/src/hooks/use-mobile.tsx deleted file mode 100644 index 6c79753e..00000000 --- a/src/hooks/use-mobile.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { useEffect, useState } from "react"; - -const MOBILE_BREAKPOINT = 768; - -export function useIsMobile() { - const [isMobile, setIsMobile] = useState(undefined); - - useEffect(() => { - const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`); - const onChange = () => { - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); - }; - mql.addEventListener("change", onChange); - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); - return () => mql.removeEventListener("change", onChange); - }, []); - - return !!isMobile; -} diff --git a/src/lib/utils.ts b/src/lib/utils.ts index c2d8c5f2..c9473f6d 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -4,3 +4,16 @@ import { twMerge } from "tailwind-merge"; export function cn(...inputs: Array) { return twMerge(clsx(inputs)); } + +/** + * Size breakpoints in pixels based on Tailwind CSS defaults. + * + * https://tailwindcss.com/docs/responsive-design + */ +export const breakpointPixels = { + sm: 640, + md: 768, + lg: 1024, + xl: 1280, + "2xl": 1536, +} as const; diff --git a/src/routes/_home.tsx b/src/routes/_home.tsx index 6379d5df..214db8ce 100644 --- a/src/routes/_home.tsx +++ b/src/routes/_home.tsx @@ -1,5 +1,6 @@ import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { SiFacebook } from "@icons-pack/react-simple-icons"; +import { useViewportSize } from "@mantine/hooks"; import { FolderOpen, Mail, MapPin } from "lucide-react"; import { motion } from "motion/react"; import { Link, NavLink, Outlet, type To } from "react-router"; @@ -15,7 +16,7 @@ import { DrawerTrigger, } from "~/components/ui/drawer"; import "~/home.css"; -import { useIsMobile } from "~/hooks/use-mobile"; +import { breakpointPixels } from "~/lib/utils"; import { navRoutes } from "~/routes"; // biome-ignore lint/style/noDefaultExport: Route Modules require default export https://reactrouter.com/start/framework/route-module @@ -31,7 +32,8 @@ export default function Layout() { } function AppHeader() { - const isMobile = useIsMobile(); + const { width } = useViewportSize(); + const isMobile = width < breakpointPixels.sm; return (
From 93b7e6a911cf21211c841e8a43304bc93cb95df1 Mon Sep 17 00:00:00 2001 From: phibkro <71797726+phibkro@users.noreply.github.com> Date: Thu, 10 Apr 2025 13:46:43 +0200 Subject: [PATCH 03/15] docs: :memo: add docstring for cn --- src/lib/utils.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib/utils.ts b/src/lib/utils.ts index c9473f6d..f7786b1d 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,6 +1,12 @@ import { type ClassValue, clsx } from "clsx"; import { twMerge } from "tailwind-merge"; +/** + * Merge and deduplicate Tailwind classes without style conflicts + * + * @param inputs - Class names to merge + * @returns A string of class names, merged and deduplicated + */ export function cn(...inputs: Array) { return twMerge(clsx(inputs)); } From a1339f9eeb3973f477f0acd4e9d64e4cf788c356 Mon Sep 17 00:00:00 2001 From: phibkro <71797726+phibkro@users.noreply.github.com> Date: Thu, 10 Apr 2025 14:39:40 +0200 Subject: [PATCH 04/15] fix: :wheelchair: fix accessibility and html structure --- src/routes/_home.tsx | 75 +++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 33 deletions(-) diff --git a/src/routes/_home.tsx b/src/routes/_home.tsx index 214db8ce..cc215ca3 100644 --- a/src/routes/_home.tsx +++ b/src/routes/_home.tsx @@ -13,6 +13,7 @@ import { DrawerDescription, DrawerFooter, DrawerHeader, + DrawerTitle, DrawerTrigger, } from "~/components/ui/drawer"; import "~/home.css"; @@ -121,45 +122,53 @@ const MobileMenu = ({ return ( -
- + + {"Tor"} +
- - -
-
    - {routes.map((route) => ( -
  • - - {route.name} - -
  • - ))} -
-
- -
+ + {"Navigasjonsmeny"} + + + +
+
    + {routes.map((route) => ( +
  • + + {route.name} + +
  • + ))} +
+
+
- +
+ - - + + {"Close"} From 2a66b2f8564e657c3fc8a26025f28341739629e6 Mon Sep 17 00:00:00 2001 From: phibkro <71797726+phibkro@users.noreply.github.com> Date: Thu, 10 Apr 2025 14:40:19 +0200 Subject: [PATCH 05/15] fix: :lipstick: fix breakpoint styling --- src/routes/_home.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/routes/_home.tsx b/src/routes/_home.tsx index cc215ca3..ecc85828 100644 --- a/src/routes/_home.tsx +++ b/src/routes/_home.tsx @@ -5,7 +5,7 @@ import { FolderOpen, Mail, MapPin } from "lucide-react"; import { motion } from "motion/react"; import { Link, NavLink, Outlet, type To } from "react-router"; import { type Sponsor, getSponsors } from "~/api/sponsor"; -import { Button, buttonVariants } from "~/components/ui/button"; +import { buttonVariants } from "~/components/ui/button"; import { Drawer, DrawerClose, @@ -17,7 +17,7 @@ import { DrawerTrigger, } from "~/components/ui/drawer"; import "~/home.css"; -import { breakpointPixels } from "~/lib/utils"; +import { breakpointPixels, cn } from "~/lib/utils"; import { navRoutes } from "~/routes"; // biome-ignore lint/style/noDefaultExport: Route Modules require default export https://reactrouter.com/start/framework/route-module @@ -34,7 +34,7 @@ export default function Layout() { function AppHeader() { const { width } = useViewportSize(); - const isMobile = width < breakpointPixels.sm; + const isMobile = width < breakpointPixels.md; return (
@@ -53,7 +53,7 @@ function AppHeader() {
-
+
From daaa6ac2c4149937ca2085267da072469ca21bfe Mon Sep 17 00:00:00 2001 From: phibkro <71797726+phibkro@users.noreply.github.com> Date: Thu, 10 Apr 2025 16:59:13 +0200 Subject: [PATCH 06/15] refactor: :truck: move mock data to api folder --- src/api/main.ts | 36 ++++++++++ src/api/sponsor.ts | 55 ++++++++++++-- src/routes/_home._index.tsx | 140 ++++++++---------------------------- src/routes/_home.tsx | 4 +- 4 files changed, 115 insertions(+), 120 deletions(-) create mode 100644 src/api/main.ts diff --git a/src/api/main.ts b/src/api/main.ts new file mode 100644 index 00000000..8821d9d7 --- /dev/null +++ b/src/api/main.ts @@ -0,0 +1,36 @@ +import { type To, href } from "react-router"; + +interface Metric { + number: number; + title: string; + description: string; + link: { + path: To; + text: string; + }; +} + +export function getMetrics(): Array { + return [ + { + number: 2218, + title: "Assistenter", + description: + "Over 2218 studenter har hatt et verv som vektorassistent i Vektorprogrammet", + link: { + path: href("/assistenter"), + text: "Les mer om assistenter", + }, + }, + { + number: 608, + title: "I team", + description: + "Over 608 studenter har hatt et verv i et av Vektorprogrammets mange team", + link: { + path: href("/team"), + text: "Les mer om verv i team", + }, + }, + ]; +} diff --git a/src/api/sponsor.ts b/src/api/sponsor.ts index 5c818934..55c5a9e3 100644 --- a/src/api/sponsor.ts +++ b/src/api/sponsor.ts @@ -1,29 +1,70 @@ +import Abelprisen from "/images/mainPage/sponsor/Abelprisen.png"; +import ksBergen from "/images/mainPage/sponsor/KSBergen.png"; +import Matematikksenteret from "/images/mainPage/sponsor/Matematikksenteret.png"; +import NTNUIE from "/images/mainPage/sponsor/NTNUIE.png"; +import NTNUIV from "/images/mainPage/sponsor/NTNUIV.png"; +import Samarbeidsforum from "/images/mainPage/sponsor/SamarbeidsForum.png"; +import sparebankstiftelsenDnb from "/images/mainPage/sponsor/SparebankstiftelsenDNB.png"; +import Tekna from "/images/mainPage/sponsor/Tekna.png"; +import UiB from "/images/mainPage/sponsor/UIB.png"; +import VisionTech from "/images/mainPage/sponsor/VisionTech.png"; + export interface Sponsor { name: string; url: URL; + image?: string; +} + +export function getMainSponsors(): Array { + return [ + { + name: "Abelprisen", + url: new URL("https://www.abelprisen.no/"), + image: Abelprisen, + }, + { + name: "Sparebankstiftelsen DNB", + url: new URL("https://www.sparebankstiftelsendnb.no/"), + image: sparebankstiftelsenDnb, + }, + ]; } -// TODO: This data should be fetched from backend later export function getSponsors(): Array { return [ - { name: "Tekna", url: new URL("https://www.tekna.no/") }, + { name: "Tekna", url: new URL("https://www.tekna.no/"), image: Tekna }, { name: "Nasjonalt senter for realfagsrekruttering", url: new URL("https://www.realfagsrekruttering.no/"), + image: Matematikksenteret, }, - { name: "NTNU IV", url: new URL("https://www.ntnu.no/iv") }, - { name: "NTNU IE", url: new URL("https://www.ntnu.no/ie") }, + { name: "NTNU IV", url: new URL("https://www.ntnu.no/iv"), image: NTNUIV }, + { name: "NTNU IE", url: new URL("https://www.ntnu.no/ie"), image: NTNUIE }, { name: "NTNU IMF", url: new URL("https://www.ntnu.no/imf") }, - { name: "Samarbeidsforum", url: new URL("https://www.ntnu.no/nv/sf") }, - { name: "Abelprisen", url: new URL("https://www.abelprisen.no/") }, - { name: "VisionTech", url: new URL("https://www.visiontech.no/") }, + { + name: "Samarbeidsforum", + url: new URL("https://www.ntnu.no/nv/sf"), + image: Samarbeidsforum, + }, + { + name: "VisionTech", + url: new URL("https://www.visiontech.no/"), + image: VisionTech, + }, { name: "Kulturstyret i Bergen", url: new URL("https://www.vtvest.no/kulturstyret/"), + image: ksBergen, }, { name: "MN-faktultetet ved UiB", url: new URL("https://www.uib.no/matnat"), + image: UiB, }, ]; } + +// TODO: This data should be fetched from backend later +export function getAllSponsors(): Array { + return [...getMainSponsors(), ...getSponsors()]; +} diff --git a/src/routes/_home._index.tsx b/src/routes/_home._index.tsx index 772f0ad3..b05388fe 100644 --- a/src/routes/_home._index.tsx +++ b/src/routes/_home._index.tsx @@ -1,99 +1,19 @@ import { buttonVariants } from "@/components/ui/button"; import { useInView, useMotionValue, useSpring } from "motion/react"; -import { useEffect, useRef } from "react"; -import { Link, type To, href } from "react-router"; +import { Fragment, useEffect, useRef } from "react"; +import { Link } from "react-router"; +import { getMetrics } from "~/api/main"; +import { getMainSponsors, getSponsors } from "~/api/sponsor"; import { Button } from "~/components/ui/button"; -import Abelprisen from "/images/mainPage/sponsor/Abelprisen.png"; -import ksBergen from "/images/mainPage/sponsor/KSBergen.png"; -import Matematikksenteret from "/images/mainPage/sponsor/Matematikksenteret.png"; -import NTNUIE from "/images/mainPage/sponsor/NTNUIE.png"; -import NTNUIV from "/images/mainPage/sponsor/NTNUIV.png"; -import Samarbeidsforum from "/images/mainPage/sponsor/SamarbeidsForum.png"; -import sparebankstiftelsenDnb from "/images/mainPage/sponsor/SparebankstiftelsenDNB.png"; -import Tekna from "/images/mainPage/sponsor/Tekna.png"; -import UiB from "/images/mainPage/sponsor/UIB.png"; -import VisionTech from "/images/mainPage/sponsor/VisionTech.png"; import vektorForsidebilde from "/images/mainPage/vektor-forsidebilde.png"; import vektorLogo from "/images/vektor-logo.svg"; -const hovedsponsor = [ - { - name: "Abelprisen", - image: Abelprisen, - }, - { - name: "Sparebankstiftelsen DNB", - image: sparebankstiftelsenDnb, - }, -]; - -const sponsorer = [ - { - name: "Tekna", - image: Tekna, - }, - { - name: "NTNU - Fakultet for ingeniørvitenskap", - image: NTNUIV, - }, - { - name: "NTNU - Fakultet for informasjonsteknologi og elektronikk", - image: NTNUIE, - }, - { - name: "Samarbeidsforum", - image: Samarbeidsforum, - }, - { - name: "Universitetet i Bergen - Det matematisk-naturvitenskapelige fakultet", - image: UiB, - }, - { - name: "Matematikksenteret", - image: Matematikksenteret, - }, - { - name: "VisionTech", - image: VisionTech, - }, - { - name: "Kulturstyret Bergen", - image: ksBergen, - }, -]; -interface MainPageProps { - number: number; - title: string; - text: string; - route: { - path: To; - text: string; - }; -} - -const cards: Array = [ - { - number: 2218, - title: "Assistenter", - text: "Over 2218 studenter har hatt et verv som vektorassistent i Vektorprogrammet", - route: { - path: href("/assistenter"), - text: "Les mer om assistenter", - }, - }, - { - number: 608, - title: "I team", - text: "Over 608 studenter har hatt et verv i et av Vektorprogrammets mange team", - route: { - path: href("/team"), - text: "Les mer om verv i team", - }, - }, -]; - // biome-ignore lint/style/noDefaultExport: Route Modules require default export https://reactrouter.com/start/framework/route-module -export default function mainPage() { +export default function MainPage() { + const mainSponsors = getMainSponsors(); + const sponsors = getSponsors(); + const metrics = getMetrics(); + return (
{/* Use component when the rendered component needs no props */} @@ -128,7 +48,7 @@ export default function mainPage() { {/*Upper end*/}
{/*Middle start*/} - {cards.map(({ number, title, text, route }) => ( + {metrics.map(({ number, title, description, link }) => (

{title}

-

{text}

+

+ {description} +

- {route.text} + {link.text}
@@ -156,31 +78,27 @@ export default function mainPage() {
- {hovedsponsor.map((sponsor) => ( + {mainSponsors.map(({ name, image }) => (
- {sponsor.name} + {name}
))}
- {sponsorer.map((sponsor) => ( -
- {sponsor.name} -
+ {sponsors.map(({ name, image }) => ( + + {image ?? ( +
+ {name} +
+ )} +
))}
diff --git a/src/routes/_home.tsx b/src/routes/_home.tsx index ecc85828..d06dd5bf 100644 --- a/src/routes/_home.tsx +++ b/src/routes/_home.tsx @@ -4,7 +4,7 @@ import { useViewportSize } from "@mantine/hooks"; import { FolderOpen, Mail, MapPin } from "lucide-react"; import { motion } from "motion/react"; import { Link, NavLink, Outlet, type To } from "react-router"; -import { type Sponsor, getSponsors } from "~/api/sponsor"; +import { type Sponsor, getAllSponsors } from "~/api/sponsor"; import { buttonVariants } from "~/components/ui/button"; import { Drawer, @@ -193,7 +193,7 @@ function AppFooter() { } function FooterSponsors() { - const sponsors = getSponsors(); + const sponsors = getAllSponsors(); return (