refactor: add react frontend
This commit is contained in:
parent
fd17e4fe6b
commit
7ee3ead05b
7
backend/.dockerignore
Normal file
7
backend/.dockerignore
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
profiles/
|
||||||
|
results/
|
||||||
|
__pycache__/
|
||||||
|
.venv/
|
||||||
|
tests/
|
||||||
|
simc
|
||||||
|
simcrunner_export.simc
|
7
backend/.gitignore
vendored
Normal file
7
backend/.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.venv/
|
||||||
|
__pycache__/
|
||||||
|
results/
|
||||||
|
profiles/
|
||||||
|
simc/
|
||||||
|
simc
|
||||||
|
simcrunner_export.simc
|
21
backend/Dockerfile
Normal file
21
backend/Dockerfile
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
FROM simulationcraftorg/simc:latest
|
||||||
|
|
||||||
|
ENV LANG=de_DE.UTF-8
|
||||||
|
|
||||||
|
RUN apk add --no-cache python3 py3-pip git
|
||||||
|
|
||||||
|
ENV VIRTUAL_ENV=/opt/venv
|
||||||
|
RUN python3 -m venv $VIRTUAL_ENV
|
||||||
|
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
|
||||||
|
|
||||||
|
# Install dependencies:
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install -r requirements.txt
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
ENTRYPOINT []
|
||||||
|
|
||||||
|
CMD ["fastapi", "run", "main.py", "--port", "8000"]
|
18
backend/README.md
Normal file
18
backend/README.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# SIM Free
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/alexohneander/sim_free
|
||||||
|
cd sim_free
|
||||||
|
|
||||||
|
python3 -m venv .venv
|
||||||
|
source .venv/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run
|
||||||
|
```bash
|
||||||
|
fastapi run main.py
|
||||||
|
```
|
104
backend/main.py
Normal file
104
backend/main.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import uuid
|
||||||
|
import time
|
||||||
|
import functools
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from simcrunner import Simc, JsonExport, Arguments, Profile
|
||||||
|
|
||||||
|
from typing import Union, Annotated
|
||||||
|
from fastapi import FastAPI, Form
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from starlette.responses import FileResponse, HTMLResponse
|
||||||
|
from simcrunner.simc import HtmlExport
|
||||||
|
|
||||||
|
# SIMC Settings
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
# simc_path = os.path.join('tests', 'simc')
|
||||||
|
simc_path = "./"
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
origins = [
|
||||||
|
"http://localhost",
|
||||||
|
"http://localhost:3000",
|
||||||
|
"https://sim-free.dev-null.rocks",
|
||||||
|
"*"
|
||||||
|
]
|
||||||
|
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=origins,
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
app.mount("/_next", StaticFiles(directory="templates/_next"), name="static")
|
||||||
|
app.mount("/_next/image", StaticFiles(directory="templates/_next"), name="static")
|
||||||
|
app.mount("/img", StaticFiles(directory="templates/img"), name="static")
|
||||||
|
|
||||||
|
# ROUTES
|
||||||
|
@app.get("/")
|
||||||
|
def read_root():
|
||||||
|
index_path = os.path.join('templates', 'index.html')
|
||||||
|
return FileResponse(index_path)
|
||||||
|
|
||||||
|
@app.post("/sim/current_gear")
|
||||||
|
def simulate_current_gear(simcprofile: Annotated[str, Form()]):
|
||||||
|
|
||||||
|
profile_path = create_sim_arguments(simcprofile)
|
||||||
|
export_path = create_html_export()
|
||||||
|
html_export = HtmlExport(export_path)
|
||||||
|
|
||||||
|
profile = Profile(profile_path)
|
||||||
|
args = Arguments(profile, iterations=1000)
|
||||||
|
|
||||||
|
runner = Simc(simc_path=simc_path)
|
||||||
|
(runner
|
||||||
|
.add_args(args)
|
||||||
|
.add_args('target_error=0.05', threads=4)
|
||||||
|
.add_args(html_export)
|
||||||
|
.run())
|
||||||
|
|
||||||
|
# response = FileResponse(export_path)
|
||||||
|
# remove_temp_files(profile_path, export_path)
|
||||||
|
|
||||||
|
return FileResponse(export_path)
|
||||||
|
|
||||||
|
# HELPER Functions
|
||||||
|
def create_profile(profile_path: str, profile_data: str):
|
||||||
|
with open(profile_path, 'w') as file:
|
||||||
|
# Write content to the file
|
||||||
|
file.write(profile_data)
|
||||||
|
|
||||||
|
def create_html_export():
|
||||||
|
rand_uuid = uuid.uuid4()
|
||||||
|
export_path = os.path.join('results', str(rand_uuid)+'.html')
|
||||||
|
|
||||||
|
return export_path
|
||||||
|
|
||||||
|
def create_sim_arguments(profile_data: str):
|
||||||
|
rand_uuid = uuid.uuid4()
|
||||||
|
profile_path = os.path.join('profiles', str(rand_uuid)+'.simc')
|
||||||
|
|
||||||
|
create_profile(profile_path, profile_data)
|
||||||
|
|
||||||
|
return profile_path
|
||||||
|
|
||||||
|
def remove_temp_files(profile_path: str, export_path: str):
|
||||||
|
time.sleep(1)
|
||||||
|
os.remove(export_path)
|
||||||
|
os.remove(profile_path)
|
||||||
|
|
||||||
|
@functools.lru_cache(maxsize=2)
|
||||||
|
def read_file_with_lru_cache(file_path):
|
||||||
|
# Read the file content
|
||||||
|
with open(file_path, 'r', encoding="utf-8") as file:
|
||||||
|
file_content = file.read()
|
||||||
|
print(f"Reading from file: {file_path}")
|
||||||
|
|
||||||
|
return file_content
|
2
backend/requirements.txt
Normal file
2
backend/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
fastapi[standard]==0.115.0
|
||||||
|
git+https://github.com/simcminmax/simcrunner.git
|
1
backend/templates/404.html
Normal file
1
backend/templates/404.html
Normal file
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
|||||||
|
self.__BUILD_MANIFEST={__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/_error":["static/chunks/pages/_error-1be831200e60c5c0.js"],sortedPages:["/_app","/_error"]},self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
|
@ -0,0 +1 @@
|
|||||||
|
self.__SSG_MANIFEST=new Set([]);self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
|||||||
|
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[409],{7589:function(e,t,n){(window.__NEXT_P=window.__NEXT_P||[]).push(["/_not-found/page",function(){return n(5457)}])},5457:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return s}}),n(9920);let i=n(7437);n(2265);let o={fontFamily:'system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"',height:"100vh",textAlign:"center",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center"},l={display:"inline-block"},r={display:"inline-block",margin:"0 20px 0 0",padding:"0 23px 0 0",fontSize:24,fontWeight:500,verticalAlign:"top",lineHeight:"49px"},d={fontSize:14,fontWeight:400,lineHeight:"49px",margin:0};function s(){return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)("title",{children:"404: This page could not be found."}),(0,i.jsx)("div",{style:o,children:(0,i.jsxs)("div",{children:[(0,i.jsx)("style",{dangerouslySetInnerHTML:{__html:"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}),(0,i.jsx)("h1",{className:"next-error-h1",style:r,children:"404"}),(0,i.jsx)("div",{style:l,children:(0,i.jsx)("h2",{style:d,children:"This page could not be found."})})]})})]})}("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)}},function(e){e.O(0,[971,23,744],function(){return e(e.s=7589)}),_N_E=e.O()}]);
|
@ -0,0 +1 @@
|
|||||||
|
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[185],{5825:function(e,a,_){Promise.resolve().then(_.t.bind(_,7800,23)),Promise.resolve().then(_.t.bind(_,2544,23)),Promise.resolve().then(_.t.bind(_,3054,23))},3054:function(){},2544:function(e){e.exports={style:{fontFamily:"'__geistMono_c3aa02', '__geistMono_Fallback_c3aa02'"},className:"__className_c3aa02",variable:"__variable_c3aa02"}},7800:function(e){e.exports={style:{fontFamily:"'__geistSans_1e4310', '__geistSans_Fallback_1e4310'"},className:"__className_1e4310",variable:"__variable_1e4310"}}},function(e){e.O(0,[571,971,23,744],function(){return e(e.s=5825)}),_N_E=e.O()}]);
|
@ -0,0 +1 @@
|
|||||||
|
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{1603:function(e,t,r){Promise.resolve().then(r.t.bind(r,8173,23)),Promise.resolve().then(r.t.bind(r,9671,23)),Promise.resolve().then(r.bind(r,436))},436:function(e,t,r){"use strict";r.d(t,{SimCurrentGear:function(){return c}});var a=r(7437),s=r(2265),n=r(9671),o=r.n(n);function c(){let[e,t]=(0,s.useState)(!1),[r,n]=(0,s.useState)("");async function c(e){try{let r=await fetch("http://127.0.0.1:8000/sim/current_gear",{method:"POST",headers:{"Access-Control-Allow-Origin":"*"},body:e});if(r.ok)t(!0),n(await r.text());else throw Error("Error! status: ".concat(r.status))}catch(e){e instanceof Error?console.log(e.message):console.log("Unexpected error",e)}}return(0,a.jsxs)("div",{children:[(0,a.jsx)("iframe",{id:"renderframe",style:{display:"".concat(e?"block":"none"),width:"100%",height:"100vh",marginBottom:"50px"},srcDoc:"".concat(r)}),(0,a.jsxs)("form",{action:c,children:[(0,a.jsx)("div",{className:o().ctas,children:(0,a.jsx)("textarea",{className:o().textarea,rows:10,id:"simcprofile",name:"simcprofile",style:{display:"".concat(e?"none":"block")}})}),(0,a.jsxs)("div",{className:o().ctas,children:[(0,a.jsx)("button",{className:o().primary,type:"submit",children:"Run Sim"}),(0,a.jsx)("a",{href:"https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app",target:"_blank",rel:"noopener noreferrer",className:o().secondary,children:"Read our docs"})]})]})]})}},9671:function(e){e.exports={page:"page_page__ZU32B",main:"page_main__GlU4n",ctas:"page_ctas__g5wGe",textarea:"page_textarea__8C2wx",primary:"page_primary__V8M9Y",secondary:"page_secondary__lm_PT",footer:"page_footer__sHKi3",logo:"page_logo__7fc9l"}}},function(e){e.O(0,[967,173,971,23,744],function(){return e(e.s=1603)}),_N_E=e.O()}]);
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
|||||||
|
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[744],{400:function(e,n,t){Promise.resolve().then(t.t.bind(t,5751,23)),Promise.resolve().then(t.t.bind(t,6513,23)),Promise.resolve().then(t.t.bind(t,6130,23)),Promise.resolve().then(t.t.bind(t,9275,23)),Promise.resolve().then(t.t.bind(t,5324,23)),Promise.resolve().then(t.t.bind(t,1343,23))}},function(e){var n=function(n){return e(e.s=n)};e.O(0,[971,23],function(){return n(1028),n(400)}),_N_E=e.O()}]);
|
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
|||||||
|
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[888],{1597:function(n,_,u){(window.__NEXT_P=window.__NEXT_P||[]).push(["/_app",function(){return u(2239)}])}},function(n){var _=function(_){return n(n.s=_)};n.O(0,[774,179],function(){return _(1597),_(6036)}),_N_E=n.O()}]);
|
@ -0,0 +1 @@
|
|||||||
|
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[820],{1981:function(n,_,u){(window.__NEXT_P=window.__NEXT_P||[]).push(["/_error",function(){return u(3387)}])}},function(n){n.O(0,[888,774,179],function(){return n(n.s=1981)}),_N_E=n.O()}]);
|
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
|||||||
|
!function(){"use strict";var e,t,n,r,o,u,i,c,f,a={},l={};function d(e){var t=l[e];if(void 0!==t)return t.exports;var n=l[e]={exports:{}},r=!0;try{a[e](n,n.exports,d),r=!1}finally{r&&delete l[e]}return n.exports}d.m=a,e=[],d.O=function(t,n,r,o){if(n){o=o||0;for(var u=e.length;u>0&&e[u-1][2]>o;u--)e[u]=e[u-1];e[u]=[n,r,o];return}for(var i=1/0,u=0;u<e.length;u++){for(var n=e[u][0],r=e[u][1],o=e[u][2],c=!0,f=0;f<n.length;f++)i>=o&&Object.keys(d.O).every(function(e){return d.O[e](n[f])})?n.splice(f--,1):(c=!1,o<i&&(i=o));if(c){e.splice(u--,1);var a=r();void 0!==a&&(t=a)}}return t},d.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return d.d(t,{a:t}),t},n=Object.getPrototypeOf?function(e){return Object.getPrototypeOf(e)}:function(e){return e.__proto__},d.t=function(e,r){if(1&r&&(e=this(e)),8&r||"object"==typeof e&&e&&(4&r&&e.__esModule||16&r&&"function"==typeof e.then))return e;var o=Object.create(null);d.r(o);var u={};t=t||[null,n({}),n([]),n(n)];for(var i=2&r&&e;"object"==typeof i&&!~t.indexOf(i);i=n(i))Object.getOwnPropertyNames(i).forEach(function(t){u[t]=function(){return e[t]}});return u.default=function(){return e},d.d(o,u),o},d.d=function(e,t){for(var n in t)d.o(t,n)&&!d.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},d.f={},d.e=function(e){return Promise.all(Object.keys(d.f).reduce(function(t,n){return d.f[n](e,t),t},[]))},d.u=function(e){},d.miniCssF=function(e){},d.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r={},o="_N_E:",d.l=function(e,t,n,u){if(r[e]){r[e].push(t);return}if(void 0!==n)for(var i,c,f=document.getElementsByTagName("script"),a=0;a<f.length;a++){var l=f[a];if(l.getAttribute("src")==e||l.getAttribute("data-webpack")==o+n){i=l;break}}i||(c=!0,(i=document.createElement("script")).charset="utf-8",i.timeout=120,d.nc&&i.setAttribute("nonce",d.nc),i.setAttribute("data-webpack",o+n),i.src=d.tu(e)),r[e]=[t];var s=function(t,n){i.onerror=i.onload=null,clearTimeout(p);var o=r[e];if(delete r[e],i.parentNode&&i.parentNode.removeChild(i),o&&o.forEach(function(e){return e(n)}),t)return t(n)},p=setTimeout(s.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=s.bind(null,i.onerror),i.onload=s.bind(null,i.onload),c&&document.head.appendChild(i)},d.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},d.tt=function(){return void 0===u&&(u={createScriptURL:function(e){return e}},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(u=trustedTypes.createPolicy("nextjs#bundler",u))),u},d.tu=function(e){return d.tt().createScriptURL(e)},d.p="/_next/",i={272:0,571:0,967:0},d.f.j=function(e,t){var n=d.o(i,e)?i[e]:void 0;if(0!==n){if(n)t.push(n[2]);else if(/^(272|571|967)$/.test(e))i[e]=0;else{var r=new Promise(function(t,r){n=i[e]=[t,r]});t.push(n[2]=r);var o=d.p+d.u(e),u=Error();d.l(o,function(t){if(d.o(i,e)&&(0!==(n=i[e])&&(i[e]=void 0),n)){var r=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src;u.message="Loading chunk "+e+" failed.\n("+r+": "+o+")",u.name="ChunkLoadError",u.type=r,u.request=o,n[1](u)}},"chunk-"+e,e)}}},d.O.j=function(e){return 0===i[e]},c=function(e,t){var n,r,o=t[0],u=t[1],c=t[2],f=0;if(o.some(function(e){return 0!==i[e]})){for(n in u)d.o(u,n)&&(d.m[n]=u[n]);if(c)var a=c(d)}for(e&&e(t);f<o.length;f++)r=o[f],d.o(i,r)&&i[r]&&i[r][0](),i[r]=0;return d.O(a)},(f=self.webpackChunk_N_E=self.webpackChunk_N_E||[]).forEach(c.bind(null,0)),f.push=c.bind(null,f.push.bind(f))}();
|
1
backend/templates/_next/static/css/9de320161955c026.css
Normal file
1
backend/templates/_next/static/css/9de320161955c026.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
@font-face{font-family:__geistSans_1e4310;src:url(/_next/static/media/4473ecc91f70f139-s.p.woff) format("woff");font-display:swap;font-weight:100 900}@font-face{font-family:__geistSans_Fallback_1e4310;src:local("Arial");ascent-override:85.83%;descent-override:20.52%;line-gap-override:9.33%;size-adjust:107.19%}.__className_1e4310{font-family:__geistSans_1e4310,__geistSans_Fallback_1e4310}.__variable_1e4310{--font-geist-sans:"__geistSans_1e4310","__geistSans_Fallback_1e4310"}@font-face{font-family:__geistMono_c3aa02;src:url(/_next/static/media/463dafcda517f24f-s.p.woff) format("woff");font-display:swap;font-weight:100 900}@font-face{font-family:__geistMono_Fallback_c3aa02;src:local("Arial");ascent-override:69.97%;descent-override:16.73%;line-gap-override:7.61%;size-adjust:131.49%}.__className_c3aa02{font-family:__geistMono_c3aa02,__geistMono_Fallback_c3aa02}.__variable_c3aa02{--font-geist-mono:"__geistMono_c3aa02","__geistMono_Fallback_c3aa02"}:root{--background:#fff;--foreground:#171717}@media (prefers-color-scheme:dark){:root{--background:#0a0a0a;--foreground:#ededed}}body,html{max-width:100vw;overflow-x:hidden}body{color:var(--foreground);background:var(--background);font-family:Arial,Helvetica,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}*{box-sizing:border-box;padding:0;margin:0}a{color:inherit;text-decoration:none}@media (prefers-color-scheme:dark){html{color-scheme:dark}}
|
1
backend/templates/_next/static/css/b5daad47fe07b32d.css
Normal file
1
backend/templates/_next/static/css/b5daad47fe07b32d.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
.page_page__ZU32B{--gray-rgb:0,0,0;--gray-alpha-200:rgba(var(--gray-rgb),0.08);--gray-alpha-100:rgba(var(--gray-rgb),0.05);--button-primary-hover:#383838;--button-secondary-hover:#f2f2f2;display:grid;grid-template-rows:20px 1fr 20px;align-items:center;justify-items:center;min-height:100svh;padding:80px;grid-gap:64px;gap:64px;font-family:var(--font-geist-sans)}@media (prefers-color-scheme:dark){.page_page__ZU32B{--gray-rgb:255,255,255;--gray-alpha-200:rgba(var(--gray-rgb),0.145);--gray-alpha-100:rgba(var(--gray-rgb),0.06);--button-primary-hover:#ccc;--button-secondary-hover:#1a1a1a}}.page_main__GlU4n{display:flex;flex-direction:column;gap:32px;grid-row-start:2}.page_main__GlU4n ol{font-family:var(--font-geist-mono);padding-left:0;margin:0;font-size:14px;line-height:24px;letter-spacing:-.01em;list-style-position:inside}.page_main__GlU4n li:not(:last-of-type){margin-bottom:8px}.page_main__GlU4n code{font-family:inherit;background:var(--gray-alpha-100);padding:2px 4px;border-radius:4px;font-weight:600}.page_ctas__g5wGe{display:flex;gap:16px}.page_ctas__g5wGe a,.page_ctas__g5wGe button{-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:128px;height:48px;padding:0 20px;border:1px solid transparent;transition:background .2s,color .2s,border-color .2s;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:16px;line-height:20px;font-weight:500}.page_ctas__g5wGe .page_textarea__8C2wx{width:100%;margin-bottom:40px}a.page_primary__V8M9Y,button.page_primary__V8M9Y{background:var(--foreground);color:var(--background);gap:8px}a.page_secondary__lm_PT{border-color:var(--gray-alpha-200);min-width:180px}.page_footer__sHKi3{grid-row-start:3;display:flex;gap:24px}.page_footer__sHKi3 a{display:flex;align-items:center;gap:8px}.page_footer__sHKi3 img{flex-shrink:0}@media (hover:hover) and (pointer:fine){a.page_primary__V8M9Y:hover{background:var(--button-primary-hover);border-color:transparent}a.page_secondary__lm_PT:hover{background:var(--button-secondary-hover);border-color:transparent}.page_footer__sHKi3 a:hover{text-decoration:underline;text-underline-offset:4px}}@media (max-width:600px){.page_page__ZU32B{padding:32px 32px 80px}.page_main__GlU4n{align-items:center}.page_main__GlU4n ol{text-align:center}.page_ctas__g5wGe{flex-direction:column}.page_ctas__g5wGe a{font-size:14px;height:40px;padding:0 16px}a.page_secondary__lm_PT{min-width:auto}.page_footer__sHKi3{flex-wrap:wrap;align-items:center;justify-content:center}}.page_logo__7fc9l{margin:auto}
|
BIN
backend/templates/_next/static/media/4473ecc91f70f139-s.p.woff
Normal file
BIN
backend/templates/_next/static/media/4473ecc91f70f139-s.p.woff
Normal file
Binary file not shown.
BIN
backend/templates/_next/static/media/463dafcda517f24f-s.p.woff
Normal file
BIN
backend/templates/_next/static/media/463dafcda517f24f-s.p.woff
Normal file
Binary file not shown.
BIN
backend/templates/favicon.ico
Normal file
BIN
backend/templates/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
BIN
backend/templates/img/warcraft-icon-22.png
Normal file
BIN
backend/templates/img/warcraft-icon-22.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 123 KiB |
1
backend/templates/index.html
Normal file
1
backend/templates/index.html
Normal file
File diff suppressed because one or more lines are too long
7
backend/templates/index.txt
Normal file
7
backend/templates/index.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
2:I[436,["173","static/chunks/173-4f078887a7bfcdfc.js","931","static/chunks/app/page-7811c68757d4c4c5.js"],"SimCurrentGear"]
|
||||||
|
3:I[8173,["173","static/chunks/173-4f078887a7bfcdfc.js","931","static/chunks/app/page-7811c68757d4c4c5.js"],"Image"]
|
||||||
|
4:I[9275,[],""]
|
||||||
|
5:I[1343,[],""]
|
||||||
|
0:["6eAreke72TFTOl_oq5LbI",[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",{"children":["__PAGE__",{},[["$L1",["$","div",null,{"className":"page_page__ZU32B","children":[["$","main",null,{"className":"page_main__GlU4n","children":[["$","img",null,{"className":"page_logo__7fc9l","src":"/img/warcraft-icon-22.png","alt":"Sim-Free Logo","width":150,"height":150}],["$","ol",null,{"children":[["$","li",null,{"children":["Copy/paste the text from the SimulationCraft addon. ",["$","a",null,{"className":"page_primary__V8M9Y","target":"_blank","href":"https://github.com/simulationcraft/simc-addon","children":"How to install and use the SimC addon"}]]}],["$","li",null,{"children":"Select pieces of gear and Sim-Free will sim them"}]]}],["$","$L2",null,{}]]}],["$","footer",null,{"className":"page_footer__sHKi3","children":[["$","a",null,{"href":"https://github.com/alexohneander/sim_free/issues/new","target":"_blank","rel":"noopener noreferrer","children":[["$","$L3",null,{"aria-hidden":true,"src":"https://nextjs.org/icons/file.svg","alt":"File icon","width":16,"height":16}],"Issues"]}],["$","a",null,{"href":"https://github.com/alexohneander/sim_free/","target":"_blank","rel":"noopener noreferrer","children":[["$","$L3",null,{"aria-hidden":true,"src":"https://nextjs.org/icons/window.svg","alt":"Window icon","width":16,"height":16}],"Project"]}],["$","a",null,{"href":"https://alexohneander.de","target":"_blank","rel":"noopener noreferrer","children":[["$","$L3",null,{"aria-hidden":true,"src":"https://nextjs.org/icons/globe.svg","alt":"Globe icon","width":16,"height":16}],"Go to alexohneander.de →"]}]]}]]}],[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/b5daad47fe07b32d.css","precedence":"next","crossOrigin":"$undefined"}]]],null],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/9de320161955c026.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__variable_1e4310 __variable_c3aa02","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}]}]}]],null],null],["$L6",null]]]]
|
||||||
|
6:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"SimC-Free"}],["$","meta","3",{"name":"description","content":"Generated by create next app"}],["$","link","4",{"rel":"icon","href":"/favicon.ico","type":"image/x-icon","sizes":"16x16"}],["$","meta","5",{"name":"next-size-adjust"}]]
|
||||||
|
1:null
|
3
frontend/.eslintrc.json
Normal file
3
frontend/.eslintrc.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"extends": ["next/core-web-vitals", "next/typescript"]
|
||||||
|
}
|
36
frontend/.gitignore
vendored
Normal file
36
frontend/.gitignore
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
.yarn/install-state.gz
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# next.js
|
||||||
|
/.next/
|
||||||
|
/out/
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env*.local
|
||||||
|
|
||||||
|
# vercel
|
||||||
|
.vercel
|
||||||
|
|
||||||
|
# typescript
|
||||||
|
*.tsbuildinfo
|
||||||
|
next-env.d.ts
|
36
frontend/README.md
Normal file
36
frontend/README.md
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
First, run the development server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
# or
|
||||||
|
yarn dev
|
||||||
|
# or
|
||||||
|
pnpm dev
|
||||||
|
# or
|
||||||
|
bun dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||||
|
|
||||||
|
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||||
|
|
||||||
|
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
To learn more about Next.js, take a look at the following resources:
|
||||||
|
|
||||||
|
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||||
|
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||||
|
|
||||||
|
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||||||
|
|
||||||
|
## Deploy on Vercel
|
||||||
|
|
||||||
|
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||||
|
|
||||||
|
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
6
frontend/next.config.mjs
Normal file
6
frontend/next.config.mjs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/** @type {import('next').NextConfig} */
|
||||||
|
const nextConfig = {
|
||||||
|
output: "export",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default nextConfig;
|
6384
frontend/package-lock.json
generated
Normal file
6384
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
29
frontend/package.json
Normal file
29
frontend/package.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "frontend",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next dev",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start",
|
||||||
|
"lint": "next lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@chakra-ui/next-js": "^2.2.0",
|
||||||
|
"@chakra-ui/react": "^2.8.2",
|
||||||
|
"@emotion/react": "^11.13.3",
|
||||||
|
"@emotion/styled": "^11.13.0",
|
||||||
|
"framer-motion": "^11.5.5",
|
||||||
|
"next": "14.2.12",
|
||||||
|
"react": "^18",
|
||||||
|
"react-dom": "^18"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20",
|
||||||
|
"@types/react": "^18",
|
||||||
|
"@types/react-dom": "^18",
|
||||||
|
"eslint": "^8",
|
||||||
|
"eslint-config-next": "14.2.12",
|
||||||
|
"typescript": "^5"
|
||||||
|
}
|
||||||
|
}
|
BIN
frontend/public/img/warcraft-icon-22.png
Normal file
BIN
frontend/public/img/warcraft-icon-22.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 123 KiB |
BIN
frontend/src/app/favicon.ico
Normal file
BIN
frontend/src/app/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
BIN
frontend/src/app/fonts/GeistMonoVF.woff
Normal file
BIN
frontend/src/app/fonts/GeistMonoVF.woff
Normal file
Binary file not shown.
BIN
frontend/src/app/fonts/GeistVF.woff
Normal file
BIN
frontend/src/app/fonts/GeistVF.woff
Normal file
Binary file not shown.
42
frontend/src/app/globals.css
Normal file
42
frontend/src/app/globals.css
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
:root {
|
||||||
|
--background: #ffffff;
|
||||||
|
--foreground: #171717;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--background: #0a0a0a;
|
||||||
|
--foreground: #ededed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
max-width: 100vw;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
color: var(--foreground);
|
||||||
|
background: var(--background);
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
html {
|
||||||
|
color-scheme: dark;
|
||||||
|
}
|
||||||
|
}
|
33
frontend/src/app/layout.tsx
Normal file
33
frontend/src/app/layout.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
import localFont from "next/font/local";
|
||||||
|
import "./globals.css";
|
||||||
|
|
||||||
|
const geistSans = localFont({
|
||||||
|
src: "./fonts/GeistVF.woff",
|
||||||
|
variable: "--font-geist-sans",
|
||||||
|
weight: "100 900",
|
||||||
|
});
|
||||||
|
const geistMono = localFont({
|
||||||
|
src: "./fonts/GeistMonoVF.woff",
|
||||||
|
variable: "--font-geist-mono",
|
||||||
|
weight: "100 900",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "SimC-Free",
|
||||||
|
description: "Generated by create next app",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: Readonly<{
|
||||||
|
children: React.ReactNode;
|
||||||
|
}>) {
|
||||||
|
return (
|
||||||
|
<html lang="en">
|
||||||
|
<body className={`${geistSans.variable} ${geistMono.variable}`}>
|
||||||
|
{children}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
184
frontend/src/app/page.module.css
Normal file
184
frontend/src/app/page.module.css
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
.page {
|
||||||
|
--gray-rgb: 0, 0, 0;
|
||||||
|
--gray-alpha-200: rgba(var(--gray-rgb), 0.08);
|
||||||
|
--gray-alpha-100: rgba(var(--gray-rgb), 0.05);
|
||||||
|
|
||||||
|
--button-primary-hover: #383838;
|
||||||
|
--button-secondary-hover: #f2f2f2;
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 20px 1fr 20px;
|
||||||
|
align-items: center;
|
||||||
|
justify-items: center;
|
||||||
|
min-height: 100svh;
|
||||||
|
padding: 80px;
|
||||||
|
gap: 64px;
|
||||||
|
font-family: var(--font-geist-sans);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.page {
|
||||||
|
--gray-rgb: 255, 255, 255;
|
||||||
|
--gray-alpha-200: rgba(var(--gray-rgb), 0.145);
|
||||||
|
--gray-alpha-100: rgba(var(--gray-rgb), 0.06);
|
||||||
|
|
||||||
|
--button-primary-hover: #ccc;
|
||||||
|
--button-secondary-hover: #1a1a1a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 32px;
|
||||||
|
grid-row-start: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main ol {
|
||||||
|
font-family: var(--font-geist-mono);
|
||||||
|
padding-left: 0;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 24px;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
list-style-position: inside;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main li:not(:last-of-type) {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main code {
|
||||||
|
font-family: inherit;
|
||||||
|
background: var(--gray-alpha-100);
|
||||||
|
padding: 2px 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ctas {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ctas a,
|
||||||
|
.ctas button {
|
||||||
|
appearance: none;
|
||||||
|
border-radius: 128px;
|
||||||
|
height: 48px;
|
||||||
|
padding: 0 20px;
|
||||||
|
border: none;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
transition:
|
||||||
|
background 0.2s,
|
||||||
|
color 0.2s,
|
||||||
|
border-color 0.2s;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ctas .textarea {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.primary {
|
||||||
|
background: var(--foreground);
|
||||||
|
color: var(--background);
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.primary {
|
||||||
|
background: var(--foreground);
|
||||||
|
color: var(--background);
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.secondary {
|
||||||
|
border-color: var(--gray-alpha-200);
|
||||||
|
min-width: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
grid-row-start: 3;
|
||||||
|
display: flex;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer a {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer img {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable hover only on non-touch devices */
|
||||||
|
@media (hover: hover) and (pointer: fine) {
|
||||||
|
a.primary:hover {
|
||||||
|
background: var(--button-primary-hover);
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.secondary:hover {
|
||||||
|
background: var(--button-secondary-hover);
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
text-underline-offset: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.page {
|
||||||
|
padding: 32px;
|
||||||
|
padding-bottom: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main ol {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ctas {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ctas a {
|
||||||
|
font-size: 14px;
|
||||||
|
height: 40px;
|
||||||
|
padding: 0 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.secondary {
|
||||||
|
min-width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @media (prefers-color-scheme: dark) {
|
||||||
|
.logo {
|
||||||
|
filter: invert();
|
||||||
|
}
|
||||||
|
} */
|
78
frontend/src/app/page.tsx
Normal file
78
frontend/src/app/page.tsx
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import Image from "next/image";
|
||||||
|
import styles from "./page.module.css";
|
||||||
|
import { SimCurrentGear } from "./ui/forms/simCurrentGear";
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<div className={styles.page}>
|
||||||
|
<main className={styles.main}>
|
||||||
|
<img
|
||||||
|
className={styles.logo}
|
||||||
|
src={`/img/warcraft-icon-22.png`}
|
||||||
|
alt="Sim-Free Logo"
|
||||||
|
width={150}
|
||||||
|
height={150}
|
||||||
|
// priority
|
||||||
|
/>
|
||||||
|
<ol>
|
||||||
|
<li>
|
||||||
|
Copy/paste the text from the SimulationCraft addon. {}
|
||||||
|
<a
|
||||||
|
className={styles.primary}
|
||||||
|
target="_blank"
|
||||||
|
href="https://github.com/simulationcraft/simc-addon"
|
||||||
|
>
|
||||||
|
How to install and use the SimC addon
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>Select pieces of gear and Sim-Free will sim them</li>
|
||||||
|
</ol>
|
||||||
|
<SimCurrentGear />
|
||||||
|
</main>
|
||||||
|
<footer className={styles.footer}>
|
||||||
|
<a
|
||||||
|
href="https://github.com/alexohneander/sim_free/issues/new"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
aria-hidden
|
||||||
|
src="https://nextjs.org/icons/file.svg"
|
||||||
|
alt="File icon"
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
|
/>
|
||||||
|
Issues
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://github.com/alexohneander/sim_free/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
aria-hidden
|
||||||
|
src="https://nextjs.org/icons/window.svg"
|
||||||
|
alt="Window icon"
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
|
/>
|
||||||
|
Project
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://alexohneander.de"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
aria-hidden
|
||||||
|
src="https://nextjs.org/icons/globe.svg"
|
||||||
|
alt="Globe icon"
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
|
/>
|
||||||
|
Go to alexohneander.de →
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
76
frontend/src/app/ui/forms/simCurrentGear.tsx
Normal file
76
frontend/src/app/ui/forms/simCurrentGear.tsx
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import styles from "../../page.module.css";
|
||||||
|
|
||||||
|
export function SimCurrentGear() {
|
||||||
|
const [isFetched, setIsFetched] = useState(false);
|
||||||
|
const [fetchedData, setFetchedData] = useState("");
|
||||||
|
|
||||||
|
async function fetchSimResult(formData: FormData) {
|
||||||
|
try {
|
||||||
|
const response = await fetch("http://127.0.0.1:8000/sim/current_gear", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Access-Control-Allow-Origin": "*",
|
||||||
|
},
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Error! status: ${response.status}`);
|
||||||
|
} else {
|
||||||
|
setIsFetched(true);
|
||||||
|
setFetchedData(await response.text());
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof Error) {
|
||||||
|
// ✅ TypeScript knows err is Error
|
||||||
|
console.log(err.message);
|
||||||
|
} else {
|
||||||
|
console.log("Unexpected error", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<iframe
|
||||||
|
id="renderframe"
|
||||||
|
style={{
|
||||||
|
display: `${isFetched ? "block" : "none"}`,
|
||||||
|
width: "100%",
|
||||||
|
height: "100vh",
|
||||||
|
marginBottom: "50px",
|
||||||
|
}}
|
||||||
|
srcDoc={`${fetchedData}`}
|
||||||
|
/>
|
||||||
|
<form action={fetchSimResult}>
|
||||||
|
<div className={styles.ctas}>
|
||||||
|
<textarea
|
||||||
|
className={styles.textarea}
|
||||||
|
rows={10}
|
||||||
|
id="simcprofile"
|
||||||
|
name="simcprofile"
|
||||||
|
style={{
|
||||||
|
display: `${isFetched ? "none" : "block"}`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={styles.ctas}>
|
||||||
|
<button className={styles.primary} type="submit">
|
||||||
|
Run Sim
|
||||||
|
</button>
|
||||||
|
<a
|
||||||
|
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className={styles.secondary}
|
||||||
|
>
|
||||||
|
Read our docs
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
26
frontend/tsconfig.json
Normal file
26
frontend/tsconfig.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"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/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user