the big mega huge amazing nextjs migration
Code still trash though but we'll fix it someday Closes #48, #47, #37, #32
No files matched your search
@@ -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,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
|
||||
@@ -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?
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
NEXT_PUBLIC_SANITY_PROJECT_ID=mmqlu667
|
||||
NEXT_PUBLIC_SANITY_DATASET=production
|
||||
|
||||
SANITY_REVALIDATE_SECRET=your-super-secret-webhook-token-here
|
||||
@@ -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;
|
||||
@@ -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`,
|
||||
],
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
declare module "*.module.css" {
|
||||
const classes: { readonly [key: string]: string }
|
||||
export = classes
|
||||
}
|
||||
@@ -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
|
||||
@@ -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",
|
||||
|
||||
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,11 +0,0 @@
|
||||
.container {
|
||||
display: grid;
|
||||
gap: 2rem;
|
||||
justify-content: center;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 100%;
|
||||
aspect-ratio: 1 / 1;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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",
|
||||
}
|
||||
}
|
||||
@@ -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 = () => (
|
||||
@@ -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 = () => {
|
||||
@@ -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)
|
||||
|
Before Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 175 KiB |
|
Before Width: | Height: | Size: 519 KiB |
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"title": "404: Siden finnes ikke",
|
||||
"text": "Du har nådd en blindvei, men ikke på Blindern",
|
||||
"back": "Tilbake til hjemmesiden"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"back": "Tilbake",
|
||||
"join": "Bli medlem",
|
||||
"other": "Annet",
|
||||
"photo": "Foto"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
Before Width: | Height: | Size: 83 KiB |
@@ -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"]
|
||||
}
|
||||
@@ -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}`)
|
||||
})
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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==
|
||||