the big mega huge amazing nextjs migration

Code still trash though but we'll fix it someday

Closes #48, #47, #37, #32
This commit is contained in:
leander committed 2025-07-31 22:38:27 +02:00
1 parent e07e6ebcd0
commit e7d7e29edf
85 files changed
+9441 -12122

No files matched your search

+34 -62
View File
@@ -1,69 +1,41 @@
# Logs
logs
*.log
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# dotenv environment variable files
# env files (can opt-in for committing if needed)
.env*
# gatsby files
.cache/
public
# vercel
.vercel
# Mac files
.DS_Store
# Yarn
yarn-error.log
.pnp/
.pnp.js
# Yarn Integrity file
.yarn-integrity
# typescript
*.tsbuildinfo
next-env.d.ts
+2
View File
@@ -2,6 +2,8 @@
- Sørg for å formatere koden med Prettier
- Sjekk om nettsiden bygges uten problemer (`npm run build`)
- Commit messages samt issues skal helst være skrevet på engelsk
- Kode skal skrives på engelsk. Det vil si variabelnavn osv. Tekst som vises til bruker skal være på norsk
+25 -17
View File
@@ -1,38 +1,46 @@
# uiogaming.no
Dette er repoet for UiO Gaming sin nettside. Nettsiden består av tre deler hvorav to av disse er i dette repoet. Her finner du frontend for nettsiden samt et webhook API. Dette APIet brukes til å trigge bygging av nettsiden når innhold endrer seg i CMS, backenden. Repoet for dette CMSet er [her](https://github.com/UiO-Gaming/uiogaming.no-backend). Nettsiden er bygget med GatsbyJS.
Dette er repoet for UiO Gaming sin nettside. Nettsiden består av to deler. Her finner du frontend for nettsiden. Repoet for CMSet er [her](https://github.com/UiO-Gaming/uiogaming.no-backend).
## Oppsett
### Frontend
1. Lag konfigurasjonsfil
1. Installer avhengigheter
I repoet er det en fil som heter [env.example](env.example). Kopier denne og sett navnet på den nye filen til `.env`.
Fyll så ut verdiene.
2. Installer avhengigheter
```bash
yarn install
npm install
```
2. Kjør nettsiden
3. Bygg nettsiden
```bash
yarn start
npm run build
```
4. Kjør nettsiden
```bash
npm start
```
Om du skal utvikle kan du kjøre følgende istedenfor steg 3 og 4
```bash
npm run dev
```
### Webhook API
Om du vil at innhold skal oppdatere seg når innholdet endres i backend må du kjøre dette APIet. Dette gjelder bare om du bruker en produksjonsversjon av nettsiden (`yarn serve`)
Om du vil at innhold skal oppdatere seg når innholdet endres i backend må du kjøre dette APIet. Dette gjelder bare om du kjører en produksjonsversjon av nettsiden
```bash
cd webhook-api
yarn install
yarn start
```
Du må spesifisere URL for webhook i Sanity sitt dashbord. URLen dette webhooket skal peke på er `https://uiogaming.no/api/revalidate` med _POST_ forespørselstype.
Du må også spesifisere URL for webhook i Sanity sitt dashbord. Du kan lese mer om det [her](https://www.sanity.io/docs/webhooks). Denne URLen skal peke på APIet ditt.
### Docker
Docker er for øyeblikket ikke støttet, men vil støttes fremtiden.
Du kan lese mer om det [her](https://www.sanity.io/docs/webhooks).
## Vil du bidra?
+4
View File
@@ -0,0 +1,4 @@
NEXT_PUBLIC_SANITY_PROJECT_ID=mmqlu667
NEXT_PUBLIC_SANITY_DATASET=production
SANITY_REVALIDATE_SECRET=your-super-secret-webhook-token-here
+16
View File
@@ -0,0 +1,16 @@
import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
});
const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
];
export default eslintConfig;
-51
View File
@@ -1,51 +0,0 @@
module.exports = {
siteMetadata: {
title: `UiO Gaming`,
description: `En hobbyforening som ønsker skape et sosialt miljø rundt gaming og e-sport på UiO!`,
author: `@uiogaming`,
url: `https://uiogaming.no`,
},
plugins: [
`gatsby-plugin-react-helmet`,
{
resolve: "gatsby-plugin-manifest",
options: {
icon: "src/images/logo_2024_neon_pink.png",
},
},
`gatsby-plugin-image`,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/images`,
},
},
// Removed locale and i18n plugins
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
{
resolve: `gatsby-source-sanity`,
options: {
projectId: `mmqlu667`,
dataset: `production`,
watchMode: true,
},
},
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `UiO Gaming`,
short_name: `uiogaming.no`,
start_url: `/`,
background_color: `#1D1320`,
theme_color: `#7D36E7`,
display: `minimal-ui`,
icon: `src/images/logo_2024_neon_pink.png`, // This path is relative to the root of the site.
},
},
// this (optional) plugin enables Progressive Web App + Offline functionality
// To learn more, visit: https://gatsby.dev/offline
// `gatsby-plugin-offline`,
],
}
-53
View File
@@ -1,53 +0,0 @@
const path = require("path")
module.exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const blogTemplate = path.resolve("./src/pages/blogPost.js")
const eventTemplate = path.resolve("./src/pages/eventPost.js")
const result = await graphql(`
query {
allSanityPost {
nodes {
_id
slug {
current
}
}
}
allSanityEvent {
nodes {
_id
slug {
current
}
}
}
}
`)
const posts = result.data.allSanityPost.nodes
posts.forEach(post => {
const path = `/blog/${post.slug.current}`
createPage({
path,
component: blogTemplate,
context: {
id: post._id,
},
})
})
const events = result.data.allSanityEvent.nodes
events.forEach(event => {
const path = `/event/${event.slug.current}`
createPage({
path,
component: eventTemplate,
context: {
id: event._id,
},
})
})
}
Vendored
+4
View File
@@ -0,0 +1,4 @@
declare module "*.module.css" {
const classes: { readonly [key: string]: string }
export = classes
}
+16
View File
@@ -0,0 +1,16 @@
import type { NextConfig } from "next"
const nextConfig: NextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "cdn.sanity.io",
port: "",
pathname: "/images/**",
},
],
},
}
export default nextConfig
+8200
View File
File diff suppressed because it is too large. Load diff
+26 -29
View File
@@ -1,40 +1,37 @@
{
"name": "uiogaming.no",
"version": "1.0.0",
"private": true,
"description": "UiO Gaming Website",
"version": "0.2.0",
"author": "Leander Furumo <leander@furumo.eu>",
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "next start -p 9000",
"lint": "next lint",
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\""
},
"dependencies": {
"@sanity/block-content-to-react": "^3.0.0",
"country-flag-icons": "^1.5.13",
"gatsby": "^5.14.0",
"gatsby-plugin-image": "^3.14.0",
"gatsby-plugin-manifest": "^5.14.0",
"gatsby-plugin-react-helmet": "^6.14.0",
"gatsby-plugin-sharp": "^5.14.0",
"gatsby-source-filesystem": "^5.14.0",
"gatsby-source-sanity": "^7.9.1",
"gatsby-transformer-sharp": "^5.14.0",
"path": "^0.12.7",
"path-browserify": "^1.0.1",
"prop-types": "^15.8.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-helmet": "^6.1.0",
"react-icons": "^5.3.0",
"react-pannellum": "^0.2.16"
"@portabletext/react": "^3.2.1",
"@sanity/client": "^7.8.1",
"@sanity/image-url": "^1.1.0",
"install": "^0.13.0",
"next": "15.4.4",
"npm": "^11.5.2",
"react": "19.1.0",
"react-dom": "19.1.0",
"react-icons": "^5.5.0",
"react-photo-sphere-viewer": "^6.2.3"
},
"devDependencies": {
"prettier": "3.3.3"
},
"scripts": {
"build": "gatsby build",
"develop": "gatsby develop",
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"",
"start": "npm run develop",
"serve": "gatsby serve",
"clean": "gatsby clean",
"test": "echo \"Write tests! -> https://gatsby.dev/unit-testing\" && exit 1"
"@eslint/eslintrc": "^3",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "15.4.4",
"prettier": "^3.6.2",
"typescript": "^5"
},
"repository": {
"type": "git",
File renamed without changes.

Before

Width:  |  Height:  |  Size: 5.7 MiB

After

Width:  |  Height:  |  Size: 5.7 MiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 5.9 MiB

After

Width:  |  Height:  |  Size: 5.9 MiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 18 MiB

After

Width:  |  Height:  |  Size: 18 MiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 679 KiB

After

Width:  |  Height:  |  Size: 679 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 696 KiB

After

Width:  |  Height:  |  Size: 696 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 112 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 115 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 164 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

+43
View File
@@ -0,0 +1,43 @@
import { revalidateTag } from "next/cache"
import { NextRequest, NextResponse } from "next/server"
export async function POST(request: NextRequest) {
const secret = request.nextUrl.searchParams.get("secret")
if (secret !== process.env.SANITY_REVALIDATE_SECRET) {
return NextResponse.json({ message: "Invalid secret" }, { status: 401 })
}
try {
const body = await request.json()
const { _type, slug } = body
console.log(
`Revalidating content: ${_type}${slug ? ` (${slug.current})` : ""}`
)
if (_type === "post") {
revalidateTag("blog-posts")
if (slug?.current) {
revalidateTag(`post-${slug.current}`)
}
} else if (_type === "event") {
revalidateTag("events")
if (slug?.current) {
revalidateTag(`event-${slug.current}`)
}
}
revalidateTag("home")
return NextResponse.json({
revalidated: true,
now: Date.now(),
type: _type,
slug: slug?.current,
})
} catch (err) {
console.error("Error revalidating:", err)
return NextResponse.json({ message: "Error revalidating" }, { status: 500 })
}
}
@@ -9,6 +9,7 @@
.article {
display: grid;
place-items: center;
margin-bottom: 2rem;
}
.header {
display: grid;
@@ -21,6 +22,9 @@
.headerImage {
padding: 0;
margin: 1rem 0;
width: 100%;
height: auto;
object-fit: contain;
max-width: 100%;
max-height: 80vh;
place-self: center;
@@ -53,6 +57,7 @@
display: grid;
gap: 2em;
}
.content {
display: grid;
gap: 1rem;
@@ -60,11 +65,6 @@
padding: 0 1rem;
}
/* Block content styling */
/* .content {
padding-inline-start: 1rem;
border-inline-start: 8px solid var(--main-color);
} */
.content blockquote {
color: rgb(225, 225, 225);
font-weight: 100;
@@ -77,18 +77,9 @@
.content h2,
.content h3,
.content h4 {
margin-top: 3rem;
margin-top: 2rem;
}
/* .content blockquote::before {
content: '"';
font-size: 3rem;
}
.content blockquote::after {
content: '"';
font-size: 3rem;
} */
@media (min-width: 60em) {
.content {
padding: 0;
+159
View File
@@ -0,0 +1,159 @@
import Layout from "@/app/layout"
import Footer from "@/components/page-sections/footer"
import { generateSeoMetadata, generateSeoViewport } from "@/components/seo"
import Back from "@/components/ui/back"
import { urlFor } from "@/lib/imageUrlBuilder"
import { getBlogPost } from "@/lib/sanity"
import { PortableText, PortableTextComponents } from "@portabletext/react"
import { Metadata, Viewport } from "next"
import Image from "next/image"
import * as styles from "./page.module.css"
interface BlogPageParams {
params: Promise<{
slug: string
}>
}
interface ImageValue {
asset?: {
_id: string
url: string
}
alt?: string
caption?: string
}
interface LinkValue {
href: string
}
export async function generateMetadata({
params,
}: BlogPageParams): Promise<Metadata> {
const { slug } = await params
const data = await getBlogPost(slug)
if (!data) {
return {
title: "Blog Post Not Found | UiO Gaming",
}
}
return generateSeoMetadata({
title: data.title,
description: data.excerpt,
imageURL: data.mainImage?.asset?.url,
author: data.author?.name,
})
}
export function generateViewport(): Viewport {
return generateSeoViewport()
}
const components: PortableTextComponents = {
types: {
image: ({ value }: { value: ImageValue }) => {
if (!value?.asset) return null
return (
<div className={styles.imageContainer}>
<Image
src={urlFor(value).url()}
alt={value.alt || "Blog image"}
width={800}
height={400}
style={{ width: "100%", height: "auto" }}
/>
{value.caption && (
<p className={styles.imageCaption}>{value.caption}</p>
)}
</div>
)
},
},
marks: {
link: ({
children,
value,
}: {
children?: React.ReactNode
value?: LinkValue
}) => (
<a
href={value?.href ?? "#"}
target="_blank"
rel="noopener noreferrer"
className={styles.link}
>
{children}
</a>
),
},
}
const BlogPost = async ({ params }: BlogPageParams) => {
const { slug }: { slug: string } = await params
const data = await getBlogPost(slug)
if (!data) {
throw new Error("Blog post not found")
}
const createdAt = new Date(data._createdAt).toLocaleDateString("nb-NO", {
day: "numeric",
month: "long",
year: "numeric",
})
return (
<Layout>
<div className={styles.container}>
<div className={styles.backButton}>
<Back />
</div>
<article className={styles.article}>
<header className={styles.header}>
{data.mainImage && (
<Image
src={urlFor(data.mainImage).url()}
alt={data.title}
width={800}
height={400}
className={styles.headerImage}
/>
)}
<h1 className={styles.title}>{data.title}</h1>
<p className={styles.excerpt}>{data.excerpt}</p>
<div className={styles.author}>
{data.author?.image && (
<Image
className="circular"
alt={`Forfatteren av innlegget, ${data.author.name}`}
src={urlFor(data.author.image).width(50).height(50).url()}
width={50}
height={50}
/>
)}
<div className={styles.authorNameSection}>
<p>{data.author?.name}</p>
<p>{createdAt}</p>
</div>
</div>
</header>
<div className={styles.content}>
<PortableText value={data.body} components={components} />
</div>
</article>
</div>
<Footer />
</Layout>
)
}
export default BlogPost
@@ -1,12 +1,7 @@
body {
display: grid;
place-content: center;
min-height: none;
}
.container {
display: grid;
place-items: center;
place-content: center;
min-height: 100vh;
margin: 1rem;
}
@@ -37,4 +32,5 @@ body {
.description {
margin-top: 1rem;
white-space: pre-wrap;
}
+96
View File
@@ -0,0 +1,96 @@
import { generateSeoMetadata, generateSeoViewport } from "@/components/seo"
import Back from "@/components/ui/back"
import { getEvent } from "@/lib/sanity"
import { Metadata, Viewport } from "next"
import { FaCalendarAlt, FaMapMarkerAlt } from "react-icons/fa"
import * as styles from "./page.module.css"
interface EventPageParams {
params: Promise<{
slug: string
}>
}
export async function generateMetadata({
params,
}: EventPageParams): Promise<Metadata> {
const { slug } = await params
const data = await getEvent(slug)
if (!data) {
return {
title: "Event Not Found | UiO Gaming",
}
}
const formatDate = (dateString: string) => {
const date = new Date(dateString)
const options: Intl.DateTimeFormatOptions = {
weekday: "long",
month: "long",
year: "numeric",
day: "numeric",
hour: "numeric",
minute: "2-digit",
timeZone: "Europe/Oslo",
}
return date.toLocaleString("no", options)
}
return generateSeoMetadata({
title: data.title,
description: `Tid: ${formatDate(data.date)}\nSted: ${data.location}\n\n${data.description}`,
})
}
export function generateViewport(): Viewport {
return generateSeoViewport()
}
const Event = async ({ params }: EventPageParams) => {
const { slug }: { slug: string } = await params
const data = await getEvent(slug)
if (!data) {
throw new Error("Event not found")
}
const formatDate = (dateString: string) => {
const date = new Date(dateString)
const options: Intl.DateTimeFormatOptions = {
weekday: "long",
month: "long",
year: "numeric",
day: "numeric",
hour: "numeric",
minute: "2-digit",
timeZone: "Europe/Oslo",
}
return date.toLocaleString("no", options)
}
return (
<div className={styles.container}>
<Back />
<div className={styles.event}>
<h1>{data.title}</h1>
<div className={styles.metadata}>
<FaMapMarkerAlt />
<p>{data.location}</p>
</div>
<div className={styles.metadata}>
<FaCalendarAlt />
<p>{formatDate(data.date)}</p>
</div>
<p className={styles.description}>{data.description}</p>
</div>
</div>
)
}
export default Event
@@ -3,6 +3,7 @@
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 1rem;
}
+40
View File
@@ -0,0 +1,40 @@
import Layout from "@/app/layout"
import { generateSeoMetadata, generateSeoViewport } from "@/components/seo"
import { Metadata, Viewport } from "next"
import Link from "next/link"
import * as styles from "./global-not-found.module.css"
export function generateMetadata(): Metadata {
return generateSeoMetadata({
title: "404: Ikke funnet",
description: "Uh oh :(",
})
}
export function generateViewport(): Viewport {
return generateSeoViewport()
}
const NotFoundPage = () => {
return (
<Layout>
<div className={styles.container}>
<h1 className={styles.centeredText}>404: Siden ble ikke funnet</h1>
<p className={styles.centeredText}>
Reisen vår endte her. Vennligst ikke forlat oss
<span role="img" aria-label="eyes">
👀
</span>
</p>
<Link href="/" className={`${styles.centeredText} ${styles.homeLink}`}>
Tilbake til trygg grunn
</Link>
</div>
</Layout>
)
}
export default NotFoundPage
+15 -13
View File
@@ -1,16 +1,18 @@
@import url("https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,400;0,900;1,100;1,400;1,900&display=swap");
/* ===== Variables ===== */
:root {
--main-color: #7D36E7;
--main-background-color: #1D1320;
--secondary-background-color: #261F31;
--main-color: #7d36e7;
--main-background-color: #1d1320;
--secondary-background-color: #261f31;
--footer-color: rgb(12, 12, 15);
--header-color: rgb(255, 255, 255);
--text-color: #EEEEEE;
--light-text-color: #CCCCCC;
--link-color: #7D36E7;
--link-color-hover: #D1B3FF;
--text-color: #eeeeee;
--light-text-color: #cccccc;
--link-color: #7d36e7;
--link-color-hover: #d1b3ff;
--main-font: "Nunito", sans-serif;
@@ -90,8 +92,6 @@ select {
/* ===== Base styling ===== */
@import url("https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,400;0,900;1,100;1,400;1,900&display=swap");
::-webkit-scrollbar {
width: 0.5em;
}
@@ -161,6 +161,7 @@ section {
padding: 7em 1em;
display: grid;
justify-content: center;
border-radius: 1rem;
}
section:nth-child(2n + 1) {
background-color: var(--secondary-background-color);
@@ -173,9 +174,9 @@ section h2 {
text-align: center;
justify-self: center;
}
@media (min-width: 60em) {
@media (min-width: 70em) {
section {
padding: 10em 5em;
padding: 10em 4em;
}
}
@@ -219,11 +220,12 @@ section h2 {
mask-image: radial-gradient(white, black);
-webkit-mask-image: -webkit-radial-gradient(white, black);
}
.photo-credit, .photo-credit:hover {
.photo-credit,
.photo-credit:hover {
color: var(--light-text-color);
text-align: end;
}
@media (min-width: 60em) {
@media (min-width: 70em) {
.no-mobile {
display: block;
}
+28
View File
@@ -0,0 +1,28 @@
import "@/app/globals.css"
import type { Metadata } from "next"
export const metadata: Metadata = {
title: "UiO Gaming",
description:
"En hobbyforening som ønsker skape et sosialt miljø rundt gaming og e-sport på UiO!",
icons: {
icon: "/logo_2024_neon_pink.png",
shortcut: "/logo_2024_neon_pink.png",
apple: "/logo_2024_neon_pink.png",
},
}
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode
}>) {
return (
<html lang="no">
<body>
<main>{children}</main>
</body>
</html>
)
}
+36
View File
@@ -0,0 +1,36 @@
import Layout from "@/app/layout"
import About from "@/components/page-sections/about"
import BoardMembers from "@/components/page-sections/boardMembers"
import Contact from "@/components/page-sections/contact"
import Events from "@/components/page-sections/events"
import Footer from "@/components/page-sections/footer"
import Hero from "@/components/page-sections/hero"
import Office from "@/components/page-sections/office"
import { generateSeoMetadata, generateSeoViewport } from "@/components/seo"
import { Metadata, Viewport } from "next"
export const revalidate = 3600 // 1 hour
export function generateMetadata(): Metadata {
return generateSeoMetadata({})
}
export function generateViewport(): Viewport {
return generateSeoViewport()
}
export default function Home() {
return (
<Layout>
<Hero />
<About />
<Events />
<Office />
<BoardMembers />
<Contact />
<Footer />
</Layout>
)
}
-20
View File
@@ -1,20 +0,0 @@
import * as React from "react"
import * as styles from "./back.module.css"
import { graphql } from "gatsby"
import { FaArrowLeft } from "react-icons/fa"
const Back = () => {
return (
<button
className={`${styles.container} ${styles.button}`}
onClick={() => window.history.back()}
>
<FaArrowLeft />
<p>Tilbake</p>
</button>
)
}
export default Back
@@ -1,16 +0,0 @@
.button {
display: flex;
justify-content: center;
font-size: 1.6rem;
background-color: var(--main-color);
padding: 0.5em 1em;
border-radius: 999em;
color: var(--text-color);
}
.button:hover {
color: white;
background-color: #A76CFF;
}
@@ -1,70 +0,0 @@
.button {
display: flex;
align-items: center;
gap: 0.5em;
justify-content: center;
font-size: 1.6rem;
background-color: #cccccc;
width: 100%;
padding: 0.5em 1em;
border: 0;
border-radius: 999em;
color: black;
}
.button:hover {
cursor: pointer;
color: black;
background-color: #e6e6e6;
}
.dropdown {
display: flex;
flex-direction: column;
position: absolute;
border-radius: 10px;
width: 300px;
background-color: rgba(0,0,0,0,0.5);
}
.dropdownButton {
width: 100%;
color: black;
background-color: #cccccc;
border: none;
border-radius: 999em !important;
padding: 8px 16px;
cursor: pointer;
outline: none;
border-radius: 10px;
text-align: left;
font-size: 1.6rem;
box-shadow: var(--shadow);
padding: 0.5em 1em;
}
.dropdownButton:hover {
color: black;
background-color: #e6e6e6;
}
.arrowUp, .arrowDown {
border: solid black;
border-width: 0 2px 2px 0;
display: inline-block;
padding: 3px;
margin-left: 5px;
transition: transform 0.1s ease-in-out;
}
.arrowDown {
transform: rotate(45deg);
-webkit-transform: rotate(45deg);
}
.arrowUp {
transform: rotate(-135deg);
-webkit-transform: rotate(-135deg);
}
-16
View File
@@ -1,16 +0,0 @@
import * as React from "react"
import PropTypes from "prop-types"
const Layout = ({ children }) => {
return (
<>
<main>{children}</main>
</>
)
}
Layout.propTypes = {
children: PropTypes.node.isRequired,
}
export default Layout
@@ -4,16 +4,11 @@
gap: 10rem;
}
.textContent {
display: grid;
place-items: center;
}
.joinSection {
display: grid;
grid-template-rows: auto auto;
place-items: center;
margin: 3.5rem 0;
margin-top: 3.5rem;
font-size: 2rem;
}
.joinSection h3 {
@@ -24,8 +19,19 @@
flex-direction: column;
gap: 1rem;
}
.image {
height: 100%;
flex-direction: column;
align-items: stretch;
justify-content: center;
}
.image img {
border-radius: 1rem;
object-fit: cover;
height: 80%;
}
@media (min-width: 60em) {
@media (min-width: 70em) {
.container {
grid-template-columns: 1fr 1fr;
gap: 3rem;
@@ -36,4 +42,8 @@
.buttons {
flex-direction: row;
}
.image {
display: flex;
place-self: center;
}
}
@@ -1,35 +1,21 @@
import * as React from "react"
import DiscordButton from "@components/ui/discordButton"
import MemberButton from "@components/ui/memberButton"
import OtherButton from "@components/ui/otherButton"
import Image from "next/image"
import * as styles from "./about.module.css"
import { graphql, useStaticQuery } from "gatsby"
import { GatsbyImage } from "gatsby-plugin-image"
import DiscordButton from "../items/discordButton"
import MemberButton from "../items/memberButton"
import OtherButton from "../items/otherButton"
const About = () => {
const data = useStaticQuery(graphql`
query {
aboutImage: file(relativePath: { eq: "about.jpg" }) {
childImageSharp {
gatsbyImageData(
placeholder: BLURRED
transformOptions: { fit: CONTAIN }
width: 650
)
}
}
}
`)
return (
<section>
<div className={styles.container}>
<div className="no-mobile">
<GatsbyImage
image={data.aboutImage.childImageSharp.gatsbyImageData}
<div className={"no-mobile " + styles.image}>
<Image
src="/about.jpg"
alt="To medlemmer spiller sammen"
width={1000}
height={433}
/>
<p className="photo-credit">Foto: 장태민, Studentenes Fotoklubb</p>
</div>
@@ -3,7 +3,7 @@
flex-wrap: wrap;
align-items: stretch;
justify-content: space-evenly;
gap: 7rem;
gap: 4rem;
word-break: break-word;
}
@@ -39,11 +39,12 @@
}
.headerImageContainer {
border: 5px solid white;
display: inline-block;
}
/* .headerImage {
box-shadow: inset 0 0 10px #000000;
} */
.headerImageContainer img {
object-fit: cover;
height: auto;
width: 100%;
}
.info {
border-bottom: var(--card-section-divider);
@@ -73,7 +74,7 @@
word-wrap: break-word;
}
@media (min-width: 60em) {
@media (min-width: 70em) {
.header {
flex-direction: row;
}
@@ -0,0 +1,97 @@
import { urlFor } from "@/lib/imageUrlBuilder"
import { sanityClient } from "@/lib/sanity"
import Image from "next/image"
import * as styles from "./boardMembers.module.css"
interface BoardMember {
_id: string
boardMember: boolean
contactInfo?: string
name: string
role?: {
no: string
en: string
}
image?: {
asset?: {
_id: string
url: string
}
}
bio?: {
no: string
en: string
}
}
const BoardMembers = async () => {
const query = `
*[_type == "author" && boardMember == true] | order(orderRank asc) {
_id,
boardMember,
contactInfo,
name,
role {
no,
en
},
image {
asset->{
_id,
url
}
},
bio {
no,
en
}
}
`
const data: BoardMember[] = await sanityClient.fetch(query)
return (
<section>
<h2>Styret</h2>
<div className={styles.container}>
{data.map((member: BoardMember) => (
<article key={member._id} className={styles.card}>
<div className={styles.header}>
<div
className={`circular-container ${styles.headerImageContainer}`}
>
<Image
src={urlFor(member.image?.asset?.url || "/person.png")
.width(512)
.height(512)
.url()}
width={256}
height={256}
className="circular"
alt={`Bilde av styremedlem, ${member.name}`}
/>
</div>
<div className={styles.info}>
<h3>{member.name}</h3>
<p>{member.role?.no}</p>
</div>
</div>
<p>{member.bio?.no}</p>
<div className={styles.footer}>
<p className="text-center">
{member.contactInfo ? (
member.contactInfo
) : (
<i>Ingen kontaktinfo</i>
)}
</p>
</div>
</article>
))}
</div>
</section>
)
}
export default BoardMembers
@@ -1,15 +1,14 @@
import * as React from "react"
import * as styles from "./contact.module.css"
import {
FaYoutube,
FaTwitch,
FaAt,
FaDiscord,
FaFacebook,
FaGithub,
FaDiscord,
FaAt,
FaInstagram,
FaTiktok,
FaTwitch,
FaYoutube,
} from "react-icons/fa"
const Contact = () => {
@@ -5,7 +5,7 @@
.container {
display: grid;
place-items: center;
gap: 6em;
gap: 4em;
grid-template-rows: auto 1fr;
}
.container > h2 {
@@ -23,7 +23,7 @@
gap: 2rem;
}
.events > *,
*:hover {
.events > *:hover {
color: white;
}
.events > h2 {
@@ -59,7 +59,7 @@
grid-template-columns: auto 1fr;
align-items: center;
font-weight: 100;
color: var(--light-text-color)
color: var(--light-text-color);
}
.metadata > *:hover {
color: var(--light-text-color);
@@ -67,8 +67,11 @@
.image {
grid-row: 1 / -1;
height: 75%;
place-self: center;
margin-top: 2em;
}
@media (min-width: 60em) {
@media (min-width: 70em) {
.stretch {
justify-content: center;
}
@@ -76,12 +79,6 @@
grid-template-columns: 1fr 1fr;
gap: 3rem;
}
.container > h2 {
margin-bottom: 1em;
}
.sectionHeader {
margin-bottom: 0.5em;
}
.card {
padding: 2em;
}
+101
View File
@@ -0,0 +1,101 @@
import { getAllEvents } from "@/lib/sanity"
import Image from "next/image"
import Link from "next/link"
import { FaCalendarAlt, FaMapMarkerAlt } from "react-icons/fa"
import * as styles from "./events.module.css"
interface Event {
_id: string
title: string
location: string
date: string
slug: {
current: string
}
description: string
}
const Events = async () => {
const data: Event[] = await getAllEvents()
const currentDate = new Date()
const filteredEvents = data
.filter((event: Event) => {
return currentDate <= new Date(event.date)
})
.slice(0, 2)
const formatDate = (dateString: string) => {
const date = new Date(dateString)
const options: Intl.DateTimeFormatOptions = {
weekday: "long",
month: "long",
year: "numeric",
day: "numeric",
hour: "numeric",
minute: "2-digit",
timeZone: "Europe/Oslo",
}
return date.toLocaleString("no", options)
}
const mapEvents = (events: Event[]) => {
if (events.length !== 0) {
return events.map((event: Event) => (
<Link key={event._id} href={"/event/" + event.slug.current}>
<article className={styles.card}>
<h3>{event.title}</h3>
<div>
<div className={styles.metadata}>
<FaMapMarkerAlt />
<p>{event.location}</p>
</div>
<div className={styles.metadata}>
<FaCalendarAlt />
<p>{formatDate(event.date)}</p>
</div>
</div>
<p>
{event.description.length < 70 ? (
event.description
) : (
<>
{event.description.substring(0, 70)}...{" "}
<span className={styles.readMore}>Les mer</span>
</>
)}
</p>
</article>
</Link>
))
} else {
return <h3 className={styles.noEvents}>Ingen kommende arrangementer</h3>
}
}
return (
<section>
<div className={styles.container}>
<h2 className={styles.sectionHeader}>Hva skjer?</h2>
<div className={styles.events}>{mapEvents(filteredEvents)}</div>
<div className={"no-mobile " + styles.image}>
<Image
src="/event.jpg"
width={1000}
height={400}
style={{ borderRadius: "1rem", objectFit: "cover", height: "100%" }}
alt="Medlemmer spiller brettspill"
/>
<div>
<p className="photo-credit">Foto: 장태민, Studentenes Fotoklubb</p>
</div>
</div>
</div>
</section>
)
}
export default Events
@@ -1,15 +1,9 @@
footer {
.footer {
background-color: var(--footer-color);
padding: 10rem 3rem;
display: grid;
place-items: center;
}
.footer {
display: grid;
place-items: center;
gap: 2rem;
grid-template-rows: auto;
display: grid;
place-items: center;
}
.orgInfo {
@@ -1,33 +1,17 @@
import * as React from "react"
import Image from "next/image"
import * as styles from "./footer.module.css"
import { graphql, useStaticQuery } from "gatsby"
import { GatsbyImage } from "gatsby-plugin-image"
const Footer = () => {
const data = useStaticQuery(graphql`
query {
logo: file(relativePath: { eq: "logo_2024_neon_trans.png" }) {
childImageSharp {
gatsbyImageData(
placeholder: BLURRED
transformOptions: { fit: COVER }
width: 256
)
}
}
}
`)
return (
<footer>
<div className={styles.footer}>
<div>
<GatsbyImage
image={data.logo.childImageSharp.gatsbyImageData}
alt="UiO Gaming logo"
/>
</div>
<Image
src="/logo_2024_neon_trans.png"
alt="UiO Gaming logo"
width={187}
height={256}
/>
<div className={styles.links}>
<ul className="remove-bullets">
<li>
@@ -1,6 +1,6 @@
.container {
background-color: #7C5CA8;
background-image: url("../../images/mustafa_blurred.png"); /* Placeholder image */
background-color: #7c5ca8;
background-image: url("/mustafa.png");
background-repeat: no-repeat;
background-size: cover;
@@ -8,24 +8,36 @@
padding: 1rem;
padding-bottom: 10vh;
height: 100vh;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
max-width: none;
text-align: center;
}
.content {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
gap: 1em;
max-width: none;
text-align: center;
background-color: rgba(0, 0, 0, 0.2);
backdrop-filter: blur(10px);
padding: 2em;
margin-block: 2.5em;
border-radius: 1rem;
}
.container > p {
.content > p {
max-width: 40ch;
color: #130729;
font-size: 1.5rem;
}
.container > h1 {
.content > h1 {
color: #130729;
font-size: 4rem;
}
+24
View File
@@ -0,0 +1,24 @@
import Image from "next/image"
import * as styles from "./hero.module.css"
const Hero = () => {
return (
<header id="hjem" className={styles.container}>
<div className={styles.content}>
<Image
src="/logo_2024_black_trans.png"
alt="UiO Gaming logo"
width={187}
height={256}
/>
<p>
En hobbyforening som ønsker å skape et sosialt miljø rundt gaming og
e-sport på UiO
</p>
</div>
</header>
)
}
export default Hero
@@ -0,0 +1,25 @@
.container {
display: grid;
gap: 2rem;
justify-content: center;
place-items: center;
}
.image {
width: 100%;
aspect-ratio: 1 / 1;
}
.panoramaContainer {
width: 100%;
height: 400px;
background: #000000;
border-radius: 8px;
overflow: hidden;
position: relative;
}
.panoramaContainer > div {
width: 100% !important;
height: 100% !important;
}
@@ -1,12 +1,9 @@
import * as React from "react"
"use client"
import { ReactPhotoSphereViewer } from "react-photo-sphere-viewer"
import * as styles from "./office.module.css"
import ReactPannellum from "react-pannellum"
const config = {
autoLoad: true,
}
const Office = () => {
return (
<section>
@@ -21,13 +18,23 @@ const Office = () => {
Tips: Du kan kikke rundt i kontoret ved å klikke og dra på bildet
nedenfor 👀
</p>
<ReactPannellum
id="1"
sceneId="office"
config={config}
imageSource="/3D_office_2024_11_03.jpg"
style={{ width: "100%", aspectRatio: "1 / 1", background: "#000000" }}
/>
<div
style={{
width: "100%",
height: "500px",
background: "#000000",
borderRadius: "8px",
overflow: "hidden",
}}
>
<ReactPhotoSphereViewer
src="/3D_office_2024_11_03.jpg"
height="100%"
width="100%"
defaultZoomLvl={0}
navbar={true}
/>
</div>
</div>
</section>
)
-77
View File
@@ -1,77 +0,0 @@
import * as React from "react"
import * as styles from "./boardMembers.module.css"
import { graphql, useStaticQuery } from "gatsby"
import { GatsbyImage } from "gatsby-plugin-image"
const BoardMembers = () => {
const data = useStaticQuery(graphql`
query {
allSanityAuthor(
filter: { boardMember: { eq: true } }
sort: { orderRank: ASC }
) {
edges {
node {
_id
boardMember
contactInfo
name
role {
no
en
}
image {
asset {
gatsbyImageData(width: 256, height: 256)
}
}
bio {
no
en
}
}
}
}
}
`)
return (
<section>
<h2>Styret</h2>
<div className={styles.container}>
{data.allSanityAuthor.edges.map(({ node: member }) => (
<article key={member._id} className={styles.card}>
<div className={styles.header}>
<div
className={`circular-container ${styles.headerImageContainer}`}
>
<GatsbyImage
image={member.image.asset.gatsbyImageData}
className="circular"
alt={`Bilde av styremedlem, ` + member.name}
/>
</div>
<div className={styles.info}>
<h3>{member.name}</h3>
<p>{member.role.no}</p>
</div>
</div>
<p>{member.bio.no}</p>
<div className={styles.footer}>
<p className="text-center">
{member.contactInfo ? (
member.contactInfo
) : (
<i>Ingen kontaktinfo</i>
)}
</p>
</div>
</article>
))}
</div>
</section>
)
}
export default BoardMembers
-109
View File
@@ -1,109 +0,0 @@
import * as React from "react"
import * as styles from "./events.module.css"
import { graphql, useStaticQuery } from "gatsby"
import { GatsbyImage } from "gatsby-plugin-image"
import { Link } from "gatsby"
import { FaMapMarkerAlt, FaCalendarAlt } from "react-icons/fa"
const Events = () => {
const data = useStaticQuery(graphql`
query {
eventImage: file(relativePath: { eq: "event.jpg" }) {
childImageSharp {
gatsbyImageData(
placeholder: BLURRED
transformOptions: { fit: COVER }
width: 650
)
}
}
allSanityEvent(sort: { date: ASC }) {
edges {
node {
_id
title
location
date
slug {
current
}
description
}
}
}
}
`)
const currentDate = new Date()
const filteredEvents = data.allSanityEvent.edges
.filter(({ node: event }) => {
return currentDate <= new Date(event.date)
})
.slice(0, 2)
const mapEvents = events => {
if (events.length !== 0) {
return events.map(({ node: event }) => (
<Link key={event._id} to={"/event/" + event.slug.current}>
<article className={styles.card}>
<h3>{event.title}</h3>
<div>
<div className={styles.metadata}>
<FaMapMarkerAlt />
<p>{event.location}</p>
</div>
<div className={styles.metadata}>
<FaCalendarAlt />
<p>
{new Date(event.date).toLocaleString("no", {
weekday: "long",
month: "long",
year: "numeric",
day: "numeric",
hour: "numeric",
minute: "2-digit",
timeZone: "Europe/Oslo",
})}
</p>
</div>
</div>
<p>
{event.description.length < 120 ? (
event.description
) : (
<>
{event.description.substring(0, 120)}...{" "}
<span className={styles.readMore}>Les mer</span>
</>
)}
</p>
</article>
</Link>
))
} else {
return <h3 className={styles.noEvents}>Ingen kommende arrangementer</h3>
}
}
return (
<section className={styles.stretch}>
<div className={styles.container}>
<h2 className={styles.sectionHeader}>Hva skjer?</h2>
<div className={styles.events}>{mapEvents(filteredEvents)}</div>
<div className={"no-mobile " + styles.image}>
<GatsbyImage
image={data.eventImage.childImageSharp.gatsbyImageData}
alt="Medlemmer spiller brettspill"
/>
<div>
<p className="photo-credit">Foto: 장태민, Studentenes Fotoklubb</p>
</div>
</div>
</div>
</section>
)
}
export default Events
-36
View File
@@ -1,36 +0,0 @@
import * as React from "react"
import * as styles from "./header.module.css"
import { graphql, useStaticQuery } from "gatsby"
import { GatsbyImage } from "gatsby-plugin-image"
const Header = () => {
const data = useStaticQuery(graphql`
query {
placeholder: file(relativePath: { eq: "logo_2024_black_trans.png" }) {
childImageSharp {
gatsbyImageData(
placeholder: BLURRED
transformOptions: { fit: CONTAIN }
width: 256
)
}
}
}
`)
return (
<header id="hjem" className={styles.container}>
<GatsbyImage
image={data.placeholder.childImageSharp.gatsbyImageData}
alt="UiO Gaming logo"
/>
<p>
En hobbyforening som ønsker å skape et sosialt miljø rundt gaming og
e-sport på UiO
</p>
</header>
)
}
export default Header
-11
View File
@@ -1,11 +0,0 @@
.container {
display: grid;
gap: 2rem;
justify-content: center;
place-items: center;
}
.image {
width: 100%;
aspect-ratio: 1 / 1;
}
-154
View File
@@ -1,154 +0,0 @@
import * as React from "react"
import PropTypes from "prop-types"
import { Helmet } from "react-helmet"
import { useStaticQuery, graphql } from "gatsby"
function Seo({ description, lang, meta, imageURL, title, author }) {
const { site } = useStaticQuery(graphql`
query {
site {
siteMetadata {
title
description
author
url
}
}
}
`)
const metaAuthor = author || site.siteMetadata.author
const metaDescription = description || site.siteMetadata.description
const defaultTitle = site.siteMetadata?.title
const jsonLd = {
"@context": "https://schema.org",
"@type": "Organization",
logo: `${site.siteMetadata.url}/logo_2024_neon_trans.png`,
url: site.siteMetadata.url,
sameAs: [
"https://www.instagram.com/uio_gaming",
"https://www.facebook.com/profile.php?id=100066853901827",
"https://www.twitch.tv/uio_gaming",
"https://www.youtube.com/channel/UCZhUhWT_q0oZGk0rCeov51Q",
"https://github.com/UiO-Gaming",
],
name: site.siteMetadata.title,
description:
"UiO Gaming har som formål å fremme gaming, e-sport, brettspill og andre spillrelaterte interesser/kulturer ved UiO. I tillegg ønsker vi å skape et sosialt miljø som legger til rette for all aktivitet rundt spill. Foreningen har som mål å arrangere spillkvelder, turneringer, LAN og andre spillrelaterte aktiviteter åpne for alle som er interesserte.",
email: "styret@uiogaming.no",
address: {
"@type": "PostalAddress",
streetAddress: "Slemdalsveien 15",
addressLocality: "Oslo",
postalCode: "0369",
addressRegion: "Oslo",
addressCountry: "NO",
},
contactPoint: {
"@type": "ContactPoint",
email: "styret@uiogaming.no",
},
foundingDate: "2020-09-11",
}
return (
<Helmet
htmlAttributes={{
lang,
}}
title={title ? title : defaultTitle}
titleTemplate={title ? `%s | ${defaultTitle}` : defaultTitle}
meta={[
{
name: `author`,
content: metaAuthor,
},
{
name: `description`,
content: metaDescription,
},
{
property: `og:site_name`,
content: site.siteMetadata.title,
},
{
property: `og:title`,
content: title ? title : defaultTitle,
},
{
property: `og:description`,
content: metaDescription,
},
{
property: `og:type`,
content: `website`,
},
{
property: `og:locale`,
content: lang,
},
{
name: `twitter:creator`,
content: metaAuthor,
},
{
name: `twitter:title`,
content: title,
},
{
name: `twitter:description`,
content: metaDescription,
},
]
.concat(
imageURL
? [
{
property: "og:image",
content: imageURL,
},
{
name: "twitter:card",
content: "summary_large_image",
},
]
: [
{
property: "og:image",
content: `${site.siteMetadata.url}/logo_2024_neon_trans.png`,
},
{
name: "twitter:card",
content: "summary",
},
]
)
.concat(meta)}
>
<script type="application/ld+json">{JSON.stringify(jsonLd)}</script>
<script
defer
src="https://umami.uiogaming.no/script.js"
data-website-id="1e985053-9158-4d62-be07-0fe83a9dce6e"
></script>
</Helmet>
)
}
Seo.defaultProps = {
lang: `no`,
meta: [],
description: ``,
}
Seo.propTypes = {
description: PropTypes.string,
lang: PropTypes.string,
meta: PropTypes.arrayOf(PropTypes.object),
title: PropTypes.string,
}
export default Seo
+69
View File
@@ -0,0 +1,69 @@
import { Metadata, Viewport } from "next"
type SeoProps = {
title?: string
description?: string
imageURL?: string
author?: string
}
export function generateSeoMetadata({
title,
description,
imageURL,
author,
}: SeoProps): Metadata {
const defaultTitle = "UiO Gaming"
const metaDescription =
description ||
"En hobbyforening som ønsker skape et sosialt miljö rundt gaming og e-sport på UiO!"
const siteUrl = "https://uiogaming.no"
const themeColor = "#7d36e7"
return {
title: title ? `${title} | ${defaultTitle}` : defaultTitle,
description: metaDescription,
authors: author ? [{ name: author }] : [{ name: "UiO Gaming" }],
openGraph: {
title: title || defaultTitle,
description: metaDescription,
type: "website",
locale: "no",
siteName: defaultTitle,
images: imageURL
? [
{
url: imageURL,
width: 1200,
height: 630,
alt: title || defaultTitle,
},
]
: [
{
url: `${siteUrl}/logo_2024_neon_trans.png`,
alt: defaultTitle,
},
],
},
twitter: {
card: imageURL ? "summary_large_image" : "summary",
title: title || defaultTitle,
description: metaDescription,
images: imageURL ? [imageURL] : [`${siteUrl}/logo_2024_neon_trans.png`],
},
other: {
"theme-color": themeColor,
"msapplication-TileColor": themeColor,
"msapplication-navbutton-color": themeColor,
"apple-mobile-web-app-status-bar-style": "black-translucent",
},
}
}
export function generateSeoViewport(): Viewport {
return {
themeColor: "#7d36e7",
colorScheme: "dark",
}
}
File renamed without changes.
+33
View File
@@ -0,0 +1,33 @@
"use client"
import { FaArrowLeft } from "react-icons/fa"
import * as styles from "./back.module.css"
const Back = () => {
const handleBack = () => {
const currentDomain = window.location.hostname
const referrer = document.referrer
const isSameDomain =
referrer && new URL(referrer).hostname === currentDomain
if (window.history.length > 1 && isSameDomain) {
window.history.back()
} else {
window.location.href = "/"
}
}
return (
<button
className={`${styles.container} ${styles.button}`}
onClick={handleBack}
>
<FaArrowLeft />
<p>Tilbake</p>
</button>
)
}
export default Back
@@ -1,12 +1,12 @@
.button {
display: flex;
justify-content: center;
justify-content: center;
align-items: center;
font-size: 1.6rem;
background-color: #5865F2;
font-size: 1.6rem;
background-color: #5865f2;
padding: 0.5em 1em;
border-radius: 999em;
border-radius: 999em;
color: var(--text-color);
}
@@ -1,4 +1,3 @@
import * as React from "react"
import * as styles from "./discordButton.module.css"
const DiscordButton = () => (
+20
View File
@@ -0,0 +1,20 @@
.button {
display: flex;
justify-content: center;
align-items: center;
text-align: center;
font-size: 1.6rem;
background-color: var(--main-color);
text-overflow: ellipsis;
white-space: nowrap;
padding: 0.5em 1em;
border-radius: 999em;
color: var(--text-color);
}
.button:hover {
color: white;
background-color: #a76cff;
}
@@ -1,4 +1,3 @@
import * as React from "react"
import * as styles from "./memberButton.module.css"
const MemberButton = () => {
+71
View File
@@ -0,0 +1,71 @@
.button {
display: flex;
align-items: center;
gap: 0.5em;
justify-content: center;
font-size: 1.6rem;
background-color: #cccccc;
width: 100%;
padding: 0.5em 1em;
border: 0;
border-radius: 999em;
color: black;
}
.button:hover {
cursor: pointer;
color: black;
background-color: #e6e6e6;
}
.dropdown {
display: flex;
flex-direction: column;
position: absolute;
border-radius: 10px;
width: 300px;
background-color: rgba(0, 0, 0, 0, 0.5);
}
.dropdownButton {
width: 100%;
color: black;
background-color: #cccccc;
border: none;
border-radius: 999em !important;
padding: 8px 16px;
cursor: pointer;
outline: none;
border-radius: 10px;
text-align: left;
font-size: 1.6rem;
box-shadow: var(--shadow);
padding: 0.5em 1em;
}
.dropdownButton:hover {
color: black;
background-color: #e6e6e6;
}
.arrowUp,
.arrowDown {
border: solid black;
border-width: 0 2px 2px 0;
display: inline-block;
padding: 3px;
margin-left: 5px;
transition: transform 0.1s ease-in-out;
}
.arrowDown {
transform: rotate(45deg);
-webkit-transform: rotate(45deg);
}
.arrowUp {
transform: rotate(-135deg);
-webkit-transform: rotate(-135deg);
}
@@ -1,8 +1,9 @@
import * as React from "react"
import * as styles from "./otherButton.module.css"
"use client"
import { useState } from "react"
import * as styles from "./otherButton.module.css"
const OtherButton = () => {
const [isOpen, setIsOpen] = useState(false)
const toggleDropdown = () => setIsOpen(!isOpen)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 519 KiB

+10
View File
@@ -0,0 +1,10 @@
import imageUrlBuilder from "@sanity/image-url"
import type { SanityImageSource } from "@sanity/image-url/lib/types/types"
import { sanityClient } from "./sanity"
const builder = imageUrlBuilder(sanityClient)
export function urlFor(source: SanityImageSource) {
return builder.image(source)
}
+97
View File
@@ -0,0 +1,97 @@
import { createClient } from "@sanity/client"
export const sanityClient = createClient({
projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID!,
dataset: process.env.NEXT_PUBLIC_SANITY_DATASET!,
apiVersion: "2025-02-19",
useCdn: true,
})
export async function getBlogPost(slug: string) {
const query = `
*[_type == "post" && slug.current == $slug][0] {
_id,
_createdAt,
title,
author-> {
name,
image {
asset-> {
_id,
url
}
}
},
mainImage {
asset-> {
_id,
url
}
},
excerpt,
body
}
`
return sanityClient.fetch(
query,
{ slug },
{
cache: "force-cache",
next: {
tags: ["blog-posts", `post-${slug}`],
revalidate: 3600, // 1 hour
},
}
)
}
export async function getEvent(slug: string) {
const query = `
*[_type == "event" && slug.current == $slug][0] {
_id,
title,
location,
date,
slug,
description
}
`
return sanityClient.fetch(
query,
{ slug },
{
cache: "force-cache",
next: {
tags: ["events", `event-${slug}`],
revalidate: 3600, // 1 hour
},
}
)
}
export async function getAllEvents() {
const query = `
*[_type == "event"] | order(date asc) {
_id,
title,
location,
date,
slug,
description
}
`
return sanityClient.fetch(
query,
{},
{
cache: "force-cache",
next: {
tags: ["events"],
revalidate: 1800, // 30 minutes
},
}
)
}
-5
View File
@@ -1,5 +0,0 @@
{
"title": "404: Siden finnes ikke",
"text": "Du har nådd en blindvei, men ikke på Blindern",
"back": "Tilbake til hjemmesiden"
}
-6
View File
@@ -1,6 +0,0 @@
{
"altText.about": "2 medlemmer som spiller sammen",
"altText.events": "Medlemmer som spiller brettspill",
"altText.boardMember": "Bilde av styremedlem",
"altText.flag": "Britisk flagg"
}
-6
View File
@@ -1,6 +0,0 @@
{
"back": "Tilbake",
"join": "Bli medlem",
"other": "Annet",
"photo": "Foto"
}
-22
View File
@@ -1,22 +0,0 @@
{
"header.text": "En hobbyforening som ønsker å skape et sosialt miljø rundt gaming og e-sport på UiO",
"about.title": "Hvem er vi?",
"about.text": "UiO Gaming er en forening med hovedfokus på å danne et inkluderende fellesskap rundt videospill og e-sport. Hos oss er det enkelt å skaffe seg venner, ettersom alle har felles interesser. Vi arrangerer ukentlige spillkvelder og fysiske sosiale arrangementer, som for eksempel brettspillkvelder og sosiale sammenkomster. Vår hovedkanal er en aktiv Discord-server hvor man kan møte nye folk med felles interesser og game med andre studenter.",
"about.interested": "Interessert?",
"events.title": "Hva skjer?",
"events.readMore": "Les mer",
"events.noEvents": "Ingen kommende arrangementer",
"office.title": "Kontoret",
"office.text": "Vi har et foreningskontor på Chateau Neuf der man kan bruke utstyret vårt og henge med andre medlemmer! Vi har blant annet alle de nyeste konsollene, 6 gaming-PCer samt et bredt utvalg av brettspill.",
"office.tip": "Tips: Du kan kikke rundt i kontoret ved å klikke og dra på bildet nedenfor 👀",
"boardMembers.title": "Styret",
"boardMembers.noContact": "Ingen kontaktinfo",
"contact.title": "Finn oss",
"contact.email": "E-post",
"footer.statutes": "Vedtekter",
"footer.complaints": "Klage",
"footer.sourcecode": "Kildekode",
"footer.admin": "Adminområdet",
"footer.orgnum": "Org.Nr.",
"documents": "Dokumenter"
}
-5
View File
@@ -1,5 +0,0 @@
{
"longDescription": "UiO Gaming sitt formål er å fremme gaming, brettspill og andre spillrelaterte interesser/kulturer ved UiO. I tillegg til dette vil vi lage et sosialt miljø som tilrettelegger alle former for aktivitet rundt spill. Foreningen har derfor som mål å arrangere spillkvelder, turneringer, LAN og andre spillrelaterte aktiviteter åpent for alle som er interesserte.",
"location": "Sted",
"time": "Tidspunkt"
}
-30
View File
@@ -1,30 +0,0 @@
import * as React from "react"
import * as styles from "./404.module.css"
import { graphql } from "gatsby"
import { Link } from "gatsby"
import Layout from "../components/layout"
import Seo from "../components/seo"
const NotFoundPage = () => {
return (
<Layout>
<Seo title="404: Ikke funnet" description="Uh oh :(" lang="no" />
<div className={styles.container}>
<h1 className={styles.centeredText}>404: Siden ble ikke funnet</h1>
<p className={styles.centeredText}>
Reisen vår endte her. Vennligst ikke forlat oss
<span role="img" aria-label="eyes">
👀
</span>
</p>
<Link to="/" className={`${styles.centeredText} ${styles.homeLink}`}>
Tilbake til trygg grunn
</Link>
</div>
</Layout>
)
}
export default NotFoundPage
-79
View File
@@ -1,79 +0,0 @@
import * as React from "react"
import * as styles from "./blogPost.module.css"
import { graphql } from "gatsby"
import { GatsbyImage } from "gatsby-plugin-image"
import Seo from "../components/seo"
import BlockContent from "@sanity/block-content-to-react"
import Back from "../components/items/back"
export const query = graphql`
query($id: String) {
sanityPost(_id: { eq: $id }) {
_createdAt(formatString: "DD. MMMM, YYYY", locale: "nb_NO")
title
author {
name
image {
asset {
gatsbyImageData(width: 50, height: 50)
}
}
}
mainImage {
asset {
url
gatsbyImageData
}
}
excerpt
_rawBody(resolveReferences: { maxDepth: 10 })
}
}
`
const BlogPost = ({ data }) => {
return (
<>
<Seo
title={data.sanityPost.title}
imageURL={data.sanityPost.mainImage.asset.url}
description={data.sanityPost.excerpt}
author={data.sanityPost.author.name}
/>
<div className={styles.container}>
<div className={styles.backButton}>
<Back />
</div>
<article className={styles.article}>
<header className={styles.header}>
<GatsbyImage
image={data.sanityPost.mainImage.asset.gatsbyImageData}
className={styles.headerImage}
/>
<h1 className={styles.title}>{data.sanityPost.title}</h1>
<p className={styles.excerpt}>{data.sanityPost.excerpt}</p>
<div className={styles.author}>
<GatsbyImage
className="circular"
alt={"Forfatteren av innlegget, " + data.sanityPost.author.name}
image={data.sanityPost.author.image.asset.gatsbyImageData}
/>
<div className={styles.authorNameSection}>
<p>{data.sanityPost.author.name}</p>
<p>{data.sanityPost._createdAt}</p>
</div>
</div>
</header>
<BlockContent
className={styles.content}
blocks={data.sanityPost._rawBody}
/>
</article>
</div>
</>
)
}
export default BlogPost
-72
View File
@@ -1,72 +0,0 @@
import * as React from "react"
import * as styles from "./eventPost.module.css"
import { graphql } from "gatsby"
// Removed: import { useTranslation } from "gatsby-plugin-react-i18next"
import Seo from "../components/seo"
import Back from "../components/items/back"
import { FaMapMarkerAlt, FaCalendarAlt } from "react-icons/fa"
export const query = graphql`
query ($id: String) {
sanityEvent(_id: { eq: $id }) {
title
location
date
slug {
current
}
description
}
}
`
const Event = ({ data }) => {
const time = new Date(data.sanityEvent.date).toLocaleString("no", {
weekday: "long",
month: "long",
year: "numeric",
day: "numeric",
hour: "numeric",
minute: "2-digit",
timeZone: "Europe/Oslo",
})
return (
<>
<Seo
title={data.sanityEvent.title}
description={`Tid: ${time}\nSted: ${data.sanityEvent.location}\n\n${data.sanityEvent.description}`}
lang="no"
/>
<div className={styles.container}>
<Back />
<div className={styles.event}>
<h1>{data.sanityEvent.title}</h1>
<div className={styles.metadata}>
<FaMapMarkerAlt />
<p>{data.sanityEvent.location}</p>
</div>
<div className={styles.metadata}>
<FaCalendarAlt />
<p>
{new Date(data.sanityEvent.date).toLocaleString("no", {
weekday: "long",
month: "long",
year: "numeric",
day: "numeric",
hour: "numeric",
minute: "2-digit",
timeZone: "Europe/Oslo",
})}
</p>
</div>
<p className={styles.description}>{data.sanityEvent.description}</p>
</div>
</div>
</>
)
}
export default Event
-33
View File
@@ -1,33 +0,0 @@
import * as React from "react"
import { graphql } from "gatsby"
import "../index.css"
import Layout from "../components/layout"
import Seo from "../components/seo"
import Header from "../components/sections/header"
import About from "../components/sections/about"
import Events from "../components/sections/events"
import Office from "../components/sections/office"
import BoardMembers from "../components/sections/boardMembers"
import Contact from "../components/sections/contact"
import Footer from "../components/sections/footer"
const IndexPage = () => {
return (
<>
<Layout>
<Seo lang="no" />
<Header />
<About />
<Events />
<Office />
<BoardMembers />
<Contact />
<Footer />
</Layout>
</>
)
}
export default IndexPage
Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

+35
View File
@@ -0,0 +1,35 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"],
"@components/*": ["./src/components/*"],
"@lib/*": ["./src/lib/*"]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
"global.d.ts"
],
"exclude": ["node_modules"]
}
-38
View File
@@ -1,38 +0,0 @@
const express = require("express")
const cors = require("cors")
const { exec } = require("child_process")
const app = express()
const frontendURL = process.env.FRONTEND_URL || "https://uiogaming.no"
const port = process.env.PORT || 9001
app.use(cors({ origin: frontendURL }))
const RateLimit = require("express-rate-limit");
const limiter = RateLimit({
windowMs: 60 * 1000, // 1 minute
max: 2
})
app.use(limiter)
app.get("/", (req, res) => {
res.send("API is running! Yeet!")
})
app.post("/build", (req, res) => {
exec("cd ../ && gatsby build", (error, stdout, stderr) => {
if (error) {
console.log(stderr.message)
console.log(error.message)
return
}
console.log(stdout)
})
res.send("Success!")
})
app.listen(port, () => {
console.log(`LIVE! http://0.0.0.0:${port}`)
})
-18
View File
@@ -1,18 +0,0 @@
{
"name": "webhook-api",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"cors": "^2.8.5",
"express": "^4.17.1",
"express-rate-limit": "^7.5.0"
}
}
-518
View File
@@ -1,518 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
accepts@~1.3.8:
version "1.3.8"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
dependencies:
mime-types "~2.1.34"
negotiator "0.6.3"
array-flatten@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
body-parser@1.20.3:
version "1.20.3"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6"
integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==
dependencies:
bytes "3.1.2"
content-type "~1.0.5"
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
http-errors "2.0.0"
iconv-lite "0.4.24"
on-finished "2.4.1"
qs "6.13.0"
raw-body "2.5.2"
type-is "~1.6.18"
unpipe "1.0.0"
bytes@3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
call-bind-apply-helpers@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz#32e5892e6361b29b0b545ba6f7763378daca2840"
integrity sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==
dependencies:
es-errors "^1.3.0"
function-bind "^1.1.2"
call-bound@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.3.tgz#41cfd032b593e39176a71533ab4f384aa04fd681"
integrity sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==
dependencies:
call-bind-apply-helpers "^1.0.1"
get-intrinsic "^1.2.6"
content-disposition@0.5.4:
version "0.5.4"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
dependencies:
safe-buffer "5.2.1"
content-type@~1.0.4, content-type@~1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
cookie-signature@1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
cookie@0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9"
integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==
cors@^2.8.5:
version "2.8.5"
resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29"
integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==
dependencies:
object-assign "^4"
vary "^1"
debug@2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
depd@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
destroy@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
dunder-proto@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a"
integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
dependencies:
call-bind-apply-helpers "^1.0.1"
es-errors "^1.3.0"
gopd "^1.2.0"
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
encodeurl@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58"
integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==
es-define-property@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa"
integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
es-errors@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
es-object-atoms@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1"
integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==
dependencies:
es-errors "^1.3.0"
escape-html@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
etag@~1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
express-rate-limit@^7.5.0:
version "7.5.0"
resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-7.5.0.tgz#6a67990a724b4fbbc69119419feef50c51e8b28f"
integrity sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==
express@^4.17.1:
version "4.21.2"
resolved "https://registry.yarnpkg.com/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32"
integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==
dependencies:
accepts "~1.3.8"
array-flatten "1.1.1"
body-parser "1.20.3"
content-disposition "0.5.4"
content-type "~1.0.4"
cookie "0.7.1"
cookie-signature "1.0.6"
debug "2.6.9"
depd "2.0.0"
encodeurl "~2.0.0"
escape-html "~1.0.3"
etag "~1.8.1"
finalhandler "1.3.1"
fresh "0.5.2"
http-errors "2.0.0"
merge-descriptors "1.0.3"
methods "~1.1.2"
on-finished "2.4.1"
parseurl "~1.3.3"
path-to-regexp "0.1.12"
proxy-addr "~2.0.7"
qs "6.13.0"
range-parser "~1.2.1"
safe-buffer "5.2.1"
send "0.19.0"
serve-static "1.16.2"
setprototypeof "1.2.0"
statuses "2.0.1"
type-is "~1.6.18"
utils-merge "1.0.1"
vary "~1.1.2"
finalhandler@1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019"
integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==
dependencies:
debug "2.6.9"
encodeurl "~2.0.0"
escape-html "~1.0.3"
on-finished "2.4.1"
parseurl "~1.3.3"
statuses "2.0.1"
unpipe "~1.0.0"
forwarded@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
fresh@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
function-bind@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
get-intrinsic@^1.2.5, get-intrinsic@^1.2.6:
version "1.2.7"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.7.tgz#dcfcb33d3272e15f445d15124bc0a216189b9044"
integrity sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==
dependencies:
call-bind-apply-helpers "^1.0.1"
es-define-property "^1.0.1"
es-errors "^1.3.0"
es-object-atoms "^1.0.0"
function-bind "^1.1.2"
get-proto "^1.0.0"
gopd "^1.2.0"
has-symbols "^1.1.0"
hasown "^2.0.2"
math-intrinsics "^1.1.0"
get-proto@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1"
integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
dependencies:
dunder-proto "^1.0.1"
es-object-atoms "^1.0.0"
gopd@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
has-symbols@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338"
integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
hasown@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
dependencies:
function-bind "^1.1.2"
http-errors@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
dependencies:
depd "2.0.0"
inherits "2.0.4"
setprototypeof "1.2.0"
statuses "2.0.1"
toidentifier "1.0.1"
iconv-lite@0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
dependencies:
safer-buffer ">= 2.1.2 < 3"
inherits@2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
ipaddr.js@1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
math-intrinsics@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9"
integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
merge-descriptors@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5"
integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==
methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
mime-db@1.52.0:
version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@~2.1.24, mime-types@~2.1.34:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
mime@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
ms@2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
negotiator@0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
object-assign@^4:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
object-inspect@^1.13.3:
version "1.13.4"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213"
integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==
on-finished@2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
dependencies:
ee-first "1.1.1"
parseurl@~1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
path-to-regexp@0.1.12:
version "0.1.12"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7"
integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==
proxy-addr@~2.0.7:
version "2.0.7"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
dependencies:
forwarded "0.2.0"
ipaddr.js "1.9.1"
qs@6.13.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==
dependencies:
side-channel "^1.0.6"
range-parser@~1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
raw-body@2.5.2:
version "2.5.2"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a"
integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==
dependencies:
bytes "3.1.2"
http-errors "2.0.0"
iconv-lite "0.4.24"
unpipe "1.0.0"
safe-buffer@5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
"safer-buffer@>= 2.1.2 < 3":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
send@0.19.0:
version "0.19.0"
resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8"
integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==
dependencies:
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
fresh "0.5.2"
http-errors "2.0.0"
mime "1.6.0"
ms "2.1.3"
on-finished "2.4.1"
range-parser "~1.2.1"
statuses "2.0.1"
serve-static@1.16.2:
version "1.16.2"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296"
integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==
dependencies:
encodeurl "~2.0.0"
escape-html "~1.0.3"
parseurl "~1.3.3"
send "0.19.0"
setprototypeof@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
side-channel-list@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad"
integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==
dependencies:
es-errors "^1.3.0"
object-inspect "^1.13.3"
side-channel-map@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42"
integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==
dependencies:
call-bound "^1.0.2"
es-errors "^1.3.0"
get-intrinsic "^1.2.5"
object-inspect "^1.13.3"
side-channel-weakmap@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea"
integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==
dependencies:
call-bound "^1.0.2"
es-errors "^1.3.0"
get-intrinsic "^1.2.5"
object-inspect "^1.13.3"
side-channel-map "^1.0.1"
side-channel@^1.0.6:
version "1.1.0"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9"
integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==
dependencies:
es-errors "^1.3.0"
object-inspect "^1.13.3"
side-channel-list "^1.0.0"
side-channel-map "^1.0.1"
side-channel-weakmap "^1.0.2"
statuses@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
toidentifier@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
type-is@~1.6.18:
version "1.6.18"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
dependencies:
media-typer "0.3.0"
mime-types "~2.1.24"
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
utils-merge@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
vary@^1, vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
-10418
View File
File diff suppressed because it is too large. Load diff