web folder (dont use this code)
This commit is contained in:
parent
fe6a88a479
commit
6adf9a8977
42
web/.gitignore
vendored
Normal file
42
web/.gitignore
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
bun.lockb
|
||||||
|
|
||||||
|
# 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*
|
||||||
|
|
||||||
|
# env files (can opt-in for committing if needed)
|
||||||
|
.env*
|
||||||
|
|
||||||
|
# vercel
|
||||||
|
.vercel
|
||||||
|
|
||||||
|
# typescript
|
||||||
|
*.tsbuildinfo
|
||||||
|
next-env.d.ts
|
0
web/README.md
Normal file
0
web/README.md
Normal file
7
web/next.config.ts
Normal file
7
web/next.config.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import type { NextConfig } from "next";
|
||||||
|
|
||||||
|
const nextConfig: NextConfig = {
|
||||||
|
/* config options here */
|
||||||
|
};
|
||||||
|
|
||||||
|
export default nextConfig;
|
27
web/package.json
Normal file
27
web/package.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "bhop-website",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next dev",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start",
|
||||||
|
"lint": "next lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@emotion/react": "^11.14.0",
|
||||||
|
"@emotion/styled": "^11.14.0",
|
||||||
|
"@mui/icons-material": "^6.1.10",
|
||||||
|
"@mui/material": "^6.1.10",
|
||||||
|
"next": "15.0.4",
|
||||||
|
"react": "^19.0.0",
|
||||||
|
"react-dom": "^19.0.0",
|
||||||
|
"sass": "^1.82.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.17.9",
|
||||||
|
"@types/react": "^19.0.1",
|
||||||
|
"@types/react-dom": "^19.0.2",
|
||||||
|
"typescript": "^5.7.2"
|
||||||
|
}
|
||||||
|
}
|
31
web/src/app/_components/header.tsx
Normal file
31
web/src/app/_components/header.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import Link from "next/link"
|
||||||
|
|
||||||
|
import "./styles/header.scss"
|
||||||
|
|
||||||
|
interface HeaderButton {
|
||||||
|
name: string,
|
||||||
|
href: string
|
||||||
|
}
|
||||||
|
function HeaderButton(header: HeaderButton) {
|
||||||
|
return (
|
||||||
|
<Link href={header.href}>
|
||||||
|
<button>{header.name}</button>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Header() {
|
||||||
|
return (
|
||||||
|
<header className="header-bar">
|
||||||
|
<nav className="left">
|
||||||
|
<HeaderButton name="Maps" href=""/>
|
||||||
|
</nav>
|
||||||
|
<nav className="right">
|
||||||
|
<HeaderButton name="Home" href=""/>
|
||||||
|
<HeaderButton name="Need" href=""/>
|
||||||
|
<HeaderButton name="Menu" href=""/>
|
||||||
|
<HeaderButton name="Items" href=""/>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
)
|
||||||
|
}
|
38
web/src/app/_components/styles/header.scss
Normal file
38
web/src/app/_components/styles/header.scss
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
.header-bar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
width: 100vw;
|
||||||
|
height: 60px;
|
||||||
|
background: rgb(59,64,70);
|
||||||
|
background: linear-gradient(180deg, #363b40 0%, #353a40 100%);
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: transparent;
|
||||||
|
border: 0;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left {
|
||||||
|
margin-left: 15px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
display: flex;
|
||||||
|
gap: 7px;
|
||||||
|
margin-right: 50px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
font-size: 1rem;
|
||||||
|
color: rgb(180,180,180);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: white
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
web/src/app/globals.scss
Normal file
26
web/src/app/globals.scss
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
$review-border-color: #c8c8c8;
|
||||||
|
$review-border: 1px solid $review-border-color;
|
||||||
|
|
||||||
|
@mixin border-with-radius {
|
||||||
|
border: $review-border {
|
||||||
|
radius: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
color-scheme: light;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, "Segoe UI", system-ui, Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Twemoji Mozilla";
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:active, a:link, a:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
9
web/src/app/layout.tsx
Normal file
9
web/src/app/layout.tsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import "./globals.scss";
|
||||||
|
|
||||||
|
export default function RootLayout({children}: Readonly<{children: React.ReactNode}>) {
|
||||||
|
return (
|
||||||
|
<html lang="en">
|
||||||
|
<body>{children}</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
34
web/src/app/map/[map_name]/layout.tsx
Normal file
34
web/src/app/map/[map_name]/layout.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { SubmissionStatus } from "@/app/ts/Submission";
|
||||||
|
import MapInfoPage from "./page";
|
||||||
|
import Header from "@/app/_components/header";
|
||||||
|
|
||||||
|
import "./styles/layout.scss";
|
||||||
|
|
||||||
|
export default function MapPage() {
|
||||||
|
const placeholder_Comment = [
|
||||||
|
{
|
||||||
|
comment: "This map has been accepted and is in the game.",
|
||||||
|
date: "on Dec 8 '24 at 18:46",
|
||||||
|
name: "BhopMaptest"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
comment: "This map is so mid...",
|
||||||
|
date: "on Dec 8 '24 at 18:46",
|
||||||
|
name: "vmsize"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
comment: "I prefer strafe client",
|
||||||
|
date: "on Dec 8 '24 at 18:46",
|
||||||
|
name: "Quaternions"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header/>
|
||||||
|
<main className="map-page-main">
|
||||||
|
<MapInfoPage assetid={14783300138} status={SubmissionStatus.Accepted} comments={placeholder_Comment}/>
|
||||||
|
</main>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
132
web/src/app/map/[map_name]/page.tsx
Normal file
132
web/src/app/map/[map_name]/page.tsx
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import { SubmissionStatus, type SubmissionInfo } from "@/app/ts/Submission";
|
||||||
|
import { useState, type ReactNode } from "react";
|
||||||
|
import { Rating, Button } from "@mui/material";
|
||||||
|
import { AssetImage } from "@/app/ts/Roblox";
|
||||||
|
import SendIcon from '@mui/icons-material/Send';
|
||||||
|
|
||||||
|
import "./styles/page.scss";
|
||||||
|
|
||||||
|
interface Window {
|
||||||
|
className: string,
|
||||||
|
title: string,
|
||||||
|
children: ReactNode
|
||||||
|
}
|
||||||
|
function Window(window: Window) {
|
||||||
|
return (
|
||||||
|
<section className={window.className}>
|
||||||
|
<header>
|
||||||
|
<p>{window.title}</p>
|
||||||
|
</header>
|
||||||
|
<main>{window.children}</main>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AssetID {
|
||||||
|
id: SubmissionInfo["AssetID"]
|
||||||
|
}
|
||||||
|
async function ImageAndRatings(asset: AssetID) {
|
||||||
|
const [assetImage, setAssetImage] = useState(null);
|
||||||
|
const asset_image = await AssetImage(asset.id, "420x420")
|
||||||
|
|
||||||
|
return (
|
||||||
|
<aside className="review-area">
|
||||||
|
<section className="rating">
|
||||||
|
<img src={asset_image} alt="Map Image"/>
|
||||||
|
</section>
|
||||||
|
<Window className="rating-window" title="Rating">
|
||||||
|
<section className="rating-type">
|
||||||
|
<aside className="rating-left">
|
||||||
|
<p>Quality</p>
|
||||||
|
<p>Difficulty</p>
|
||||||
|
<p>Fun</p>
|
||||||
|
<p>Length</p>
|
||||||
|
</aside>
|
||||||
|
<aside className="rating-right">
|
||||||
|
<Rating defaultValue={2.5} precision={0.5}/>
|
||||||
|
<Rating defaultValue={2.5} precision={0.5}/>
|
||||||
|
<Rating defaultValue={2.5} precision={0.5}/>
|
||||||
|
<Rating defaultValue={2.5} precision={0.5}/>
|
||||||
|
</aside>
|
||||||
|
</section>
|
||||||
|
</Window>
|
||||||
|
</aside>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Comment {
|
||||||
|
picture?: string, //TEMP
|
||||||
|
comment: string,
|
||||||
|
date: string,
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
function Comment(comment: Comment) {
|
||||||
|
let placeHolder;
|
||||||
|
if (comment.name === "BhopMaptest") {
|
||||||
|
placeHolder = "https://tr.rbxcdn.com/30DAY-AvatarHeadshot-FB29ADF0A483B2745DB2571DC4785202-Png/150/150/AvatarHeadshot/Webp/noFilter"
|
||||||
|
} else if (comment.name === "vmsize") {
|
||||||
|
placeHolder = "https://tr.rbxcdn.com/30DAY-AvatarHeadshot-ACEB71FADC70B458ECB9D6AA9AAE5913-Png/150/150/AvatarHeadshot/Webp/noFilter"
|
||||||
|
} else {
|
||||||
|
placeHolder = "https://tr.rbxcdn.com/30DAY-AvatarHeadshot-1ED6D3ED61793733397BB596F0ADD369-Png/150/150/AvatarHeadshot/Webp/noFilter"
|
||||||
|
}
|
||||||
|
|
||||||
|
//Highlighted comment
|
||||||
|
const IsBhopMaptest = comment.name == "BhopMaptest"
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="commenter" data-highlighted={IsBhopMaptest}>
|
||||||
|
<img src={placeHolder} alt={`${comment.name}'s comment`}/>
|
||||||
|
<div className="details">
|
||||||
|
<header>
|
||||||
|
<p className="name">{comment.name}</p>
|
||||||
|
<p className="date">{comment.date}</p>
|
||||||
|
</header>
|
||||||
|
<p className="comment">{comment.comment}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CreatorAndReviewStatus {
|
||||||
|
creator: SubmissionInfo["DisplayName"],
|
||||||
|
review: SubmissionInfo["StatusID"],
|
||||||
|
comments: Comment[]
|
||||||
|
}
|
||||||
|
function TitleAndComments(stats: CreatorAndReviewStatus) {
|
||||||
|
//TODO: switch case this for matching the enums
|
||||||
|
return (
|
||||||
|
<main className="review-info">
|
||||||
|
<div>
|
||||||
|
<h1>bhop_quaternions</h1>
|
||||||
|
<aside data-review-status={stats.review} className="review-status">
|
||||||
|
<p>ACCEPTED</p>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
<p className="by-creator">by <a href="" target="_blank">{stats.creator}</a></p>
|
||||||
|
<span className="spacer"></span>
|
||||||
|
<main className="comments">
|
||||||
|
{stats.comments.map(comment => (
|
||||||
|
<Comment name={comment.name} date={comment.date} comment={comment.comment}/>
|
||||||
|
))}
|
||||||
|
</main>
|
||||||
|
<Window title="Leave a Comment:" className="leave-comment-window">
|
||||||
|
<textarea name="comment-box" id="comment-text-field"></textarea>
|
||||||
|
<Button variant="contained" endIcon={<SendIcon/>}>Submit</Button>
|
||||||
|
</Window>
|
||||||
|
</main>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MapInfo {
|
||||||
|
assetid: SubmissionInfo["AssetID"],
|
||||||
|
status: SubmissionStatus,
|
||||||
|
comments: Comment[]
|
||||||
|
}
|
||||||
|
export default function MapInfoPage(info: MapInfo) {
|
||||||
|
return (
|
||||||
|
<section className="review-section">
|
||||||
|
<ImageAndRatings id={info.assetid}/>
|
||||||
|
<TitleAndComments creator="Quaternions" review={info.status} comments={info.comments}/>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
5
web/src/app/map/[map_name]/styles/layout.scss
Normal file
5
web/src/app/map/[map_name]/styles/layout.scss
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
.map-page-main {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
50
web/src/app/map/[map_name]/styles/page.scss
Normal file
50
web/src/app/map/[map_name]/styles/page.scss
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
@forward "./page/comments.scss";
|
||||||
|
@forward "./page/review.scss";
|
||||||
|
@forward "./page/rating_window.scss";
|
||||||
|
@forward "./page/leave_comment_window.scss";
|
||||||
|
@forward "./page/review_status.scss";
|
||||||
|
|
||||||
|
@use "../../../globals.scss";
|
||||||
|
|
||||||
|
.spacer {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
background-color: globals.$review-border-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.by-creator {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review-info {
|
||||||
|
width: 650px;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
p, h1 {
|
||||||
|
color: #1e1e1e;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font: {
|
||||||
|
weight: 500;
|
||||||
|
size: 1.8rem
|
||||||
|
};
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #008fd6;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
47
web/src/app/map/[map_name]/styles/page/comments.scss
Normal file
47
web/src/app/map/[map_name]/styles/page/comments.scss
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
$comments-size: 60px;
|
||||||
|
|
||||||
|
.comments {
|
||||||
|
display: grid;
|
||||||
|
gap: 25px;
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
.commenter {
|
||||||
|
display: flex;
|
||||||
|
height: $comments-size;
|
||||||
|
|
||||||
|
//BhopMaptest comment
|
||||||
|
&[data-highlighted="true"] {
|
||||||
|
background-color: #ffffd7;
|
||||||
|
}
|
||||||
|
|
||||||
|
> img {
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
font: {
|
||||||
|
weight: 500;
|
||||||
|
size: 1.3em;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
.date {
|
||||||
|
font-size: .8em;
|
||||||
|
margin: 0 0 0 5px;
|
||||||
|
color: #646464
|
||||||
|
}
|
||||||
|
|
||||||
|
.details {
|
||||||
|
display: grid;
|
||||||
|
margin-left: 10px;
|
||||||
|
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
p:not(.date) {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
@use "../../../../globals.scss";
|
||||||
|
|
||||||
|
#comment-text-field {
|
||||||
|
@include globals.border-with-radius;
|
||||||
|
resize: none;
|
||||||
|
width: 100%;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leave-comment-window {
|
||||||
|
@include globals.border-with-radius;
|
||||||
|
width: 100%;
|
||||||
|
height: 230px;
|
||||||
|
margin-top: 35px;
|
||||||
|
|
||||||
|
.rating-type {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
gap: 35%;
|
||||||
|
|
||||||
|
.rating-right {
|
||||||
|
display: grid;
|
||||||
|
|
||||||
|
> span {
|
||||||
|
margin: 6px 0 6px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 15px 0 15px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-bottom: globals.$review-border;
|
||||||
|
height: 45px;
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0 0 0 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main {
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-top: 9px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
web/src/app/map/[map_name]/styles/page/rating_window.scss
Normal file
44
web/src/app/map/[map_name]/styles/page/rating_window.scss
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
@use "../../../../globals.scss";
|
||||||
|
|
||||||
|
.rating-window {
|
||||||
|
@include globals.border-with-radius;
|
||||||
|
width: 100%;
|
||||||
|
height: 225px;
|
||||||
|
|
||||||
|
.rating-type {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
gap: 35%;
|
||||||
|
|
||||||
|
.rating-right {
|
||||||
|
display: grid;
|
||||||
|
|
||||||
|
> span {
|
||||||
|
margin: 6px 0 6px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 15px 0 15px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-bottom: globals.$review-border;
|
||||||
|
height: 45px;
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0 0 0 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main {
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
|
}
|
20
web/src/app/map/[map_name]/styles/page/review.scss
Normal file
20
web/src/app/map/[map_name]/styles/page/review.scss
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
@use "../../../../globals.scss";
|
||||||
|
|
||||||
|
.review-section {
|
||||||
|
display: flex;
|
||||||
|
gap: 50px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review-area {
|
||||||
|
display: grid;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 25px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
@include globals.border-with-radius;
|
||||||
|
width: 100%;
|
||||||
|
height: 350px;
|
||||||
|
object-fit: contain
|
||||||
|
}
|
||||||
|
}
|
73
web/src/app/map/[map_name]/styles/page/review_status.scss
Normal file
73
web/src/app/map/[map_name]/styles/page/review_status.scss
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
$Published: "0";
|
||||||
|
$Rejected: "1";
|
||||||
|
$Publishing: "2";
|
||||||
|
$Validated: "3";
|
||||||
|
$Validating: "4";
|
||||||
|
$Accepted: "5";
|
||||||
|
$ChangesRequested: "6";
|
||||||
|
$Submitted: "7";
|
||||||
|
$UnderConstruction: "8";
|
||||||
|
|
||||||
|
.review-status {
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 3px 25px 3px 25px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-review-status="#{$Published}"] {
|
||||||
|
background-color: orange;
|
||||||
|
p {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&[data-review-status="#{$Rejected}"] {
|
||||||
|
background-color: orange;
|
||||||
|
p {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&[data-review-status="#{$Publishing}"] {
|
||||||
|
background-color: orange;
|
||||||
|
p {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&[data-review-status="#{$Validated}"] {
|
||||||
|
background-color: orange;
|
||||||
|
p {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&[data-review-status="#{$Validating}"] {
|
||||||
|
background-color: orange;
|
||||||
|
p {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&[data-review-status="#{$Accepted}"] {
|
||||||
|
background-color: rgb(2, 162, 2);
|
||||||
|
p {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&[data-review-status="#{$ChangesRequested}"] {
|
||||||
|
background-color: orange;
|
||||||
|
p {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&[data-review-status="#{$Submitted}"] {
|
||||||
|
background-color: orange;
|
||||||
|
p {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&[data-review-status="#{$UnderConstruction}"] {
|
||||||
|
background-color: orange;
|
||||||
|
p {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
web/src/app/page.tsx
Normal file
7
web/src/app/page.tsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import Header from "./_components/header";
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<Header/>
|
||||||
|
);
|
||||||
|
}
|
39
web/src/app/ts/Roblox.ts
Normal file
39
web/src/app/ts/Roblox.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
const FALLBACK_IMAGE = ""
|
||||||
|
|
||||||
|
type thumbsizes = "420" | "720"
|
||||||
|
type thumbsize<S extends thumbsizes> = `${S}x${S}`
|
||||||
|
|
||||||
|
async function RoproxyParse(api: Response): Promise<string> {
|
||||||
|
if (api.ok) {
|
||||||
|
const json = await api.json()
|
||||||
|
if (json.errors) {
|
||||||
|
console.warn(json.errors)
|
||||||
|
return FALLBACK_IMAGE
|
||||||
|
}
|
||||||
|
if (json.data) {
|
||||||
|
const data = json.data[0]
|
||||||
|
if (!data) { //For whatever reason roblox will sometimes return an empty array instead of an error message
|
||||||
|
console.warn("Roblox gave us no data,", json)
|
||||||
|
return FALLBACK_IMAGE
|
||||||
|
}
|
||||||
|
if (data.state === "Completed") {
|
||||||
|
return data.imageUrl
|
||||||
|
}
|
||||||
|
console.warn(data)
|
||||||
|
return FALLBACK_IMAGE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.warn(api)
|
||||||
|
return FALLBACK_IMAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function AvatarHeadshot<S extends thumbsizes>(userid: number, size: thumbsize<S>): Promise<string> {
|
||||||
|
const avatarthumb_api = await fetch(`https://thumbnails.roproxy.com/v1/users/avatar-headshot?userIds=${userid}&size=${size}&format=Png&isCircular=false`)
|
||||||
|
return RoproxyParse(avatarthumb_api)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function AssetImage<S extends thumbsizes>(assetid: number, size: thumbsize<S>): Promise<string> {
|
||||||
|
const avatarthumb_api = await fetch(`https://thumbnails.roblox.com/v1/assets?assetIds=${assetid}&returnPolicy=PlaceHolder&size=${size}&format=Png&isCircular=false`)
|
||||||
|
return RoproxyParse(avatarthumb_api)
|
||||||
|
}
|
25
web/src/app/ts/Submission.ts
Normal file
25
web/src/app/ts/Submission.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
export const enum SubmissionStatus {
|
||||||
|
Published,
|
||||||
|
Rejected,
|
||||||
|
Publishing,
|
||||||
|
Validated,
|
||||||
|
Validating,
|
||||||
|
Accepted,
|
||||||
|
ChangesRequested,
|
||||||
|
Submitted,
|
||||||
|
UnderConstruction
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SubmissionInfo {
|
||||||
|
readonly ID: number,
|
||||||
|
readonly DisplayName: string,
|
||||||
|
readonly Creator: string,
|
||||||
|
readonly GameID: number,
|
||||||
|
readonly Date: number,
|
||||||
|
readonly Submitter: number,
|
||||||
|
readonly AssetID: number,
|
||||||
|
readonly AssetVersion: number,
|
||||||
|
readonly Completed: boolean,
|
||||||
|
readonly TargetAssetID: number,
|
||||||
|
readonly StatusID: SubmissionStatus
|
||||||
|
}
|
28
web/tsconfig.json
Normal file
28
web/tsconfig.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"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",
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"incremental": true,
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "next"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user