Content Migration from websites repo
3
.eslintrc.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": "next/core-web-vitals"
|
||||||
|
}
|
18
.gitignore
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# do not track installed modules
|
||||||
|
/node_modules
|
||||||
|
|
||||||
|
# do not track built files
|
||||||
|
/.next
|
||||||
|
*.tsbuildinfo
|
||||||
|
next-env.d.ts
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
/data
|
||||||
|
/confs
|
50
Dockerfile
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
## INIT STEP
|
||||||
|
# Install dependencies only when needed
|
||||||
|
FROM node:16-alpine AS deps
|
||||||
|
|
||||||
|
RUN apk add --no-cache libc6-compat
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy the files needed to install deps
|
||||||
|
COPY package.json yarn.lock ./
|
||||||
|
RUN yarn install --frozen-lockfile
|
||||||
|
|
||||||
|
## BUILD STEP
|
||||||
|
# Rebuild the source code only when needed
|
||||||
|
FROM node:16-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy node_modules installed by the deps step
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN yarn build
|
||||||
|
|
||||||
|
## RUN STEP
|
||||||
|
FROM node:16-alpine AS runner
|
||||||
|
|
||||||
|
LABEL author="neshura@proton.me"
|
||||||
|
WORKDIR /usr/src/ap
|
||||||
|
|
||||||
|
ENV NODE_ENV production
|
||||||
|
|
||||||
|
RUN addgroup --system --gid 1001 nodejs
|
||||||
|
RUN adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
|
COPY --from=builder /app/public ./public
|
||||||
|
COPY --from=builder /app/node_modules ./node_modules
|
||||||
|
|
||||||
|
# Automatically leverage output traces to reduce image size
|
||||||
|
# https://nextjs.org/docs/advanced-features/output-file-tracing
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||||
|
|
||||||
|
USER nextjs
|
||||||
|
|
||||||
|
# expose port 3000
|
||||||
|
ENV PORT 3000
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
CMD [ "yarn", "start" ]
|
|
@ -1 +0,0 @@
|
||||||
# readyornot
|
|
37
components/layout.tsx
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import Script from 'next/script';
|
||||||
|
import React from 'react';
|
||||||
|
import Sidebar from './sidebar';
|
||||||
|
import styles from '/styles/ReadyOrNot.module.css';
|
||||||
|
|
||||||
|
const LayoutReadyOrNot = ({ children }: { children: React.ReactNode }) => {
|
||||||
|
return (
|
||||||
|
<div className={styles.page}>
|
||||||
|
<Script id="matomo_analytics">
|
||||||
|
{`
|
||||||
|
var _paq = window._paq = window._paq || [];
|
||||||
|
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
|
||||||
|
_paq.push(["setDocumentTitle", document.domain + "/" + document.title]);
|
||||||
|
_paq.push(["setCookieDomain", "readyornot.neshura-server.net"]);
|
||||||
|
_paq.push(["disableCookies"]);
|
||||||
|
_paq.push(['trackPageView']);
|
||||||
|
_paq.push(['enableLinkTracking']);
|
||||||
|
(function() {
|
||||||
|
var u="//temp.neshura-server.net/";
|
||||||
|
_paq.push(['setTrackerUrl', u+'matomo.php']);
|
||||||
|
_paq.push(['setSiteId', '2']);
|
||||||
|
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||||
|
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
|
||||||
|
})();
|
||||||
|
`}
|
||||||
|
</Script>
|
||||||
|
|
||||||
|
<Sidebar />
|
||||||
|
<div className={styles.sidebarPlaceholder}></div>
|
||||||
|
<main className={styles.main}>
|
||||||
|
{children}
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LayoutReadyOrNot;
|
60
components/sidebar.tsx
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
import styles from '/styles/ReadyOrNot.module.css'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import useSWR from 'swr';
|
||||||
|
import ReadyOrNotMap from '../interfaces/ReadyOrNot';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import Image from 'next/image'
|
||||||
|
|
||||||
|
const fetcher = (url: string) => fetch(url).then((res) => res.json())
|
||||||
|
|
||||||
|
function stopPropagation(e: any) {
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const Sidebar = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const [active, setActive] = useState(true);
|
||||||
|
const { maps, isLoading, isError } = useNavbar();
|
||||||
|
|
||||||
|
if (isError) { return (<div><nav><a>Error loading Sidemenu</a></nav></div>) }
|
||||||
|
else if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<nav>
|
||||||
|
<a>Loading...</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// > is a placeholder
|
||||||
|
return (
|
||||||
|
<div className={styles.sidebar} onClick={() => setActive(!active)}>
|
||||||
|
<nav className={[styles.sidebarList, (active ? styles.sl_active : styles.sl_inactive)].join(" ")}>
|
||||||
|
{maps.map((item: ReadyOrNotMap) => (
|
||||||
|
<Link key={item.name} href={item.href}>
|
||||||
|
<a className={[styles.navElem, (router.query.map == item.href ? styles.ne_active: styles.ne_inactive)].join(" ")} onClick={stopPropagation}>{item.name}</a>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
<div className={styles.sidebarArrow}>
|
||||||
|
<Image src="/sidebar_arrow.webp" width={32} height={96} alt=">"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function useNavbar() {
|
||||||
|
const { data, error } = useSWR(`/api/navbar`, fetcher)
|
||||||
|
|
||||||
|
return {
|
||||||
|
maps: data,
|
||||||
|
isLoading: !error && !data,
|
||||||
|
isError: error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Sidebar;
|
6
interfaces/ReadyOrNot.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
export default interface ReadyOrNotMap {
|
||||||
|
name: string,
|
||||||
|
alias: string,
|
||||||
|
href: string,
|
||||||
|
floors: number[]
|
||||||
|
}
|
8
next.config.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/** @type {import('next').NextConfig} */
|
||||||
|
|
||||||
|
const nextConfig = {
|
||||||
|
reactStrictMode: true,
|
||||||
|
output: 'standalone',
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = nextConfig
|
5908
package-lock.json
generated
Normal file
31
package.json
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
"name": "www",
|
||||||
|
"version": "0.2.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev:debug": "NODE_OPTIONS='--inspect' next dev -p 4042",
|
||||||
|
"dev": "next dev",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start",
|
||||||
|
"lint": "next lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@types/dockerode": "^3.3.14",
|
||||||
|
"@types/next": "^9.0.0",
|
||||||
|
"dockerode": "^3.3.4",
|
||||||
|
"next": "^12.3.0",
|
||||||
|
"node": "^18.9.0",
|
||||||
|
"node-html-parser": "^5.3.3",
|
||||||
|
"react": "18.2.0",
|
||||||
|
"react-dom": "18.2.0",
|
||||||
|
"sharp": "^0.31.2",
|
||||||
|
"swr": "^1.3.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^18.7.18",
|
||||||
|
"@types/react": "^18.0.14",
|
||||||
|
"eslint": "^8.23.1",
|
||||||
|
"eslint-config-next": "12.2.0",
|
||||||
|
"typescript": "^4.7.4"
|
||||||
|
}
|
||||||
|
}
|
179
pages/[map].tsx
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
import Head from 'next/head'
|
||||||
|
import { ReactElement, useEffect, useState } from 'react'
|
||||||
|
import LayoutReadyOrNot from '../components/layout'
|
||||||
|
import styles from '/styles/ReadyOrNot.module.css'
|
||||||
|
import { NextPageWithLayout } from './_app';
|
||||||
|
import React from 'react';
|
||||||
|
import useSWR, { KeyedMutator, mutate } from 'swr';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
import Image from 'next/image'
|
||||||
|
import ReadyOrNotMap from '../interfaces/ReadyOrNot';
|
||||||
|
|
||||||
|
const fetcher = (url: string) => fetch(url).then((res) => res.json())
|
||||||
|
|
||||||
|
function useWindowSize() {
|
||||||
|
const [windowSize, setWindowSize] = useState({
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function handleResize() {
|
||||||
|
setWindowSize({
|
||||||
|
width: window.innerWidth,
|
||||||
|
height: window.innerHeight,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("resize", handleResize);
|
||||||
|
|
||||||
|
handleResize();
|
||||||
|
|
||||||
|
return () => window.removeEventListener("resize", handleResize);
|
||||||
|
}, []);
|
||||||
|
return windowSize.width <= 1080;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ReadyOrNotMaps: NextPageWithLayout = () => {
|
||||||
|
const [floor, setFloor] = useState(0);
|
||||||
|
const { mapInfo, isLoadingInfo, isErrorInfo } = useMap(useRouter().query.map?.toString() || "a_lethal_obsession")
|
||||||
|
const { mapImages, isLoadingImages, isErrorImages, mutateImage } = useImages(useRouter().query.map?.toString() || "a_lethal_obsession")
|
||||||
|
const isMobile = useWindowSize();
|
||||||
|
|
||||||
|
let infoHtml: ReactElement = <></>;
|
||||||
|
let mapHtml: ReactElement = <></>;
|
||||||
|
if (isErrorInfo) {
|
||||||
|
infoHtml = (
|
||||||
|
<h1 className={styles.title}>
|
||||||
|
<a>Error loading page Data</a>
|
||||||
|
</h1>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else if (isLoadingInfo) {
|
||||||
|
infoHtml = (
|
||||||
|
<h1 className={styles.title}>
|
||||||
|
<a>Loading...</a>
|
||||||
|
</h1>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const info = mapInfo
|
||||||
|
infoHtml = (
|
||||||
|
<h1 className={styles.title}>
|
||||||
|
<a>{info.name} ({info.alias})</a>
|
||||||
|
</h1>
|
||||||
|
)
|
||||||
|
|
||||||
|
if (isErrorImages) {
|
||||||
|
mapHtml = <div className={styles.mapContainer}><p>Error loading Map or Map not present</p></div>
|
||||||
|
}
|
||||||
|
else if (isLoadingImages) {
|
||||||
|
mapHtml = <div className={styles.mapContainer}><p>Loading...</p></div>
|
||||||
|
}
|
||||||
|
else if(info.floors){
|
||||||
|
// TODO: ask Yahia if he would rather the buttons turn off at the corresponding limits
|
||||||
|
// move to highest floor after going down from lowest
|
||||||
|
if (floor < info.floors[0]) {
|
||||||
|
setFloor(info.floors[info.floors.length-1])
|
||||||
|
}
|
||||||
|
// move to lowest floor after going up from highest
|
||||||
|
if (floor > info.floors[info.floors.length-1]) {
|
||||||
|
setFloor(info.floors[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// map the images to the corresponding floor number in an array
|
||||||
|
let images: string[] = [];
|
||||||
|
mapImages.forEach((image, index) => {
|
||||||
|
// assign the floor number of the image position, since floors and images are synced from the info object this works
|
||||||
|
images[info.floors[index]] = image
|
||||||
|
});
|
||||||
|
|
||||||
|
let inverseFloors = info.floors.slice(0);
|
||||||
|
inverseFloors.reverse()
|
||||||
|
|
||||||
|
mapHtml = (
|
||||||
|
<div className={[styles.mapContainer, (isMobile ? styles.mc_mobile : styles.mc_desktop)].join(" ")}>
|
||||||
|
<div className={[
|
||||||
|
styles.floorIndicator,
|
||||||
|
(isMobile ? styles.findicator_mobile : styles.findicator_desktop)
|
||||||
|
].join(" ")}>
|
||||||
|
<p className={[
|
||||||
|
styles.floorNumber,
|
||||||
|
(isMobile ? styles.fn_mobile : styles.fn_desktop)
|
||||||
|
].join(" ")}>{floor}</p>
|
||||||
|
{inverseFloors.map((level: number, index: number) => (
|
||||||
|
<div key={level} className={[
|
||||||
|
styles.floorIcon,
|
||||||
|
(floor == level ? styles.fi_active : styles.fi_inactive),
|
||||||
|
(isMobile ? styles.fi_mobile : styles.fi_desktop)
|
||||||
|
].join(" ")} onClick={() => {setFloor(level)}}></div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className={[styles.map, (isMobile ? styles.map_mobile : styles.map_desktop)].join(" ")}>
|
||||||
|
<Image alt="Floorplan" src={images[floor]} layout="fill" objectFit='contain'></Image>
|
||||||
|
</div>
|
||||||
|
<div className={[
|
||||||
|
styles.floorSelection,
|
||||||
|
(isMobile ? styles.fs_mobile : styles.fs_desktop)
|
||||||
|
].join(" ")}>
|
||||||
|
<button className={[
|
||||||
|
styles.floor_button,
|
||||||
|
(isMobile ? styles.fb_mobile : "")
|
||||||
|
].join(" ")} onClick={() => setFloor(floor + 1)} >+</button>
|
||||||
|
<button className={[
|
||||||
|
styles.floor_button,
|
||||||
|
(isMobile ? styles.fb_mobile : "")
|
||||||
|
].join(" ")} onClick={() => setFloor(floor - 1)} >-</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mapHtml = <div className={styles.mapContainer}><p>Invalid or missing floor info: {info.floors}</p></div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: set background here depending on image loaded
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>Ready or Not</title>
|
||||||
|
<meta charSet='utf-8' />
|
||||||
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
</Head>
|
||||||
|
{infoHtml}
|
||||||
|
{mapHtml}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadyOrNotMaps.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return (
|
||||||
|
<LayoutReadyOrNot>
|
||||||
|
{page}
|
||||||
|
</LayoutReadyOrNot>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function useMap(path: string):{mapInfo: ReadyOrNotMap, isLoadingInfo: boolean, isErrorInfo: boolean} {
|
||||||
|
const { data, error } = useSWR(`/api/${path}`, fetcher)
|
||||||
|
|
||||||
|
return {
|
||||||
|
mapInfo: data,
|
||||||
|
isLoadingInfo: !error && !data,
|
||||||
|
isErrorInfo: error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function useImages(path: string):{mapImages: string[], isLoadingImages: boolean, isErrorImages: boolean, mutateImage: KeyedMutator<any>} {
|
||||||
|
const { data, error, mutate } = useSWR(`/api/${path}/maps`, fetcher)
|
||||||
|
|
||||||
|
return {
|
||||||
|
mapImages: data,
|
||||||
|
isLoadingImages: !error && !data,
|
||||||
|
isErrorImages: error,
|
||||||
|
mutateImage: mutate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ReadyOrNotMaps;
|
22
pages/_app.tsx
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import '/styles/globals.css'
|
||||||
|
import type { ReactElement, ReactNode } from 'react'
|
||||||
|
import Layout from '../components/layout'
|
||||||
|
import type { NextPage } from 'next'
|
||||||
|
import { AppProps } from 'next/app';
|
||||||
|
|
||||||
|
|
||||||
|
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
|
||||||
|
getLayout?: (page: ReactElement) => ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AppPropsWithLayout = AppProps & {
|
||||||
|
Component: NextPageWithLayout
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Website({ Component, pageProps }: AppPropsWithLayout) {
|
||||||
|
// Use the layout defined at the page level, if available
|
||||||
|
const getLayout = Component.getLayout ?? ((page) => (
|
||||||
|
<Layout>{page}</Layout>))
|
||||||
|
|
||||||
|
return getLayout(<Component {...pageProps} />)
|
||||||
|
}
|
16
pages/api/[map].tsx
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import fsPromises from 'fs/promises'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
export default async function MapApi(req: any, res: any) {
|
||||||
|
const { map } = req.query
|
||||||
|
try {
|
||||||
|
// get Empire and Game name first to create an EmpireData object
|
||||||
|
const filePathMap = path.join(process.cwd(), '/public/images/'+map+'/info.json');
|
||||||
|
const jsonMapData = await fsPromises.readFile(filePathMap);
|
||||||
|
res.status(200).send(jsonMapData.toString())
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
res.status(500).json({error: 'Error reading data'})
|
||||||
|
}
|
||||||
|
}
|
23
pages/api/[map]/maps.tsx
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import fsPromises from 'fs/promises'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
export default async function MapApi(req: any, res: any) {
|
||||||
|
const { map } = req.query
|
||||||
|
try {
|
||||||
|
// get list of all files(maps) in the readyornot folder - maybe there is a cleaner way to do this?
|
||||||
|
// we filter out any subdirectories as well as any files not ending in png or jpg
|
||||||
|
var fs = require('fs')
|
||||||
|
var maps = fs.readdirSync(path.join(process.cwd(), "/public/images/" + map + "/"), { withFileTypes: true })
|
||||||
|
.filter((file: any) => file.isFile())
|
||||||
|
.filter((file: any) => file.name.endsWith(".jpg") || file.name.endsWith(".png"))
|
||||||
|
.map((file: any) => '/images/' + map + '/' + file.name);
|
||||||
|
|
||||||
|
|
||||||
|
// get Empire and Game name first to create an EmpireData object
|
||||||
|
res.status(200).json(maps)
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
res.status(500).json({ error: 'Error reading data' })
|
||||||
|
}
|
||||||
|
}
|
33
pages/api/navbar.tsx
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
// jsut iterate through all relevant data serverside instead of dealing with it client side
|
||||||
|
import fsPromises from 'fs/promises'
|
||||||
|
import path from 'path'
|
||||||
|
import ReadyOrNotMap from '../../interfaces/ReadyOrNot'
|
||||||
|
|
||||||
|
export default async function TobarApi(req: any, res: any) {
|
||||||
|
try {
|
||||||
|
// get list of all folders(maps) in the readyornot folder - maybe there is a cleaner way to do this?
|
||||||
|
var fs = require('fs')
|
||||||
|
var mapList = fs.readdirSync(path.join(process.cwd(), '/public/images/'), { withFileTypes: true })
|
||||||
|
.filter((dirent:any) => dirent.isDirectory())
|
||||||
|
.map((dirent:any) => dirent.name);
|
||||||
|
|
||||||
|
// iterate through every map entry and extract the info
|
||||||
|
let maps: ReadyOrNotMap[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < mapList.length; i++) {
|
||||||
|
// get map data for the API request
|
||||||
|
const filePathEmpire = path.join(process.cwd(), '/public/images/' + mapList[i] + '/info.json');
|
||||||
|
const jsonDataEmpire = await fsPromises.readFile(filePathEmpire);
|
||||||
|
let mapData = JSON.parse(jsonDataEmpire.toString());
|
||||||
|
mapData.href = mapList[i];
|
||||||
|
|
||||||
|
maps.push(mapData) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(200).json(maps);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
res.status(500).json({ error: 'Error reading data' });
|
||||||
|
}
|
||||||
|
}
|
34
pages/index.tsx
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import Head from 'next/head'
|
||||||
|
import type { ReactElement } from 'react'
|
||||||
|
import LayoutReadyOrNot from '../components/layout'
|
||||||
|
import styles from '/styles/ReadyOrNot.module.css'
|
||||||
|
import { NextPageWithLayout } from './_app';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const ReadyOrNot: NextPageWithLayout = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>Ready or Not</title>
|
||||||
|
<meta charSet='utf-8' />
|
||||||
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
</Head>
|
||||||
|
<h1 className={styles.title}>
|
||||||
|
About
|
||||||
|
</h1>
|
||||||
|
<p className={styles.description}>
|
||||||
|
Ready or Not Floor Plan Application in the works
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadyOrNot.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return (
|
||||||
|
<LayoutReadyOrNot>
|
||||||
|
{page}
|
||||||
|
</LayoutReadyOrNot>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ReadyOrNot;
|
BIN
public/favicon.ico
Normal file
After Width: | Height: | Size: 1.2 KiB |
4
public/images/a_lethal_obsession/info.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"name": "Ridgeline",
|
||||||
|
"alias": "A Leathal Obsession"
|
||||||
|
}
|
4
public/images/buy_cheap_buy_twice/info.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"name": "Caesar's Cars Dealership",
|
||||||
|
"alias": "Buy Cheap, Buy Twice"
|
||||||
|
}
|
BIN
public/images/carriers_of_the_vine/0.jpg
Normal file
After Width: | Height: | Size: 496 KiB |
BIN
public/images/carriers_of_the_vine/1.jpg
Normal file
After Width: | Height: | Size: 432 KiB |
BIN
public/images/carriers_of_the_vine/2.jpg
Normal file
After Width: | Height: | Size: 567 KiB |
5
public/images/carriers_of_the_vine/info.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"name": "Cherryessa Farm",
|
||||||
|
"alias": "Carriers Of The Vine",
|
||||||
|
"floors": [0, 1, 2]
|
||||||
|
}
|
BIN
public/images/checkin_in/0.jpg
Normal file
After Width: | Height: | Size: 1.6 MiB |
BIN
public/images/checkin_in/1.png
Normal file
After Width: | Height: | Size: 106 KiB |
BIN
public/images/checkin_in/2.png
Normal file
After Width: | Height: | Size: 122 KiB |
5
public/images/checkin_in/info.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"name": "Wenderly Hills Hotel",
|
||||||
|
"alias": "Checkin' In",
|
||||||
|
"floors": [0, 1, 2]
|
||||||
|
}
|
4
public/images/crystal_and_lead/info.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"name": "Fast Food",
|
||||||
|
"alias": "Crystal And Lead"
|
||||||
|
}
|
4
public/images/hide_and_seek/info.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"name": "Port Hokan",
|
||||||
|
"alias": "Hide And Seek"
|
||||||
|
}
|
BIN
public/images/ides_of_march/0.png
Normal file
After Width: | Height: | Size: 147 KiB |
5
public/images/ides_of_march/info.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"name": "Brisa Cove",
|
||||||
|
"alias": "Ides Of March",
|
||||||
|
"floors": [0]
|
||||||
|
}
|
BIN
public/images/neon_tomb/0.png
Normal file
After Width: | Height: | Size: 113 KiB |
5
public/images/neon_tomb/info.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"name": "Neon Night Club",
|
||||||
|
"alias": "Neon Tomb",
|
||||||
|
"floors": [0]
|
||||||
|
}
|
4
public/images/relapse/info.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"name": "Hospital",
|
||||||
|
"alias": "Relapse"
|
||||||
|
}
|
4
public/images/test_level/info.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"name": "Hotel Rooms",
|
||||||
|
"alias": "Test Level"
|
||||||
|
}
|
BIN
public/images/thank_you_come_again/0.png
Normal file
After Width: | Height: | Size: 182 KiB |
5
public/images/thank_you_come_again/info.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"name": "4U Gas Station",
|
||||||
|
"alias": "Thank You, Come Again",
|
||||||
|
"floors": [0]
|
||||||
|
}
|
4
public/images/the_spider/info.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"name": "Agency",
|
||||||
|
"alias": "The Spider"
|
||||||
|
}
|
4
public/images/twisted_nerve/info.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"name": "213 Park Homes",
|
||||||
|
"alias": "Twisted Nerve"
|
||||||
|
}
|
BIN
public/images/valley_of_the_dolls/-1.png
Normal file
After Width: | Height: | Size: 104 KiB |
BIN
public/images/valley_of_the_dolls/0.png
Normal file
After Width: | Height: | Size: 94 KiB |
5
public/images/valley_of_the_dolls/info.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"name": "Voll Health House",
|
||||||
|
"alias": "Valley Of The Dolls",
|
||||||
|
"floors": [-1, 0]
|
||||||
|
}
|
BIN
public/sidebar_arrow.webp
Normal file
After Width: | Height: | Size: 532 B |
59
resources/sidebar_arrow.svg
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="32"
|
||||||
|
height="96"
|
||||||
|
viewBox="0 0 32 96"
|
||||||
|
version="1.1"
|
||||||
|
id="svg5"
|
||||||
|
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14, custom)"
|
||||||
|
sodipodi:docname="sidebar_arrow.svg"
|
||||||
|
inkscape:export-filename="sidebar_arrow.webp"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview7"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="false"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:zoom="11.313709"
|
||||||
|
inkscape:cx="-3.1819805"
|
||||||
|
inkscape:cy="35.974057"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1391"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
borderlayer="true"
|
||||||
|
showborder="true">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid1049"
|
||||||
|
originx="0"
|
||||||
|
originy="0" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<path
|
||||||
|
id="path1211"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 8,3.9708715 V 19.970872 c 3.449498,11.952732 9.599609,28.000391 9.599609,28.000391 L 8,75.970872 v 16 L 24,47.971263 C 18.666759,33.304433 13.333375,18.637654 8,3.9708715 Z"
|
||||||
|
sodipodi:nodetypes="ccccccc" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
BIN
resources/sidebar_arrow.webp
Normal file
After Width: | Height: | Size: 532 B |
249
styles/ReadyOrNot.module.css
Normal file
|
@ -0,0 +1,249 @@
|
||||||
|
.page {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
background-color: var(--background_black);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
color: var(--text_normal);
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 1rem;
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapContainer {
|
||||||
|
aspect-ratio: 1;
|
||||||
|
margin-top: 2rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mc_desktop {
|
||||||
|
height: 80%;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mc_mobile {
|
||||||
|
width: 90%;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map {
|
||||||
|
position: relative;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map_desktop {
|
||||||
|
height: 90%;
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map_mobile {
|
||||||
|
width: 90%;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floorSelection {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fs_desktop {
|
||||||
|
height: 100%;
|
||||||
|
align-items: left;
|
||||||
|
margin: 4%;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fs_mobile {
|
||||||
|
width: 100%;
|
||||||
|
align-items: center;
|
||||||
|
margin: 8%;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floor_button {
|
||||||
|
color: var(--text_normal);
|
||||||
|
background-color: var(--background_red);
|
||||||
|
border: 1px solid var(--button_border_normal);
|
||||||
|
min-width: 32px;
|
||||||
|
min-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fb_mobile {
|
||||||
|
margin: 0 5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floor_button:active {
|
||||||
|
border: 1px solid var(--button_border_pressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
.floorIndicator {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.findicator_desktop {
|
||||||
|
flex-direction: column;
|
||||||
|
margin-right: 2%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.findicator_mobile {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floorNumber {
|
||||||
|
font-size: 18pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fn_desktop {
|
||||||
|
margin: 2%;
|
||||||
|
margin-bottom: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fn_mobile {
|
||||||
|
margin-right: 4%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floorIcon {
|
||||||
|
background-clip: content-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fi_desktop {
|
||||||
|
min-height: 4px;
|
||||||
|
min-width: 40px;
|
||||||
|
width: 100%;
|
||||||
|
height: 1%;
|
||||||
|
margin: 8%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fi_mobile {
|
||||||
|
aspect-ratio: 0.5;
|
||||||
|
min-height: 40px;
|
||||||
|
min-width: 4px;
|
||||||
|
height: 25%;
|
||||||
|
margin: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fi_inactive {
|
||||||
|
background-color: var(--floor_selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fi_inactive:hover {
|
||||||
|
background-color: var(--red_light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fi_active {
|
||||||
|
background-color: var(--red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebarPlaceholder {
|
||||||
|
width: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
max-width: 100%;
|
||||||
|
min-width: 5%;
|
||||||
|
z-index: 100;
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebarArrow {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
width: 3%;
|
||||||
|
min-width: 10px;
|
||||||
|
background-color: var(--background_red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebarList {
|
||||||
|
overflow-y: scroll;
|
||||||
|
scrollbar-width: none;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 2rem;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: flex-start;
|
||||||
|
background-color: var(--background_grey_opaque);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar::-webkit-scrollbar {
|
||||||
|
width: 0px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sl_active {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sl_inactive {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navElem {
|
||||||
|
font-size: 14pt;
|
||||||
|
width: auto;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin: 0.4rem;
|
||||||
|
padding: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ne_inactive:hover {
|
||||||
|
color: var(--red_light);
|
||||||
|
background-color: var(--background_grey_opaque);
|
||||||
|
border-radius: 5px;
|
||||||
|
outline: 2px solid;
|
||||||
|
outline-offset: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ne_inactive {
|
||||||
|
color: var(--text_normal);
|
||||||
|
background-color: var(--background_grey);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ne_active {
|
||||||
|
color: var(--red);
|
||||||
|
outline: 2px solid;
|
||||||
|
outline-offset: -2px;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title a {
|
||||||
|
color: var(--red);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1.15;
|
||||||
|
font-size: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title,
|
||||||
|
.description {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
margin: 4rem 0;
|
||||||
|
line-height: 1.5;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
35
styles/globals.css
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
|
||||||
|
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
/* Ready or Not vars */
|
||||||
|
--background_black: #000;
|
||||||
|
--background_grey_opaque: #131313ee;
|
||||||
|
--background_grey: #333;
|
||||||
|
--background_red: #300;
|
||||||
|
|
||||||
|
--button_border_normal: #666;
|
||||||
|
--button_border_pressed: #333;
|
||||||
|
|
||||||
|
--floor_selector: #999;
|
||||||
|
|
||||||
|
--red: #600;
|
||||||
|
--red_light: #a00;
|
||||||
|
|
||||||
|
--text_normal: #ddd;
|
||||||
|
--text_dark: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
30
tsconfig.json
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"esnext"
|
||||||
|
],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"incremental": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "nodenext",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "preserve"
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"next-env.d.ts",
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
|
}
|