import './reset.css'; // Generic reset import './style.css'; // The new new import React, {useState,useEffect,useReducer} from 'react'; import Toggle from 'react-toggle' //Tooltip props: http://aaronshaf.github.io/react-toggle/ import Helmet from 'react-helmet' import {TrashFill, PlusCircle, LifePreserver, Server, CloudUploadFill} from 'react-bootstrap-icons' import { SkillTreeEditor } from './skilltree/skillTreeEditor' import { HashRouter, Switch, Route, useHistory } from "react-router-dom"; import { HashLink as Link } from 'react-router-hash-link'; import TestHeader from './TestHeader'; // Test Header! import TestPanel from './TestPanel'; // Dudley's Test Panel import md5 from 'md5'; const axios = require('axios'); const parse = require('csv-parse/lib/sync') /* Damage types const MELEE_DMG = 0 const RANGE_DMG = 1 const TECH_DMG = 2 Art properties const NORMAL = 0 const PHOTON_ART = 1 const WEAPON_ACTION = 2 const STEP_COUNTER = 3 const PARRY_COUNTER = 4 //NOT USED YET*/ const BACKENDURL=process.env.REACT_APP_GITPOD_WORKSPACE_URL||process.env.REACT_APP_BACKENDURL||'https://projectdivar.com:4504' const APP_TITLE = "NGS Planner" function GetBackendURL(p) { return (BACKENDURL)+(p.TESTMODE?"/test":"") } function Box(p) { return <> <div className="box"> <div className="boxTitleBar"> <h1>{p.title}</h1> </div> {p.children} </div> </> } function Table(p) { return <p className={p.classes}> {p.children} </p> } function InputBox(p) { const [value,setValue] = useState(p.value) const [failed,setFailed] = useState(false) const [sending,setSending] = useState(false) function changeFunc(f){setValue(f.currentTarget.value) if (p.callback4) { p.callback4(f.currentTarget.value) }} function blurFunc(f){ if (p.callback) { setSending(true) setFailed(false) p.callback(f.currentTarget.value) .then(()=>{setFailed(false)}) .catch(()=>{setFailed(true)}) .then(()=>{setSending(false)})} else if (p.callback3) { p.callback3(f.currentTarget.value) }} function keydownFunc(f){ if (p.callback2) { p.callback2(f,value) } } return p.data?<select disabled={p.lockSubmission} className={failed?"failedInput":sending?"submitting":""} value={value} onKeyDown={(f)=>{keydownFunc(f)}} onChange={(f)=>{changeFunc(f)}} onBlur={(f)=>{blurFunc(f)}}> {p.includeBlankValue&&<option/>} {p.data.map((item)=><option value={item.id}>{item.id} - {item.name||item.username}</option>)} </select>:<input disabled={p.lockSubmission} className={failed?"failedInput":sending?"submitting":""} value={value} onKeyDown={(f)=>{keydownFunc(f)}} onChange={(f)=>{changeFunc(f)}} onBlur={(f)=>{blurFunc(f)}}/> } function TableEditor(p) { const initialVals={} const { TESTMODE } = p function updateVals(state,update) { if (update==='Clear') { return initialVals } state[update.field]=update.value return state } const [fields,setFields] = useState([]) const [data,setData] = useState([]) const [update,setUpdate] = useState(false) const [submitVals,setSubmitVal] = useReducer(updateVals,initialVals) const [loading,setLoading] = useState(false) const [dependencies,setDependencies] = useState([]) const [importAllowed,setImportAllowed] = useState(false) const [lockSubmission,setLockSubmission] = useState(false) function patchValue(value,p,col,dat) { return axios.patch(p.BACKENDURL+p.path,{ [col.name]:value==="null"?null:value, id:dat.id, pass:p.password }) } function SubmitBoxes() { if (!lockSubmission) { setLockSubmission(true) axios.post(p.BACKENDURL+p.path,{...submitVals,pass:p.password}) .then(()=>{ setSubmitVal("Clear") setUpdate(true) }) .catch((err)=>{ alert(JSON.stringify(err.response.data)) }) .then(()=>{ setLockSubmission(false) }) } } function SubmitDeletion() { if (!lockSubmission) { setLockSubmission(true) var promises = [] for (var dat of data) { if (document.getElementById("delete_"+dat.id).checked) { promises.push(axios.delete(p.BACKENDURL+p.path,{data:{pass:p.password,id:dat.id}})) } } Promise.allSettled(promises) .catch((err)=>{ alert(err.message) }) .then((data)=>{ setLockSubmission(false) setUpdate(true) }) } } useEffect(()=>{ setUpdate(true) },[p.path,TESTMODE]) useEffect(()=>{ for (var col of fields) { if (col.name==="name") { setImportAllowed(true) break; } } },[fields]) useEffect(()=>{ if (update) { setLoading(true) var dependency_map = {} axios.get(p.BACKENDURL+p.path+"?pass="+p.password) .then((data)=>{ var cols = data.data.fields var rows = data.data.rows setFields(cols.filter((col,i)=>col.name!=="id"&&!(i===0&&col.name==="name"))) var promise_list = [] cols.filter((col)=>col.name!=="id"&&col.name.includes("_id")).forEach((col)=>{ promise_list.push(axios.get(p.BACKENDURL+"/"+col.name.replace("_id","")+"?pass="+p.password) .then((data)=>{ dependency_map[col.name]=data.data.rows.sort((a,b)=>b.id-a.id) })) }) setData(rows) return Promise.allSettled(promise_list) }) .then(()=>{ setDependencies(dependency_map) setLoading(false) }) setUpdate(false) } },[update,p.path,p.BACKENDURL,p.password]) return <> {!loading? <div> <table> {importAllowed&&<caption><label className="buttonLabel" for="uploads">Import CSV</label><input onChange={(f)=>{ const reader = new FileReader() reader.onload=(ev)=>{ var promises=[] parse(ev.target.result,{columns:true,skip_empty_lines:true}).forEach((entry)=>{ for (var col of fields) { if ((col.dataTypeID===23||col.dataTypeID===701||col.dataTypeID===16)&&entry[col.name]==="") { entry[col.name]=0 } } promises.push(axios.post(p.BACKENDURL+p.path,{...entry,pass:p.password})) }) Promise.allSettled(promises) .then(()=>{ setUpdate(true) }) } reader.readAsText(f.target.files[0]) }} style={{opacity:0}} id="uploads" type="file" accept=".txt,.csv"/></caption>} <thead> <tr> <th className="table-padding"><TrashFill onClick={()=>{SubmitDeletion()}} className="trashButton"/></th> {fields.map((field,i)=><React.Fragment key={i}><th scope="col" className="table-padding">{field.name}</th></React.Fragment>)} </tr> </thead> <tbody> {<tr><td></td>{fields.map((col,i)=><td key={i}>{<InputBox includeBlankValue={true} data={dependencies[col.name]} callback4={ (f)=>{setSubmitVal({field:col.name,value:f});}}/>}</td>)}<input style={{display:"none"}}/><PlusCircle onClick={()=>{SubmitBoxes()}} className="submitbutton"/></tr>} {data.map((dat)=><tr key={dat.id}> <td><input id={"delete_"+dat.id} type="checkbox"/></td>{fields.map((col,i)=><td key={dat.id+"_"+i} className="table-padding table"> <InputBox lockSubmission={lockSubmission} data={dependencies[col.name]} callback={(value)=>patchValue(value,p,col,dat)} callback2={(f,value)=>{if (f.key==='Enter') {f.currentTarget.blur()} else {return 'Chill'}}} value={String(dat[col.name])}/></td>)}</tr>)} </tbody> </table> </div>:<><img src={process.env.PUBLIC_URL+"/spinner.gif"} alt=""/><img src={process.env.PUBLIC_URL+"/spinner.gif"} alt=""/><img src={process.env.PUBLIC_URL+"/spinner.gif"} alt=""/><img src={process.env.PUBLIC_URL+"/spinner.gif"} alt=""/><img src={process.env.PUBLIC_URL+"/spinner.gif"} alt=""/><img src={process.env.PUBLIC_URL+"/spinner.gif"} alt=""/><img src={process.env.PUBLIC_URL+"/spinner.gif"} alt=""/><img src={process.env.PUBLIC_URL+"/spinner.gif"} alt=""/><img src={process.env.PUBLIC_URL+"/spinner.gif"} alt=""/><img src={process.env.PUBLIC_URL+"/spinner.gif"} alt=""/><img src={process.env.PUBLIC_URL+"/spinner.gif"} alt=""/><img src={process.env.PUBLIC_URL+"/spinner.gif"} alt=""/><img src={process.env.PUBLIC_URL+"/spinner.gif"} alt=""/><img src={process.env.PUBLIC_URL+"/spinner.gif"} alt=""/><img src={process.env.PUBLIC_URL+"/spinner.gif"} alt=""/><img src={process.env.PUBLIC_URL+"/spinner.gif"} alt=""/><img src={process.env.PUBLIC_URL+"/spinner.gif"} alt=""/></>} </> } function DatabaseEditor(p) { const [loading,setLoading] = useState(true) const [message,setMessage] = useState(<span style={{color:"black"}}></span>) const [databases,setDatabases] = useState([]) const [update,setUpdate] = useState(true) useEffect(()=>{ if (update) { axios.get(p.BACKENDURL+"/databases?pass="+p.password) .then((data)=>{ setDatabases(data.data) }) .catch((err)=>{ console.log(err.message) }) .then(()=>{ setLoading(false) }) setUpdate(false) } },[update,p.BACKENDURL,p.password]) return <> {!loading?<> <button className="basichover" style={{backgroundColor:"navy"}} onClick={()=>{ setLoading(true) setMessage(<span style={{color:"black"}}>Uploading Test Database to Production...</span>) axios.post(p.BACKENDURL+"/databases/testtolive",{pass:p.password}) .then(()=>{ setMessage(<span style={{color:"green"}}>Success! Test Database is now live!</span>) }) .catch((err)=>{ setMessage(<span style={{color:"red"}}>{err.message}</span>) }) .then(()=>{ setLoading(false) }) }}>Apply TEST Database to LIVE Database</button><br/><br/> <button className="basichover" style={{backgroundColor:"maroon"}} onClick={()=>{ setLoading(true) setMessage(<span style={{color:"black"}}>Restoring Test Database using Live Database...</span>) axios.post(p.BACKENDURL+"/databases/livetotest",{pass:p.password}) .then(()=>{ setMessage(<span style={{color:"green"}}>Success! Live Database has been applied to the Test Database!</span>) }) .catch((err)=>{ setMessage(<span style={{color:"red"}}>{err.message}</span>) }) .then(()=>{ setLoading(false) }) }}>Reset TEST database using current LIVE Database</button><br/><br/> <button className="basichover" style={{backgroundColor:"darkgreen"}} onClick={()=>{ setLoading(true) setMessage(<span style={{color:"black"}}>Backing up the Live database...</span>) axios.post(p.BACKENDURL+"/databases/backup",{pass:p.password}) .then(()=>{ setMessage(<span style={{color:"green"}}>Success! Live Database has been saved!</span>) }) .catch((err)=>{ setMessage(<span style={{color:"red"}}>{err.message}</span>) }) .then(()=>{ setUpdate(true) })}}>Backup current LIVE Database</button><br/><br/> </>:<img src={process.env.PUBLIC_URL+"/spinner.gif"} alt=""/> } {message} <hr/> <br/><br/> <h2><u>Current Databases</u></h2> <br/><br/> <span style={{fontSize:"24px",top:"-16px",position:"relative",height:"64px",lineHeight:"64px",textAlign:"center"}}><LifePreserver className="databaseIcon" style={{color:"green"}}/>Live Database</span> <span style={{fontSize:"24px",top:"-16px",position:"relative",height:"64px",lineHeight:"64px",textAlign:"center"}}><LifePreserver className="databaseIcon" style={{color:"red"}}/>Test Database</span><br/> {databases.map((db)=>{ var label = "" if (db.datname!=="ngsplanner"&&db.datname!=="ngsplanner2") { var dateStr = db.datname.replace("ngsplanner","") var date = new Date(dateStr.slice(0,4),dateStr.slice(4,6),dateStr.slice(6,8),dateStr.slice(8,10),dateStr.slice(10,12),dateStr.slice(12,14)) label=<><Server className="databaseIcon" style={{color:"blue"}}/>{"Backup from "+date}</> return <><span style={{fontSize:"24px",top:"-16px",position:"relative",height:"64px",lineHeight:"64px",textAlign:"center"}}>{label}<button style={{background:"blue"}} onClick={()=>{ setLoading(true) axios.post(p.BACKENDURL+"/databases/restorefrombackup",{ database:db.datname, pass:p.password }) .then((data)=>{ setMessage(<span style={{color:"green"}}>{"Success! Database has been set to the state from "+date}</span>) }) .catch((err)=>{ setMessage(<span style={{color:"red"}}>{err.message}</span>) }) .then(()=>{ setLoading(false) }) }}><CloudUploadFill/> Restore</button></span><br/></> } else { return <></> } })} </> } function AdminPanel(p) { const [verified,setVerified] = useState(false) const [password,setPassword] = useState("") const navigationData=[ {page:"Class",url:"/admin/class",table:"/class"}, {page:"Class Data",url:"/admin/classdata",table:"/class_level_data"}, {page:"Class-Weapon Compatibility",url:"/admin/classweaponcompatibility",table:"/class_weapon_type_data"}, {page:"Class Skills",url:"/admin/classskills",table:"/class_skill"}, {page:"Class Skill Data",url:"/admin/classskilldata",table:"/class_skill_data"}, {hr:true}, {page:"Weapons",url:"/admin/weapons",table:"/weapon"}, {page:"Weapon Existence Data",url:"/admin/weaponexistencedata",table:"/weapon_existence_data"}, {page:"Weapon Types",url:"/admin/weapontypes",table:"/weapon_type"}, {page:"Class-Weapon Compatibility",url:"/admin/classweaponcompatibility",table:"/class_weapon_type_data",duplicate:true}, {page:"Photon Arts",url:"/admin/photonarts",table:"/photon_art"}, {hr:true}, {page:"Armor",url:"/admin/armor",table:"/armor"}, {page:"Potentials",url:"/admin/potentials",table:"/potential"}, {page:"Potential Data",url:"/admin/potentialdata",table:"/potential_data"}, {hr:true}, {page:"Builds",url:"/admin/builds",table:"/builds"}, {hr:true}, {page:"Skills",url:"/admin/skills",table:"/skill"}, {page:"Skill Types",url:"/admin/skilltypes",table:"/skill_type"}, {page:"Skill Data",url:"/admin/skilldata",table:"/skill_data"}, {page:<span style={{color:"gold"}}>Skill Tree Editor</span>,url:"/admin/skilltreeeditor",render:<SkillTreeEditor setUpdate={p.setUpdate} password={password} BACKENDURL={GetBackendURL(p)} GetData={p.DATA}/>}, {page:"Skill Tree Data",url:"/admin/skilltreedata",table:"/skill_tree_data"}, {page:"Photon Arts",url:"/admin/photonarts",table:"/photon_art",duplicate:true}, {page:"Class Skills",url:"/admin/classskills",table:"/class_skill",duplicate:true}, {page:"Class Skill Data",url:"/admin/classskilldata",table:"/class_skill_data",duplicate:true}, {hr:true}, {page:"Augments",url:"/admin/augments",table:"/augment"}, {page:"Augment Types",url:"/admin/augmenttypes",table:"/augment_type"}, {hr:true}, {page:"Enemy Data",url:"/admin/enemydata",table:"/enemy_data"}, {hr:true}, {page:"Food",url:"/admin/food",table:"/food"}, {page:"Food Multipliers",url:"/admin/foodmultipliers",table:"/food_mult"}, {hr:true}, {page:"Roles",url:"/admin/roles",table:"/roles"}, {page:"Users",url:"/admin/users",table:"/users"}, {hr:true}, {page:"Misc. Site Data",url:"/admin/sitedata",table:"/site_data"}, {page:"Database Audit",url:"/admin/database_audit",table:"/database_audit"}, ] return <div className="adminMain"> {!verified? <div className="modalOverlay"> <div className="modal"> <div className="box boxAdmin"> <div className="boxTitleBar"><h1>Admin</h1></div> <p></p> <input type="password" value={password} onChange={(f)=>{setPassword(f.currentTarget.value)}} onKeyDown={(e)=>{ if (e.key==="Enter") { axios.post(GetBackendURL(p)+"/passwordcheck",{ pass:password }) .then((data)=>{ if (data.data.verified) { setVerified(data.data.verified) } }) .catch((err)=>{ setVerified(false) setPassword("") })}}}></input> </div> </div> </div> :<> <div className="box boxAdminNav"> <div className="boxTitleBar"> <h1>Navigation</h1> </div> <p>Testing Mode <Toggle checked={p.TESTMODE} onChange={(f)=>{p.setTESTMODE(f.target.checked)}}/> {p.TESTMODE?<b>ON</b>:<b>OFF</b>}</p> <div className="adminNavContainer customScrollbar"> <Table classes="adminNav"> {navigationData.map((nav)=>(nav.hr)?<hr/>:<><Link to={process.env.PUBLIC_URL+nav.url}>{nav.page}</Link><br/></>)} <Link to={process.env.PUBLIC_URL+"/admin/database_manager"}>Database Manager</Link><br/> </Table> </div> </div> {navigationData.map((nav)=>(nav.duplicate===undefined&&nav.hr===undefined)&&<Route path={process.env.PUBLIC_URL+nav.url}> <div className="box boxAdminContent"> <div className="boxTitleBar"> <h1>{nav.page}</h1></div> <div className="adminContainer customScrollbar"> <Helmet> <title>{APP_TITLE+" - Admin Panel: "+nav.page}</title> </Helmet> {nav.render??<TableEditor TESTMODE={p.TESTMODE} password={password} BACKENDURL={GetBackendURL(p)} path={nav.table}/>} </div></div></Route>)} <Route path={process.env.PUBLIC_URL+"/admin/database_manager"}> <div className="box boxAdminContent"> <div className="boxTitleBar"> <h1>Database Editor</h1></div> <div className="adminContainer"> <DatabaseEditor password={password} BACKENDURL={GetBackendURL(p)}/> </div> </div> </Route> </>} </div> } function EditStatBox(p) { const [value,setValue] = useState(p.value) useEffect(()=>{ setValue(p.value) },[p.value]) return <><input value={value} onChange={(f)=>{setValue(f.currentTarget.value);p.callback(f.currentTarget.value)}}/> ({value})<br/></> } function DamageCalculator(p) { const [augmentData,setAugmentData] = useState({}) //const [update,setUpdate] = useState(false) useEffect(()=>{ axios.get(p.BACKENDURL+"/augment") .then((data)=>{ var augmentData = {} data.data.rows.forEach((entry)=>{augmentData[entry.name]=entry}) setAugmentData(augmentData) }) },[p.BACKENDURL]) const character = { weapon:{ augments:["1","2"] }, armor1:{ augments:["2"] }, armor2:{ augments:[] }, armor3:{ augments:[] } } useEffect(()=>{ if (Object.keys(augmentData).length>0) { var searchFields = [{field:"variance",variable:0},{field:"mel_dmg",variable:0}] for (var equip of [character.weapon,character.armor1,character.armor2,character.armor3]) { for (var field of searchFields) { for (var i=0;i<equip.augments.length;i++) { var variance = augmentData[equip.augments[i]][field.field] field.variable+=variance } } } setAugDmgVariance(searchFields[0].variable) } },[augmentData,character.armor1,character.armor2,character.armor3,character.weapon]) const [rawDmg,setRawDmg] = useState(0) const [weaponTotalAtk,setWeaponTotalAtk] = useState(100) const [weaponBaseAtk,setWeaponBaseAtk] = useState(1) const [weaponEnhanceLv,setweaponEnhanceLv] = useState(1) useEffect(()=>{ setWeaponTotalAtk(Number(weaponBaseAtk)+Number(weaponEnhanceLv)) },[weaponBaseAtk,weaponEnhanceLv]) const [dmgVariance,setDmgVariance] = useState(1) const [weaponDmgVariance,setWeaponDmgVariance] = useState(1) const [augDmgVariance,setAugDmgVariance] = useState(1) useEffect(()=>{ setDmgVariance(Number(weaponDmgVariance)+Number(augDmgVariance)) },[weaponDmgVariance,augDmgVariance]) const [baseAtk,setBaseAtk] = useState(100) const [enemyDef,setEnemyDef] = useState(5) const [multipliers,setMultipliers] = useState(1) useEffect(()=>{ setRawDmg(((Number(weaponTotalAtk)*Number(dmgVariance))+Number(baseAtk)-Number(enemyDef))*Number(multipliers)/5) },[weaponTotalAtk,dmgVariance,baseAtk,enemyDef,multipliers]) const [atkmult,setAtkMult] = useState(1); const [partmult,setPartMult] = useState(1); const [elementalWeaknessMult,setElementalWeaknessMult] = useState(1.2) const [mainClassWeaponBoost,setMainClassWeaponBoost] = useState(1.1) const [classSkillMult,setClassSkillMult] = useState(1) const [equipMult,setEquipMult] = useState(1) const [augmentEquipMult,setAugmentEquipMult] = useState(1) const [potencyFloorEquipMult,setPotencyFloorEquipMult] = useState(1) const [elementalWeaponEquipMult,setElementalWeaponEquipMult] = useState(1.1) const [critMult,setCritMult] = useState(1.2) const [appropriateDistance,setAppropriateDistance] = useState(1) const [foodBoost,setFoodBoost] = useState(1) const [fieldEffects,setFieldEffects] = useState(1.05) const [statusAilments,setStatusAilments] = useState(1) const [enemyCorrectionMult,setEnemyCorrectionMult] = useState(1) const [highLevelEnemy,setHighLevelEnemy] = useState(1) useEffect(()=>{ setMultipliers(Number(atkmult)*Number(partmult)*Number(elementalWeaknessMult)*Number(mainClassWeaponBoost)*Number(classSkillMult)*Number(equipMult)*Number(augmentEquipMult)*Number(potencyFloorEquipMult)*Number(elementalWeaponEquipMult)*Number(critMult)*Number(appropriateDistance)*Number(foodBoost)*Number(fieldEffects)*Number(statusAilments)*Number(enemyCorrectionMult)*Number(highLevelEnemy)) },[atkmult,partmult,elementalWeaknessMult,mainClassWeaponBoost,classSkillMult,equipMult,augmentEquipMult,potencyFloorEquipMult,elementalWeaponEquipMult,critMult,appropriateDistance,foodBoost,fieldEffects,statusAilments,enemyCorrectionMult,highLevelEnemy]) return <> <div style={{background:"rgba(200,255,200,1)"}}> Weapon Total Atk:<EditStatBox value={weaponTotalAtk} callback={(val)=>{setWeaponTotalAtk(val)}}/> <ul> <li>●Weapon Base Atk:<EditStatBox value={weaponBaseAtk} callback={(val)=>{setWeaponBaseAtk(val)}}/></li> <li>●Weapon Enhance Lv:<EditStatBox value={weaponEnhanceLv} callback={(val)=>{setweaponEnhanceLv(val)}}/></li> </ul> <br/><br/><br/> Damage Variance:<EditStatBox value={dmgVariance} callback={(val)=>{setDmgVariance(val)}}/> <ul> <li>●Weapon Damage Variance:<EditStatBox value={weaponDmgVariance} callback={(val)=>{setWeaponDmgVariance(val)}}/></li> <li>●Augment Damage Variance:<EditStatBox value={augDmgVariance} callback={(val)=>{setAugDmgVariance(val)}}/></li> </ul> <br/><br/><br/> Base Attack:<EditStatBox value={baseAtk} callback={(val)=>{setBaseAtk(val)}}/> Enemy Defense:<EditStatBox value={enemyDef} callback={(val)=>{setEnemyDef(val)}}/> Multipliers:<EditStatBox value={multipliers} callback={(val)=>{setMultipliers(val)}}/> <ul> <li>●Atk Mult:<EditStatBox value={atkmult} callback={(val)=>{setAtkMult(val)}}/></li> <li>●Part Mult:<EditStatBox value={partmult} callback={(val)=>{setPartMult(val)}}/></li> <li>●Elemental Weakness Mult:<EditStatBox value={elementalWeaknessMult} callback={(val)=>{setElementalWeaknessMult(val)}}/></li> <li>●Main Class Weapon Boost:<EditStatBox value={mainClassWeaponBoost} callback={(val)=>{setMainClassWeaponBoost(val)}}/></li> <li>●Class Skill Mult:<EditStatBox value={classSkillMult} callback={(val)=>{setClassSkillMult(val)}}/></li> <li>●Equip Mult:<EditStatBox value={equipMult} callback={(val)=>{setEquipMult(val)}}/></li> <li> <ul> <li>●Augment Equip Mult:<EditStatBox value={augmentEquipMult} callback={(val)=>{setAugmentEquipMult(val)}}/></li> <li>●Potency Floor Equip Mult:<EditStatBox value={potencyFloorEquipMult} callback={(val)=>{setPotencyFloorEquipMult(val)}}/></li> <li>●Elemental Weapon Equip Mult:<EditStatBox value={elementalWeaponEquipMult} callback={(val)=>{setElementalWeaponEquipMult(val)}}/></li> </ul> </li> <li>●Crit Mult:<EditStatBox value={critMult} callback={(val)=>{setCritMult(val)}}/></li> <li>●Appropriate Distance:<EditStatBox value={appropriateDistance} callback={(val)=>{setAppropriateDistance(val)}}/></li> <li>●Food Boost:<EditStatBox value={foodBoost} callback={(val)=>{setFoodBoost(val)}}/></li> <li>●Field Effects:<EditStatBox value={fieldEffects} callback={(val)=>{setFieldEffects(val)}}/></li> <li>●Status Ailments:<EditStatBox value={statusAilments} callback={(val)=>{setStatusAilments(val)}}/></li> <li>●Enemy Correction Multiplier:<EditStatBox value={enemyCorrectionMult} callback={(val)=>{setEnemyCorrectionMult(val)}}/></li> <li>●High Level Enemy:<EditStatBox value={highLevelEnemy} callback={(val)=>{setHighLevelEnemy(val)}}/></li> </ul> <br/><br/><br/> Raw Dmg:{rawDmg} </div> </> } function FormField(p) { return <><label className="formField" for={p.field}>{p.label}</label>{ p.type==="toggle"?<><Toggle id={p.field} checked={p.checked} onChange={p.onChange} disabled={p.loading}/> <label className="formDescription" for={p.field}>{p.checked?<b>YES</b>:<b>NO</b>}</label></>:<input type={p.type??"text"} disabled={p.loading} id={p.field} maxlength={p.maxlength} value={p.value} checked={p.checked} onChange={p.onChange} placeholder={p.placeholder}/>} <label className="formDescription" for={p.field}>{p.tooltip}</label></> } function VerifyLogin(p) { axios.post(GetBackendURL(p)+"/validUser",{ username:p.LOGGEDINUSER, password:p.LOGGEDINHASH }) .then((data)=>{ if (data.data.verified) { p.history.push("/") } }) .catch((err)=>{ console.log(err.message) }) } function LoginForm(p) { const [username,setUsername] = useState("") const [password,setPassword] = useState("") const [rememberMe,setRememberMe] = useState(false) const [error,setError] = useState("") const [loading,setLoading] = useState(false) const history = useHistory() useEffect(()=>{ VerifyLogin({...p,history:history}) },[history,p]) function SubmitLogin() { setError("") setLoading(true) axios.post(GetBackendURL(p)+"/validUser",{ username:username, password:md5(password) }) .then((data)=>{ if (data.data.verified) { p.setLOGGEDINUSER(username) p.setLOGGEDINHASH(md5(password)) setUsername("") setPassword("") setRememberMe(false) history.push("/") } else { setError("Could not authenticate!") } }) .catch((err)=>{ setError(err?.message??err); }) .then(()=>{ setLoading(false) }) } return <> <Box title="Login Form"> {loading? <img src={process.env.PUBLIC_URL+"/spinner.gif"} alt="" style={{background:"linear-gradient(white,#bca9f5)",marginTop:"10px"}} /> :<><div onKeyDown={(f)=>{if (f.key==="Enter") {SubmitLogin()}}}><h3 className="formError">{error}</h3> <FormField field="username" label="Username: " value={username} maxlength={20} onChange={(p)=>{setUsername(p.currentTarget.value)}} placeholder="Username"/><br/> <FormField field="password" label="Password: " type="password" value={password} onChange={(p)=>{setPassword(p.currentTarget.value)}} placeholder="Password"/><br/> <FormField field="rememberMe" label="Remember Me " type="toggle" checked={rememberMe} onChange={(p)=>{setRememberMe(p.currentTarget.checked)}}/><br/> <button type="submit" onClick={SubmitLogin}>Login</button></div></> } </Box></> } function RegisterForm(p) { const [username,setUsername] = useState("") const [password,setPassword] = useState("") const [password2,setPassword2] = useState("") const [email,setEmail] = useState("") const [rememberMe,setRememberMe] = useState(false) const [error,setError] = useState("") const [loading,setLoading] = useState(false) const history = useHistory() useEffect(()=>{ VerifyLogin({...p,history:history}) },[history,p]) function SubmitRegister() { setError("") setLoading(true) try{ if (username.length<4) {throw new Error("Username must be at least 4 characters in length.")} if (username.length>20) {throw new Error("Username must be less than 21 characters in length.")} if (password.length<6) {throw new Error("Password must contain at least 6 characters.")} if (password!==password2) {throw new Error("Password fields must match.")} if (!email.includes("@")) {throw new Error("Invalid E-mail.")} }catch(err){ setError(err?.message??err); setLoading(false) return } axios.post(GetBackendURL(p)+"/register",{ username:username, password:md5(password), email:email }) .then((data)=>{ if (data.data.verified) { p.setLOGGEDINUSER(username) p.setLOGGEDINHASH(md5(password)) setUsername("") setPassword("") setRememberMe(false) } else { setError("Could not authenticate!") } }) .catch((err)=>{ setError(err?.message??err); }) .then(()=>{ setLoading(false) }) } return <> <Box title="Registration Form"> {loading? <img src={process.env.PUBLIC_URL+"/spinner.gif"} alt="" style={{background:"linear-gradient(white,#bca9f5)",marginTop:"10px"}} /> :<><h3 className="formError">{error}</h3> <FormField field="username" label="Username: " value={username} maxlength={20} onChange={(p)=>{setUsername(p.currentTarget.value)}} placeholder="Username" tooltip="Enter a username (4-20 characters, a-z and _ only)"/><br/> <FormField field="password" label="Password: " type="password" value={password} onChange={(p)=>{setPassword(p.currentTarget.value)}} placeholder="Password" tooltip="Enter a password (6 or more characters)"/><br/> <FormField field="password2" label="Verify Password: " type="password" value={password2} onChange={(p)=>{setPassword2(p.currentTarget.value)}} placeholder="Verify Password" tooltip="Enter password again."/><br/> <FormField field="email" label="E-mail: " type="email" value={email} onChange={(p)=>{setEmail(p.currentTarget.value)}} placeholder="email@example.com" tooltip="This is used to send you password reset emails."/><br/> <FormField field="rememberMe" label="Remember Me " type="toggle" checked={rememberMe} onChange={(p)=>{setRememberMe(p.currentTarget.checked)}}/><br/> <button type="submit" onClick={SubmitRegister}>Login</button></> } </Box></> } function App() { const [author] = useState("Dudley") const [buildName] = useState("Fatimah") const [className] = useState("Ranger") const [secondaryClassName] = useState("Force") const [classLv] = useState(20) const [secondaryClassLv] = useState(15) const [bp] = useState(1330) const [hp] = useState(388) const [pp] = useState(154) const [weaponTotalAtk] = useState(282) const [baseAtk] = useState(650) const [statDisplayAtk,setstatDisplayAtk] = useState(282) useEffect(()=>{ setstatDisplayAtk(Number(weaponTotalAtk)+Number(baseAtk)) },[weaponTotalAtk,baseAtk]) const [def] = useState(932) const [weaponUp1] = useState(0.317) const [weaponUp2] = useState(0.241) const [weaponUp3] = useState(0.241) const [damageResist] = useState(0.18) const [burnResist] = useState(0) const [shockResist] = useState(0) const [panicResist] = useState(0) const [stunResist] = useState(0) const [freezeResist] = useState(0) const [blindResist] = useState(0) const [poisonResist] = useState(0) const [TESTMODE,setTESTMODE] = useState(false) const [DATA,setDATA] = useState(undefined) const [DATAID,setDATAID] = useState({GetData:()=>{}}) const [update,setUpdate] = useState(false) const [LOGGEDINUSER,setLOGGEDINUSER] = useState("") const [LOGGEDINHASH,setLOGGEDINHASH] = useState("") function GetData(table,row,col,id){ if (row===undefined) {row=''} if (col===undefined) {col=''} var data = id?DATAID:DATA if (!data) { return "no data" } else { return data!==undefined?data[table]!==undefined?data[table][row]!==undefined?data[table][row][col]!==undefined?data[table][row][col]:data[table][row]:data[table]:data:"no data" } } useEffect(()=>{ if (update) { setUpdate(false) axios.get(GetBackendURL({TESTMODE:TESTMODE})+"/data") .then((data)=>{ setDATA(data.data) }) axios.get(GetBackendURL({TESTMODE:TESTMODE})+"/dataid") .then((data)=>{ setDATAID(data.data) }) } },[update,TESTMODE]) useEffect(()=>{ axios.get(GetBackendURL({TESTMODE:TESTMODE})+"/data") .then((data)=>{ setDATA(data.data) }) axios.get(GetBackendURL({TESTMODE:TESTMODE})+"/dataid") .then((data)=>{ setDATAID(data.data) }) },[TESTMODE]) return ( <> <HashRouter> <Switch> <Route path={process.env.PUBLIC_URL+"/admin"}> <Helmet> <title>{APP_TITLE+" - Admin Panel"}</title> </Helmet> <AdminPanel setUpdate={setUpdate} setTESTMODE={setTESTMODE} BACKENDURL={BACKENDURL} TESTMODE={TESTMODE} DATA={GetData}/> </Route> <Route path={process.env.PUBLIC_URL+"/test"}> <Helmet> <title>{"Test - "+APP_TITLE}</title> </Helmet> <TestHeader/> <TestPanel author={author} buildName={buildName} className={className} secondaryClassName={secondaryClassName} classLv={classLv} secondaryClassLv={secondaryClassLv} bp={bp} hp={hp} pp={pp} def={def} weaponUp1={weaponUp1} weaponUp2={weaponUp2} weaponUp3={weaponUp3} damageResist={damageResist} burnResist={burnResist} shockResist={shockResist} panicResist={panicResist} stunResist={stunResist} freezeResist={freezeResist} blindResist={blindResist} poisonResist={poisonResist} statDisplayAtk={statDisplayAtk} GetData={GetData} /> </Route> <Route path={process.env.PUBLIC_URL+"/login"}> <Helmet> <title>{APP_TITLE+" - Login"}</title> </Helmet> <TestHeader/> <LoginForm BACKENDURL={BACKENDURL} TESTMODE={TESTMODE} LOGGEDINUSER={LOGGEDINUSER} LOGGEDINHASH={LOGGEDINHASH} setLOGGEDINHASH={setLOGGEDINHASH} setLOGGEDINUSER={setLOGGEDINUSER}/> </Route> <Route path={process.env.PUBLIC_URL+"/register"}> <Helmet> <title>{APP_TITLE+" - Register"}</title> </Helmet> <TestHeader/> <RegisterForm BACKENDURL={BACKENDURL} TESTMODE={TESTMODE} LOGGEDINUSER={LOGGEDINUSER} LOGGEDINHASH={LOGGEDINHASH} setLOGGEDINHASH={setLOGGEDINHASH} setLOGGEDINUSER={setLOGGEDINUSER}/> </Route> <Route path={process.env.PUBLIC_URL+"/formula"}> <DamageCalculator/> </Route> <Route path="/"> <Helmet> <title>{APP_TITLE}</title> </Helmet> <div className="modalOverlaySplash"> <div className="modal"> <div className="box boxMisc"> <div className="boxTitleBar"><h1>{GetData("site_data","h1","data")}</h1></div><h2>{GetData("site_data","h2","data")}</h2><p><img src={process.env.PUBLIC_URL+"/spinner.gif"} alt=""/> {GetData("site_data","UNDER_CONSTRUCTION_TEXT","data")}</p><br style={{clear:"both"}} /> </div> </div> <footer><a href="https://github.com/sigonasr2/ngsplanner/"><span className="github"> </span></a><a href="https://twitter.com/ngsplanner"><span className="twitter">@NGSPlanner</span></a></footer> </div> </Route> </Switch> </HashRouter> </> ); } export default App;