diff --git a/package-lock.json b/package-lock.json index 50ca489..408501d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", + "@mui/icons-material": "^6.0.1", "@mui/material": "^6.0.1", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", @@ -3394,6 +3395,31 @@ "url": "https://opencollective.com/mui-org" } }, + "node_modules/@mui/icons-material": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.0.1.tgz", + "integrity": "sha512-CsgaF65jA3H1YzpDg6H2nFH/UHueVlmpEtPim7xF9VbjYnmnblG3aX0GflBahH96Pg0schrFWyRySlgbVAh5Kw==", + "dependencies": { + "@babel/runtime": "^7.25.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^6.0.1", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@mui/material": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.0.1.tgz", @@ -23069,6 +23095,14 @@ "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.0.1.tgz", "integrity": "sha512-TmKkCTwgtwvlFTF1tZzG4lYbi7v6NGweEJwFBZoIWZrkF1OLa0xu4umifmIyd+bVIScsEj//E2AD6bOJbPMOOQ==" }, + "@mui/icons-material": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.0.1.tgz", + "integrity": "sha512-CsgaF65jA3H1YzpDg6H2nFH/UHueVlmpEtPim7xF9VbjYnmnblG3aX0GflBahH96Pg0schrFWyRySlgbVAh5Kw==", + "requires": { + "@babel/runtime": "^7.25.0" + } + }, "@mui/material": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.0.1.tgz", diff --git a/package.json b/package.json index c45e3fd..552dcb2 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "dependencies": { "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", + "@mui/icons-material": "^6.0.1", "@mui/material": "^6.0.1", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", diff --git a/public/index.html b/public/index.html index aa069f2..b2ea4e0 100644 --- a/public/index.html +++ b/public/index.html @@ -2,42 +2,22 @@ - - - - + - - React App + OSRS Leagues Planner
diff --git a/src/App.js b/src/App.js index 358d6ff..d28a231 100644 --- a/src/App.js +++ b/src/App.js @@ -6,6 +6,7 @@ import StepList from './components/StepList'; import StepDetails from './components/StepDetails'; import Inventory from './components/Inventory'; import MapView from './components/MapView'; +import SkillTable from './components/SkillTable'; import './App.css'; import useSteps from './hooks/useSteps'; import { handleFileUpload, handleDownloadCSV } from './utils/csvUtils'; @@ -80,6 +81,7 @@ function App() { <> + { +for (let i = xpTable.length - 1; i >= 0; i--) { + if (xp >= xpTable[i].xp) { + return xpTable[i].level; + } +} +return 1; // Default to level 1 if no match +}; + +const SkillTable = ({ steps, currentStep }) => { + const [open, setOpen] = useState(false); + const [skillXP, setSkillXP] = useState({}); + + useEffect(() => { + const skillXPMap = {}; + + // Calculate XP gains from all previous tasks + for (let i = 0; i < steps.length; i++) { + steps[i].description.split('\n').forEach(line => { + skills.forEach(skill => { + const regex = new RegExp(`${skill}\\s*\\+([0-9,]+)\\s*XP`, 'i'); + const match = line.match(regex); + if (match) { + const xpValue = parseInt(match[1].replace(/,/g, ''), 10); + if (!skillXPMap[skill]) { + skillXPMap[skill] = { total: 0, includingCurrent: 0 }; + } + if (i < currentStep) { + skillXPMap[skill].total += xpValue; + } + if (i <= currentStep) { + skillXPMap[skill].includingCurrent += xpValue; + } + } + }); + }); + } + + setSkillXP(skillXPMap); + }, [steps, currentStep]); + + // Filter skills with XP + const skillsWithXP = Object.keys(skillXP).filter(skill => skillXP[skill].total > 0 || skillXP[skill].includingCurrent > 0); + + return ( + + + + setOpen(!open)} size="small"> + {open ? : } + Skill XP Tracker + + + + + + + Skill + Total XP + Including Current + + + + {skillsWithXP.map(skill => ( + + {skill} + + {skillXP[skill]?.total.toLocaleString()} ({getLevelFromXP(skillXP[skill]?.total)}) + + + {skillXP[skill]?.includingCurrent.toLocaleString()} ({getLevelFromXP(skillXP[skill]?.includingCurrent)}) + + + ))} + +
+
+
+
+ + + +
+
+ ); +}; + +export default SkillTable; diff --git a/src/components/TaskTracker.js b/src/components/TaskTracker.js new file mode 100644 index 0000000..f5533fa --- /dev/null +++ b/src/components/TaskTracker.js @@ -0,0 +1,46 @@ +import React, { useState, useEffect } from 'react'; +import { Box, Typography } from '@mui/material'; + +const TaskTracker = ({ steps, currentStep }) => { + const [tasksBefore, setTasksBefore] = useState(0); + const [tasksIncluding, setTasksIncluding] = useState(0); + const [pointsBefore, setPointsBefore] = useState(0); + const [pointsIncluding, setPointsIncluding] = useState(0); + + useEffect(() => { + let beforeTasks = 0; + let beforePoints = 0; + let includingTasks = 0; + let includingPoints = 0; + + for (let i = 0; i <= currentStep; i++) { + const taskMatches = steps[i].description.match(/<(\d+)>/g); + if (taskMatches) { + taskMatches.forEach(match => { + const points = parseInt(match.replace(/[<>]/g, ''), 10); + includingTasks += 1; + includingPoints += points; + if (i < currentStep) { + beforeTasks += 1; + beforePoints += points; + } + }); + } + } + + setTasksBefore(beforeTasks); + setPointsBefore(beforePoints); + setTasksIncluding(includingTasks); + setPointsIncluding(includingPoints); + }, [steps, currentStep]); + + return ( + + + {tasksIncluding} tasks incl. current ({pointsIncluding} pts) + + + ); +}; + +export default TaskTracker;