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/lib/utils.ts b/src/lib/utils.ts index c2d8c5f2..f7786b1d 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,6 +1,25 @@ 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)); } + +/** + * 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._index.tsx b/src/routes/_home._index.tsx index 772f0ad3..91c47375 100644 --- a/src/routes/_home._index.tsx +++ b/src/routes/_home._index.tsx @@ -1,134 +1,56 @@ 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 { cn } from "~/lib/utils"; 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 */} - {/* Getting the routes from the defined route file in pages */} -
- {/*Upper start*/} -
- Vektorprogrammet - Vektorprogrammet bildet -
-
-

- {"Vektorprogrammet"} -

-
-

- {`- sender studenter til ungdomsskoler for å hjelpe til som lærerens +

+
+ {"Logo + Illustrasjon av assistenter fra Vektorprogrammet foran en tavle med matteformler +
+

{"Vektorprogrammet"}

+

+ {`- sender studenter til ungdomsskoler for å hjelpe til som lærerens assistent i matematikkundervisningen`} -

-
- +

+
-
+ + {/*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 +80,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.assistenter.tsx b/src/routes/_home.assistenter.tsx index d8c33a6a..b74b54fb 100644 --- a/src/routes/_home.assistenter.tsx +++ b/src/routes/_home.assistenter.tsx @@ -30,6 +30,7 @@ import { Button } from "~/components/ui/button"; import { Checkbox } from "~/components/ui/checkbox"; import type { CityPretty } from "~/lib/types"; import { cities } from "~/lib/types"; +import { cn } from "~/lib/utils"; // biome-ignore lint/style/noDefaultExport: Route Modules require default export https://reactrouter.com/start/framework/route-module export default function Assistenter() { @@ -45,7 +46,15 @@ export default function Assistenter() { const assistantFaqs = getAssistantFaqs(); return ( -
+

{title} diff --git a/src/routes/_home.foreldre.tsx b/src/routes/_home.foreldre.tsx index 4e4434b6..973ecf6c 100644 --- a/src/routes/_home.foreldre.tsx +++ b/src/routes/_home.foreldre.tsx @@ -1,12 +1,21 @@ import { getForeldre } from "@/api/foreldre"; import { Divider } from "~/components/divider"; import { TextPictureParagraph } from "~/components/text-picture-paragraph"; +import { cn } from "~/lib/utils"; // biome-ignore lint/style/noDefaultExport: Route Modules require default export https://reactrouter.com/start/framework/route-module export default function ForForeldre() { const { title, ingress, cards, bottomText } = getForeldre(); return ( -
+

{title} diff --git a/src/routes/_home.kontakt.tsx b/src/routes/_home.kontakt.tsx index 296c237a..1d0cfdd0 100644 --- a/src/routes/_home.kontakt.tsx +++ b/src/routes/_home.kontakt.tsx @@ -1,11 +1,20 @@ import { Outlet } from "react-router"; import { getKontakt } from "~/api/kontakt"; +import { cn } from "~/lib/utils"; // biome-ignore lint/style/noDefaultExport: Route Modules require default export https://reactrouter.com/start/framework/route-module export default function KontaktLayout() { const kontaktInfo = getKontakt(); return ( -
+

diff --git a/src/routes/_home.om-oss.tsx b/src/routes/_home.om-oss.tsx index fbb9f715..d555fcab 100644 --- a/src/routes/_home.om-oss.tsx +++ b/src/routes/_home.om-oss.tsx @@ -1,6 +1,7 @@ import { getOmOss } from "@/api/om-oss"; import { Divider } from "~/components/divider"; import { TextPictureParagraph } from "~/components/text-picture-paragraph"; +import { cn } from "~/lib/utils"; // biome-ignore lint/style/noDefaultExport: Route Modules require default export https://reactrouter.com/start/framework/route-module export default function OmOss() { @@ -8,7 +9,15 @@ export default function OmOss() { getOmOss(); return ( -
+

{title} diff --git a/src/routes/_home.skoler.tsx b/src/routes/_home.skoler.tsx index 2603f475..4212e37b 100644 --- a/src/routes/_home.skoler.tsx +++ b/src/routes/_home.skoler.tsx @@ -2,11 +2,18 @@ import { Link, href } from "react-router"; import { Divider } from "~/components/divider"; import { Button } from "~/components/ui/button"; import { cities } from "~/lib/types"; +import { cn } from "~/lib/utils"; // biome-ignore lint/style/noDefaultExport: Route Modules require default export https://reactrouter.com/start/framework/route-module export default function ForSkoler() { return ( -
+

diff --git a/src/routes/_home.team.aas.evaluering-rekruttering-profilering.tsx b/src/routes/_home.team.aas.evaluering-rekruttering-profilering.tsx index e96468d9..e59f3791 100644 --- a/src/routes/_home.team.aas.evaluering-rekruttering-profilering.tsx +++ b/src/routes/_home.team.aas.evaluering-rekruttering-profilering.tsx @@ -1,9 +1,18 @@ import { TeamTemplate } from "@/components/team-template"; +import { cn } from "~/lib/utils"; // biome-ignore lint/style/noDefaultExport: Route Modules require default export https://reactrouter.com/start/framework/route-module export default function EvalueringRekrutteringProfilering() { return ( -
+
+
+
+
+
+
+
+
+
+
+
+
, +
+
+
+
+
+

diff --git a/src/routes/_home.tsx b/src/routes/_home.tsx index 2cd09e41..7c99df2f 100644 --- a/src/routes/_home.tsx +++ b/src/routes/_home.tsx @@ -1,10 +1,11 @@ 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"; -import { type Sponsor, getSponsors } from "~/api/sponsor"; -import { Button, buttonVariants } from "~/components/ui/button"; +import { type Sponsor, getAllSponsors } from "~/api/sponsor"; +import { buttonVariants } from "~/components/ui/button"; import { Drawer, DrawerClose, @@ -12,42 +13,85 @@ import { DrawerDescription, DrawerFooter, DrawerHeader, + DrawerTitle, DrawerTrigger, } from "~/components/ui/drawer"; import "~/home.css"; +import { breakpointPixels, cn } from "~/lib/utils"; import { navRoutes } from "~/routes"; +import itTor from "/images/team/IT-Tor.png"; +import icon from "/images/vektor-logo-circle.svg"; +import logoWhite from "/images/vektor-logo-white.svg"; // biome-ignore lint/style/noDefaultExport: Route Modules require default export https://reactrouter.com/start/framework/route-module export default function Layout() { return ( -
- - {/* Banner */} +
+ + - + +
); } -function AppHeader() { +function NavBar({ className }: { className?: string }) { + const { width } = useViewportSize(); + const isMobile = width < breakpointPixels.md; + return ( -
-
-
- vektorprogrammet logo - -
-
-
- -
- -
+ ); } @@ -57,14 +101,19 @@ function NavTabs({ routes: Array<{ name: string; path: To }>; }) { return ( -
+
{routes.map((route) => { return ( - `${isActive ? "text-black" : "text-neutral-700 hover:text-black"} relative my-1.5 place-content-center px-4 py-auto text-center font-medium text-sm` + cn( + isActive ? "text-black" : "text-neutral-700 hover:text-black", + "my-1.5 px-4 font-medium text-sm", + // Affects children + "relative place-content-center text-nowrap text-center", + ) } prefetch="intent" > @@ -93,90 +142,114 @@ function NavTabs({ function LoginButtons() { return ( -
- - {"Logg inn"} - -
+ + {"Logg inn"} + ); } const MobileMenu = ({ routes, -}: { routes: Array<{ name: string; path: To }> }) => { + className, +}: { routes: Array<{ name: string; path: To }>; className?: string }) => { return ( -
- - -
- -
-
- - - -
-
    - {routes.map((route) => ( -
  • - - {route.name} - -
  • - ))} -
-
- -
-
+ + +
+ + + {"Tor"} + +
+
+ + + {"Navigasjonsmeny"} + - - - - - - -
-
+ + +
+
    + {routes.map((route) => ( +
  • + + {route.name} + +
  • + ))} +
+
+ +
+
+ + + + {"Close"} + + + + ); }; -function AppFooter() { +function Footer({ className }: { className?: string }) { return ( -