diff --git a/changelog.md b/changelog.md index 88f1fd1..d51df01 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,26 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Unreleased +## [1.2.1] - 2022-04-17 +Moved from **Github Pages** to **AWS Amplify** as pages has problems with single page web applications. + +### Added + +- Added help page. +- Added guide links to profession jobs. +- Added useFetchData custom hook, replaces the useEffect nested async function pattern. + +### Changes + +- Added media queries for multiple components. +- Featured events link will now open a new tab. +- Events will be displayed based on the current date. +- Quests system refactor. Improved maintainability and reduced overhead. + +### Fixed +- Quests not appearing when reference character data was not updating. +- Fixed failed search when using the searchbar within a character profile. + ## Released ## [1.2.0] - 2022-04-16 diff --git a/package-lock.json b/package-lock.json index 1e0af2d..c2def05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "xiv-tracker", - "version": "0.1.0", + "version": "1.2.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "xiv-tracker", - "version": "0.1.0", + "version": "1.2.1", "dependencies": { "@testing-library/jest-dom": "^5.16.2", "@testing-library/react": "^12.1.2", @@ -17,9 +17,6 @@ "react-router-dom": "^6.2.1", "react-scripts": "^5.0.0", "web-vitals": "^2.1.4" - }, - "devDependencies": { - "gh-pages": "^3.2.3" } }, "node_modules/@babel/code-frame": { @@ -4207,15 +4204,6 @@ "node": ">=8" } }, - "node_modules/array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/array.prototype.flat": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", @@ -6148,12 +6136,6 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.50.tgz", "integrity": "sha512-g5X/6oVoqLyzKfsZ1HsJvxKoUAToFMCuq1USbmp/GPIwJDRYV1IEcv+plYTdh6h11hg140hycCBId0vf7rL0+Q==" }, - "node_modules/email-addresses": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz", - "integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==", - "dev": true - }, "node_modules/emittery": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", @@ -7241,32 +7223,6 @@ "minimatch": "^3.0.4" } }, - "node_modules/filename-reserved-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", - "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/filenamify": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", - "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", - "dev": true, - "dependencies": { - "filename-reserved-regex": "^2.0.0", - "strip-outer": "^1.0.1", - "trim-repeated": "^1.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/filesize": { "version": "8.0.7", "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", @@ -7708,94 +7664,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gh-pages": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz", - "integrity": "sha512-jA1PbapQ1jqzacECfjUaO9gV8uBgU6XNMV0oXLtfCX3haGLe5Atq8BxlrADhbD6/UdG9j6tZLWAkAybndOXTJg==", - "dev": true, - "dependencies": { - "async": "^2.6.1", - "commander": "^2.18.0", - "email-addresses": "^3.0.1", - "filenamify": "^4.3.0", - "find-cache-dir": "^3.3.1", - "fs-extra": "^8.1.0", - "globby": "^6.1.0" - }, - "bin": { - "gh-pages": "bin/gh-pages.js", - "gh-pages-clean": "bin/gh-pages-clean.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/gh-pages/node_modules/array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "dependencies": { - "array-uniq": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gh-pages/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/gh-pages/node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/gh-pages/node_modules/globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "dev": true, - "dependencies": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gh-pages/node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/gh-pages/node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -11582,36 +11450,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "dependencies": { - "pinkie": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/pirates": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", @@ -14378,18 +14216,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strip-outer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", - "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/style-loader": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", @@ -14899,18 +14725,6 @@ "node": ">=8" } }, - "node_modules/trim-repeated": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", - "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/tryer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", @@ -19092,12 +18906,6 @@ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true - }, "array.prototype.flat": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", @@ -20532,12 +20340,6 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.50.tgz", "integrity": "sha512-g5X/6oVoqLyzKfsZ1HsJvxKoUAToFMCuq1USbmp/GPIwJDRYV1IEcv+plYTdh6h11hg140hycCBId0vf7rL0+Q==" }, - "email-addresses": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz", - "integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==", - "dev": true - }, "emittery": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", @@ -21335,23 +21137,6 @@ "minimatch": "^3.0.4" } }, - "filename-reserved-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", - "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=", - "dev": true - }, - "filenamify": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", - "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", - "dev": true, - "requires": { - "filename-reserved-regex": "^2.0.0", - "strip-outer": "^1.0.1", - "trim-repeated": "^1.0.0" - } - }, "filesize": { "version": "8.0.7", "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", @@ -21648,77 +21433,6 @@ "get-intrinsic": "^1.1.1" } }, - "gh-pages": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz", - "integrity": "sha512-jA1PbapQ1jqzacECfjUaO9gV8uBgU6XNMV0oXLtfCX3haGLe5Atq8BxlrADhbD6/UdG9j6tZLWAkAybndOXTJg==", - "dev": true, - "requires": { - "async": "^2.6.1", - "commander": "^2.18.0", - "email-addresses": "^3.0.1", - "filenamify": "^4.3.0", - "find-cache-dir": "^3.3.1", - "fs-extra": "^8.1.0", - "globby": "^6.1.0" - }, - "dependencies": { - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - } - } - }, "glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -24437,27 +24151,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } - }, "pirates": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", @@ -26369,15 +26062,6 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" }, - "strip-outer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", - "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.2" - } - }, "style-loader": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", @@ -26739,15 +26423,6 @@ "punycode": "^2.1.1" } }, - "trim-repeated": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", - "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.2" - } - }, "tryer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", diff --git a/package.json b/package.json index 4131b95..d6e214b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xiv-tracker", - "version": "0.1.0", + "version": "1.2.1", "private": true, "homepage": "https://www.xivtracker.gg/", "dependencies": { @@ -39,8 +39,5 @@ "last 1 firefox version", "last 1 safari version" ] - }, - "devDependencies": { - "gh-pages": "^3.2.3" } } diff --git a/public/index.html b/public/index.html index bf7c786..2a4c393 100644 --- a/public/index.html +++ b/public/index.html @@ -7,35 +7,12 @@ - - - XIV Tracker
- diff --git a/src/App.css b/src/App.css index df93f1c..042f909 100644 --- a/src/App.css +++ b/src/App.css @@ -31,6 +31,7 @@ body { background-color: var(--c-background); background-position: top; background-repeat: no-repeat; + background-attachment: fixed; } h1, h2, h3, h4, h5, p { @@ -164,7 +165,7 @@ input:focus { } .icon--mid { - height: 4rem; + min-height: 4rem; min-width: 4rem; } @@ -186,7 +187,7 @@ input:focus { background-image: linear-gradient(-90deg, transparent, rgba(255, 255, 255, 0.25)); } -@media only screen and (max-width: 1050px) { +@media only screen and (max-width: 1150px) { * { --content-width: calc(100vw - 2rem) diff --git a/src/App.jsx b/src/App.jsx index 9e3409a..215f8eb 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -4,6 +4,7 @@ import { BrowserRouter, Routes, Route} from 'react-router-dom'; import Home from './pages/Home'; import Character from './pages/Character'; import Settings from './pages/Settings'; +import Help from './pages/Help'; import Navbar from './components/Navbar'; import Footer from './components/Footer'; import Loading from './components/utility/Loading'; @@ -130,6 +131,14 @@ const App = () => { /> } /> + + } + /> { - - const [description, setDescription] = useState(null); - const [type, setType] = useState(null); - - useEffect(() => { - - const fetchData = async () => { - await fetch("https://xivapi.com/achievement/" + props.id, {mode: 'cors'}) - .then(response => response.json()) - .then(data => { - setDescription(data.Description); - setType(data.AchievementCategory.Name); - }); - } - fetchData(); - - }, []) +/** + * @name Achievement + * @description The achievement component to be displayed. + * @param {*} props + * @returns + */ +const Achievement = (props) => { + const {data, loading} = useFetchData("https://xivapi.com/achievement/" + props.id) return ( + loading ? + null : +
  • -
    +

    {props.name}

    -

    {type}

    +

    {data.AchievementCategory.Name}

    -

    {description}

    +

    {data.Description}

    {props.points}

  • diff --git a/src/components/Achievements.css b/src/components/Achievements.css index 508d7f5..ceca211 100644 --- a/src/components/Achievements.css +++ b/src/components/Achievements.css @@ -1,9 +1,3 @@ -.achievements__content { - display: flex; - flex-direction: column; - gap: 1rem; -} - .achievement { display: flex; align-items: center; @@ -11,4 +5,19 @@ padding: 1rem; border-radius: .25rem; border: 1px solid var(--c-mid-background); +} + +.achievement__header { + display: flex; + align-items: center; + gap: .25rem 1rem; +} + +@media only screen and (max-width: 500px) { + + .achievement__header { + flex-direction: column; + align-items: flex-start; + } + } \ No newline at end of file diff --git a/src/components/Achievements.jsx b/src/components/Achievements.jsx index b7db23c..e5daa47 100644 --- a/src/components/Achievements.jsx +++ b/src/components/Achievements.jsx @@ -1,44 +1,35 @@ +// Hooks import { useEffect, useState } from 'react'; -import Header from './Header'; +import { useFetchData } from '../hooks/useFetchData'; + +// Components import Achievement from './Achievement'; import Loading from './utility/Loading'; -import './Achievements.css'; +import Header from './Header'; import Navigator from './utility/Navigator'; -const Achievements = (props) => { - const [points, setPoints] = useState(0); - const [achivementsContent, setAchievementsContent] = useState(null); - const [achievementsPage, setAchievementsPage] = useState(0); - const [maxPage, setMaxPage] = useState(0); - const [loading, setLoading] = useState(true); - const [data, setData] = useState(null); - const capacity = 8; - - // Mount - useEffect(() => { - - const fetchData = async () => { - await fetch("https://xivapi.com/character/" + props.data.ID + "?extended=1&data=AC", {mode: 'cors'}) - .then(response => response.json()) - .then(data => { - setPoints(data.Achievements.Points); - setData(data.Achievements.List); - setMaxPage(Math.ceil(data.Achievements.List.length / capacity) - 1) - setLoading(false); - }); +// Style +import './Achievements.css'; - } - - fetchData(); +/** + * @name Achievements + * @description Container for character achievements. + * @param {*} props + * @returns + */ +const Achievements = (props) => { - }, [props.data.ID]); + const capacity = 8; + const {data, loading} = useFetchData("https://xivapi.com/character/" + props.data.ID + "?extended=1&data=AC"); + const [content, setContent] = useState(null); + const [index, setIndex] = useState(0); // Update useEffect(() => { if (!loading) { - setAchievementsContent( - + +
    +
    + +
    + {Object.values(props.data.Character.GearSet.Gear).map((item, index) => + + )} +
    + + + ); +} + +export default Profile; \ No newline at end of file diff --git a/src/components/Quests.css b/src/components/Quests.css index 768612f..9c6604c 100644 --- a/src/components/Quests.css +++ b/src/components/Quests.css @@ -1,17 +1,25 @@ -.quests__collection { +.quests__header { display: flex; - justify-content: space-between; + align-items: center; } .quests__sub-header { padding: 1rem 3rem; } -.quests__list { +/* .quests__list { display: flex; gap: 2rem; font-size: .8rem; width: 100%; +} */ + +.quests__list { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + gap: 1rem 2rem; + font-size: .8rem; + width: 100%; } .quests__column { @@ -42,11 +50,27 @@ outline: 1px solid var(--color-completed); } -@media only screen and (max-width: 1050px) { +@media only screen and (max-width: 1150px) { .quests__list { + grid-template-columns: 1fr 1fr; + } + +} + +@media only screen and (max-width: 800px) { + + .quests__list { + grid-template-columns: 1fr; + } + +} + +@media only screen and (max-width: 500px) { + + .quests__header{ flex-direction: column; - gap: 0; + gap: 2rem; } } \ No newline at end of file diff --git a/src/components/Quests.jsx b/src/components/Quests.jsx index dac6b30..c02b9e7 100644 --- a/src/components/Quests.jsx +++ b/src/components/Quests.jsx @@ -5,12 +5,11 @@ import trialIcon from '../images/trials.png'; import raidIcon from '../images/raids.png'; import highEndIcon from '../images/high-end-duty.png'; import msqIcon from '../images/meteor.png'; -import { useEffect, useState, useRef } from 'react'; -import achievementsJSON from '../data/achievements.json'; +import { useEffect, useState} from 'react'; import Header from './Header'; import { FaCheck } from 'react-icons/fa'; import Navigator from './utility/Navigator'; -import Divider from './utility/Divider'; +import questsJSON from '../data/quests.json'; const questIcon = [ meteorIcon, @@ -22,13 +21,13 @@ const questIcon = [ const Quests = (props) => { - const eorzeadbBaseUrl = "https://na.finalfantasyxiv.com/lodestone/playguide/db/"; const [panel, setPanel] = useState(0); - const [totals, setTotals] = useState([[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]); - const msqRef = useRef(null); + const [content, setContent] = useState([]); + const [completion, setCompletion] = useState([0, 0]); const maxPanel = 4; useEffect(async () => { + let achievementData; await fetch("https://xivapi.com/character/" + props.id + "?data=AC", {mode: 'cors'}) .then(response => response.json()) @@ -47,374 +46,85 @@ const Quests = (props) => { refAchievementIds.push(props.referenceCharacter.Achievements.List[i].ID) } } - - // Determine what quests have been completed, and what to show relative - // to reference character. - let completedArray = [] - for (let i = 0; i < achievementsJSON.length; i++) { - let completed = 0; - const ids = Object.values(achievementsJSON[i].id); - const elements = msqRef.current.children[i].querySelectorAll('a'); - - for (let j = 0; j < achievementIds.length; j++) { - if (achievementIds.includes(ids[j])) { - if (refAchievementIds.includes(ids[j])) { - - // Both reference character and character has completed quest/encounter. - elements[j].setAttribute('class', 'eorzeadb_link completed'); - } else { - - // Reference character has not completed quest/encounter, but character has. - elements[j].setAttribute('class', elements[j].className + ' completed'); + + let keys, values; + let total = 0; + let completed = 0; + const eorzeadbBaseUrl = "https://na.finalfantasyxiv.com/lodestone/playguide/db/"; + const content = []; + const headers = Object.keys(questsJSON); + for (let i = 0; i < headers.length; i++) { + keys = Object.keys(questsJSON[headers[i]]); + values = Object.values(questsJSON[headers[i]]); + content.push( + <> +
    + +

    {headers[i]}

    +
    +
    + { + values.map((expansion, index) => +
    +
    +

    {keys[index]}

    + { + expansion.map(quest => { + if (achievementIds.includes(quest.id)) { + completed++; + } + total++; + return ( + + + {quest.name} + { + quest.isMainStory ? + : + null + } + + ); + }) + } +
    +
    + ) } - // Show checkmark - elements[j].children[0].setAttribute('class', 'icon'); - completed++; - } - } - completedArray.push([completed, ids.length]); +
    + + ) } - setTotals(completedArray) + setContent(content); + setCompletion([completed, total]); }, []); return ( -
    - -
    - -
    -

    {totals[panel][0] + " / " + totals[panel][1]}

    -

    {Math.round(totals[panel][0] / totals[panel][1] * 100)} %

    -
    - -
    - {achievementsJSON[panel].name -

    {achievementsJSON[panel].name}

    - -
    - -
    - - {/* Main Scenario */} -
      - - - - - -
      -

      Endwalker

      - Endwalker - New Found Adventure -
      - -
    - - {/* Dungeons */} -
      - - - - - -
    - - {/* Trials */} -
      - - - - -
    - - {/* Raids */} -
      - - - -
      -
    - - {/* High End */} -
      - - -
      -
    - -
    +
    + +
    + + {content[panel]} + +
    ); } diff --git a/src/components/Searchbar.css b/src/components/Searchbar.css index 055405c..88da203 100644 --- a/src/components/Searchbar.css +++ b/src/components/Searchbar.css @@ -81,16 +81,29 @@ color: white; } -@media only screen and (max-width: 1050px) { +@media only screen and (max-width: 500px) { .searchbar { - margin: 0 auto; + width: var(--content-width); } + } -@media only screen and (max-width: 500px) { +@media only screen and (max-width: 750px) { - .searchbar { + .searchbar--character { width: var(--content-width); + position: fixed; + bottom: 1rem; + left: 1rem; + background-color: var(--c-content-background-opaque); + border: 1px solid var(--c-mid-background); + box-shadow: 0 .5rem 1rem var(--c-shadow); + border-radius: .5rem; + } + + .recent--character { + bottom: 4rem; + top: auto; } } \ No newline at end of file diff --git a/src/components/Searchbar.jsx b/src/components/Searchbar.jsx index f28b7c4..c0a7146 100644 --- a/src/components/Searchbar.jsx +++ b/src/components/Searchbar.jsx @@ -41,7 +41,7 @@ const Searchbar = (props) => { return (
    @@ -62,7 +62,7 @@ const Searchbar = (props) => { -
    +

    Servers

    setDisplayDropdown(false)}/> @@ -145,7 +145,7 @@ const Searchbar = (props) => {
    Zurvan
    -
    setDisplayRecent(false)}> +
    setDisplayRecent(false)}>

    Recently Viewed

    setDisplayRecent(false)}/> diff --git a/src/components/utility/Return.jsx b/src/components/utility/Return.jsx new file mode 100644 index 0000000..8351989 --- /dev/null +++ b/src/components/utility/Return.jsx @@ -0,0 +1,20 @@ +import { IoMdArrowBack } from 'react-icons/io'; +import { useNavigate } from 'react-router-dom'; + +const Return = () => { + const navigate = useNavigate(); + return( + + ); +} + +export default Return; \ No newline at end of file diff --git a/src/data/achievements.json b/src/data/achievements.json deleted file mode 100644 index d661189..0000000 --- a/src/data/achievements.json +++ /dev/null @@ -1,214 +0,0 @@ -{ - "length": 5, - "0": { - "name": "Main Scenario", - "id": { - "A Realm Reborn": 788, - "A Realm Awoken": 898, - "Through the Maelstrom": 899, - "Defenders of Eorzea": 1001, - "Dreams of Ice": 1029, - "Before the Fall": 1129, - "Heavensward": 1139, - "As Goes Light, So Goes Darkness": 1387, - "The Gears of Change": 1493, - "Revenge of the Horde": 1594, - "Soul Surrender": 1630, - "The Far Edge of Fate": 1691, - "Stormblood": 1794, - "The Legend Returns": 1990, - "Rise of a New Sun": 2013, - "Under the Moonlight": 2098, - "Prelude in Violet": 2124, - "A Requiem for Heroes": 2233, - "Shadowbringers": 2298, - "Vows of Virtue, Deeds of Cruelty": 2424, - "Echos of a Fallen Star": 2587, - "Reflections in Crystal": 2642, - "Futures Rewritten": 2714, - "Death Unto Dawn": 2851, - "Endwalker": 2958, - "New Found Adventure": 3075 - } - }, - "1": { - "name": "Dungeons", - "id": { - "Sastasha": 663, - "The Tam-Tara Deepcroft": 669, - "Copperbell Mines": 666, - "Halatali": 667, - "The Thousand Maws of Toto-Rak": 673, - "Haukke Manor": 670, - "Brayflox's Longstop": 664, - "The Sunken Temple of Qarn": 668, - "Cutter's Cry": 674, - "The Stone Vigil": 672, - "Dzemael Darkhold": 675, - "The Aurum Vale": 676, - "Castrum Meridianum": 678, - "The Praetorium": 679, - "The Wanderer's Palace": 665, - "Amdapor Keep": 671, - "Pharos Sirius": 873, - "Copperbell Mines (Hard)": 871, - "Haukke Manor (Hard)": 872, - "The Lost City of Amdapor": 897, - "Halatali (Hard)": 895, - "Brayflox's Longstop (Hard)": 896, - "Hullbreaker Isle": 993, - "The Stone Vigil (Hard)": 992, - "The Tam-Tara Deepcroft (Hard)": 991, - "Snowcloak": 1039, - "Sastasha (Hard)": 1038, - "The Sunken Temple of Qarn (Hard)": 1037, - "The Keeper of the Lake": 1072, - "The Wanderer's Palace (Hard)": 1071, - "Amdapor Keep (Hard)": 1070, - "The Dusk Vigil": 1208, - "Sohm Al": 1209, - "The Aery": 1210, - "The Vault": 1211, - "The Great Gubal Library": 1212, - "The Aetherochemical Research Facility": 1213, - "Neverreap": 1214, - "The Fractal Continuum": 1215, - "Saint Mocianne's Arboretum": 1402, - "Pharos Sirius (Hard)": 1403, - "The Antitower": 1486, - "The Lost City of Amdapor (Hard)": 1487, - "Sohr Khai": 1599, - "Hullbreaker Isle (Hard)": 1600, - "Xelphatol": 1637, - "The Great Gubal Library (Hard)": 1638, - "Baelsar's Wall": 1686, - "Sohm Al (Hard)": 1687, - "The Sirensong Sea": 1883, - "Shisui of the Violet Tides": 1884, - "Bardam's Mettle": 1885, - "Doma Castle": 1886, - "Castrum Abania": 1887, - "Ala Mhigo": 1888, - "Kugane Castle": 1889, - "The Temple of the Fist": 1890, - "The Drowned City of Skalla": 1988, - "Hells' Lid": 2021, - "The Fractal Continuum (Hard)": 2022, - "The Swallow's Compass": 2057, - "The Burn": 2115, - "Saint Mocianne's Arboretum (Hard)": 2116, - "The Ghimlyt Dark": 2162, - "Holminster Switch": 2377, - "Dohn Mheg": 2378, - "The Qitana Ravel": 2379, - "Malikah's Well": 2380, - "Mt. Gulg": 2381, - "Amaurot": 2382, - "The Twinning": 2383, - "Akadaemia Anyder": 2384, - "The Grand Cosmos": 2440, - "Anamnesis Anyder": 2589, - "The Heroes' Gauntlet": 2618, - "Matoya's Relict": 2717, - "Paglth'an": 2849, - "The Tower of Zot": 2980, - "The Tower of Babil": 2981, - "Vanaspati": 2982, - "Ktisis Hyperboreia": 2983, - "The Aitiascope": 2984, - "The Dead Ends": 2985, - "Smileton": 2987, - "The Stigma Dreamscape": 2986, - "Alzadaal’s Legacy": 3070 - } - }, - "2": { - "name": "Trials", - "id": { - "The Bowl of Embers": 856, - "The Navel": 857, - "The Howling Eye": 855, - "Thornmarch": 894, - "The Whorleater": 893, - "The Striking Tree": 994, - "Akh Afah Amphitheatre": 1045, - "Urth's Fount": 1064, - "Thok ask Thok": 1221, - "The Limitless Blue": 1220, - "Thordan's Reign": 1400, - "Containment Bay S1T7": 1485, - "Nidhogg's Rage": 1601, - "Containment Bay P1T6": 1636, - "Containment Bay Z1T9": 1685, - "The Pool of Tribute": 1902, - "Emanation": 1901, - "Shinryu's Domain": 1989, - "The Jade Stoa": 2023, - "Tsukuyomi's Pain": 2060, - "The Great Hunt": 2109, - "Hell's Kier": 2117, - "The Wreath of Snakes": 2165, - "The Dancing Plague": 2385, - "The Crown of the Immaculate": 2386, - "Hade's Elegy": 2441, - "Cinder Drift": 2590, - "The Seat of Sacrifice": 2621, - "Memoria Misera": 2586, - "Castrum Marinum": 2718, - "The Cloud Deck": 2846, - "Zodiark's Fall": 2988, - "Hydaelyn's Call": 2989, - "The Final Day": 3072 - } - }, - "3": { - "name": "Raids", - "id": { - "The Binding Coil of Bahamut": 747, - "The Second Coil of Bahamut": 887, - "The Final Coil of Bahamut": 1040, - "Alexander: Gordias": 1228, - "Alexander: Midas": 1476, - "Alexander: The Creator": 1639, - "Omega: Deltascape": 1895, - "Omega: Sigmascape": 2024, - "Omega: Alpahscape": 2118, - "Eden's Gate": 2409, - "Eden's Verse": 2591, - "Eden's Promise": 2719, - "Asphodelos": 3035, - "Labyrinth of the Ancients": 883, - "Syrcus Tower": 995, - "The World of Darkness": 1068, - "The Void Ark": 1399, - "The Weeping City of Mhach": 1574, - "Dun Scaith": 1689, - "The Royal City of Rabanastre": 1992, - "The Ridorana Lighthouse": 2106, - "The Orbonne Monastery": 2164, - "The Copied Factory": 2443, - "The Puppet's Bunker": 2622, - "The Tower at Paradigm's Breach": 2847, - "Aglaia": 3073 - } - }, - "4": { - "name": "High End", - "id": { - "The Second Coil of Bahamut": 1000, - "Alexander: Gordias": 1231, - "Alexander: Midas": 1479, - "Alexander: The Creator": 1642, - "Omega: Deltascape": 1898, - "Omega: Sigmascape": 2027, - "Omega: Alpahscape": 2121, - "Eden's Gate": 2412, - "Eden's Verse": 2594, - "Eden's Promise": 2722, - "Asphodelos": 3038, - "The Unending Coils of Bahamut": 1993, - "The Weapon's Refrain": 2107, - "The Epic of Alexander": 2444 - } - } -} \ No newline at end of file diff --git a/src/data/feature.json b/src/data/feature.json index 12c2825..e54bbb2 100644 --- a/src/data/feature.json +++ b/src/data/feature.json @@ -1,32 +1,37 @@ { "PATCH_6.0": { - "title": "ENDWALKER", - "type": "New Expansion", + "title": "6.0 ENDWALKER", "date": "December 19th 2021", + "start": [2021, 11, 19], + "end": [2023, 11, 19], "link": "https://na.finalfantasyxiv.com/endwalker" }, "PATCH_6.1": { - "title": "NEWFOUND ADVENTURE", - "type": "Patch 6.1", + "title": "6.1 NEWFOUND ADVENTURE", "date": "April 12th 2022", + "start": [2022, 3, 12], + "end": [2022, 7, 12], "link": "https://na.finalfantasyxiv.com/endwalker/patch_6_1/" }, "EVENT_0": { "title": "LITTLE LADIES' DAY", - "type": "Live Event", "date": "March 14 - March 31", + "start": [2022, 2, 14], + "end": [2022, 2, 31], "link": "https://na.finalfantasyxiv.com/lodestone/special/2022/Little_Ladies_Day/jsuk7gn8z4" }, "EVENT_1": { "title": "MOOGLE TREASURE TROVE", - "type": "Live Event", "date": "March 14 - April 12", + "start": [2022, 2, 14], + "end": [2022, 3, 12], "link": "https://na.finalfantasyxiv.com/lodestone/special/mogmog-collection/202203/dceh8sj926" }, "EVENT_2": { "title": "HATCHING TIDE", - "type": "Live Event", "date": "April 13 - April 27", + "start": [2022, 3, 13], + "end": [2022, 3, 27], "link": "https://na.finalfantasyxiv.com/lodestone/special/2022/Hatching_tide/qeng0r6k46" } } \ No newline at end of file diff --git a/src/data/quests.json b/src/data/quests.json new file mode 100644 index 0000000..8ea4d6d --- /dev/null +++ b/src/data/quests.json @@ -0,0 +1,1352 @@ +{ + "Main Scenario": { + "A Realm Reborn": [ + { + "name": "A Realm Reborn", + "id": 788, + "link": "quest/3ab32cfebf8/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "A Realm Awoken", + "id": 898, + "link": "quest/defec516ba9/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Through the Maelstrom", + "id": 899, + "link": "quest/7f72ebda286/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Defenders of Eorzea", + "id": 1001, + "link": "quest/e31b1481e7a/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Dreams of Ice", + "id": 1029, + "link": "quest/48e85c94175/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Before the Fall", + "id": 1129, + "link": "quest/7be5b6453e1/", + "isSpoiler": true, + "isMainStory": true + } + ], + "Heavensward": [ + { + "name": "Heavensward", + "id": 1139, + "link": "quest/29fa56153f5/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "As Goes Light, So Goes Darkness", + "id": 1387, + "link": "quest/02f039d7119/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "The Gears of Change", + "id": 1493, + "link": "quest/64c819d8eb3/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Revenge of the Horde", + "id": 1594, + "link": "quest/36d55886b37/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Soul Surrender", + "id": 1630, + "link": "quest/723efddb90c/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "The Far Edge of Fate", + "id": 1691, + "link": "quest/f024c2b6931/", + "isSpoiler": true, + "isMainStory": true + } + ], + "Stormblood": [ + { + "name": "Stormblood", + "id": 1794, + "link": "quest/08908744553/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "The Legend Returns", + "id": 1990, + "link": "quest/4b032f92080/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Rise of a New Sun", + "id": 2013, + "link": "quest/6ef50b6c9fe/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Under the Moonlight", + "id": 2098, + "link": "quest/82318efbb39/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Prelude in Violet", + "id": 2124, + "link": "quest/692bc7a1186/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "A Requiem for Heroes", + "id": 2233, + "link": "quest/4e3ff3ab391/", + "isSpoiler": true, + "isMainStory": true + } + ], + "Shadowbringers": [ + { + "name": "Shadowbringers", + "id": 2298, + "link": "quest/4ed1668d377/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Vows of Virtue, Deeds of Cruelty", + "id": 2424, + "link": "quest/fc70298b76f/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Echos of a Fallen Star", + "id": 2587, + "link": "quest/b7a00665b7b/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Reflections in Crystal", + "id": 2642, + "link": "quest/90de78eeddc/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Futures Rewritten", + "id": 2714, + "link": "quest/a424070bc4c/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Death Unto Dawn", + "id": 2851, + "link": "quest/964cf0528f1/", + "isSpoiler": true, + "isMainStory": true + } + ], + "Endwalker": [ + { + "name": "Endwalker", + "id": 2958, + "link": "quest/52a65d1961d/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "New Found Adventure", + "id": 3075, + "link": "quest/52a65d1961d/", + "isSpoiler": true, + "isMainStory": true + } + ] + }, + "Dungeons": { + "A Realm Reborn": [ + { + "name": "Sastasha", + "id": 663, + "link": "duty/b229b89b3a8/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Tam-Tara Deepcroft", + "id": 669, + "link": "duty/b229b89b3a8/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Copperbell Mines", + "id": 666, + "link": "duty/619923ac984/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Halatali", + "id": 667, + "link": "duty/98319325b98/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Thousand Maws of Toto-Rak", + "id": 673, + "link": "duty/cf7612fa294/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Haukke Manor", + "id": 670, + "link": "duty/e9740dde26c/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Brayflox's Longstop", + "id": 664, + "link": "duty/e371c7ab919/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Sunken Temple of Qarn", + "id": 668, + "link": "duty/b7a48152df9/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Cutter's Cry", + "id": 674, + "link": "duty/d601f85dc1e/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Stone Vigil", + "id": 672, + "link": "duty/b6491e1b508/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Dzemael Darkhold", + "id": 675, + "link": "duty/4a36cbca533/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Aurum Vale", + "id": 676, + "link": "duty/f507633618c/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Castrum Meridianum", + "id": 678, + "link": "duty/59c2b3b84fa/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "The Praetorium", + "id": 679, + "link": "duty/2407dbd0cae/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "The Wanderer's Palace", + "id": 665, + "link": "duty/3fd66be47b2/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Amdapor Keep", + "id": 671, + "link": "duty/a8dd3748467/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Pharos Sirius", + "id": 873, + "link": "duty/ae8a92122ec/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Copperbell Mines (Hard)", + "id": 871, + "link": "duty/a780ca9b970/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Haukke Manor (Hard)", + "id": 872, + "link": "duty/61c74c68e00/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Lost City of Amdapor", + "id": 897, + "link": "duty/87d3b951d3d/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Halatali (Hard)", + "id": 895, + "link": "duty/87d3b951d3d/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Brayflox's Longstop (Hard)", + "id": 896, + "link": "duty/40681f6c94a/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Hullbreaker Isle", + "id": 993, + "link": "duty/18aeffad7c5/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Stone Vigil (Hard)", + "id": 992, + "link": "duty/418628edfbf/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Tam-Tara Deepcroft (Hard)", + "id": 991, + "link": "duty/84d01fe5b6c/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Snowcloak", + "id": 1039, + "link": "duty/6f1778eb631/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Sastasha (Hard)", + "id": 1038, + "link": "duty/df38ed5c808/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "The Sunken Temple of Qarn (Hard)", + "id": 1037, + "link": "duty/6b35d4a1179/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "The Keeper of the Lake", + "id": 1072, + "link": "duty/5e75d2059af/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "The Wanderer's Palace (Hard)", + "id": 1071, + "link": "duty/7c11b0ba080/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Amdapor Keep (Hard)", + "id": 1070, + "link": "duty/a4288ecf826/", + "isSpoiler": false, + "isMainStory": false + } + ], + "Heavensward": [ + { + "name": "The Dusk Vigil", + "id": 1208, + "link": "duty/eed0add7a62/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Sohm Al", + "id": 1209, + "link": "duty/df5ab8bfd61/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "The Aery", + "id": 1210, + "link": "duty/339c4923556/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "The Vault", + "id": 1211, + "link": "duty/a62f7ee3718/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "The Great Gubal Library", + "id": 1212, + "link": "duty/f368b40c648/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "The Aetherochemical Research Facility", + "id": 1213, + "link": "duty/923e0a1d1d0/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Neverreap", + "id": 1214, + "link": "duty/618168354ea/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Fractal Continuum", + "id": 1215, + "link": "duty/c39cf50a6a5/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Saint Mocianne's Arboretum", + "id": 1402, + "link": "duty/cdad1cb65e8/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Pharos Sirius (Hard)", + "id": 1403, + "link": "duty/cd500c08682/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Antitower", + "id": 1486, + "link": "duty/36e172ff46c/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "The Lost City of Amdapor (Hard)", + "id": 1487, + "link": "duty/193a96b0fa4/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Sohr Khai", + "id": 1599, + "link": "duty/a8c7c5c13bd/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Hullbreaker Isle (Hard)", + "id": 1600, + "link": "duty/313b1415d0f/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Xelphatol", + "id": 1637, + "link": "duty/1d95a773990/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "The Great Gubal Library (Hard)", + "id": 1638, + "link": "duty/d6e98e35e6f/", + "isSpoiler": true, + "isMainStory": false + }, + { + "name": "Baelsar's Wall", + "id": 1686, + "link": "duty/bc72ef27ade/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Sohm Al (Hard)", + "id": 1687, + "link": "duty/9bd9004a140/", + "isSpoiler": true, + "isMainStory": false + } + ], + "Stormblood": [ + { + "name": "The Sirensong Sea", + "id": 1883, + "link": "duty/471227e1ee7/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Shisui of the Violet Tides", + "id": 1884, + "link": "duty/39ba54ada1c/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Bardam's Mettle", + "id": 1885, + "link": "duty/53d7100d839/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Doma Castle", + "id": 1886, + "link": "duty/cd924bd8eac/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Castrum Abania", + "id": 1887, + "link": "duty/e635797754f/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Ala Mhigo", + "id": 1888, + "link": "duty/c71bb06e67b/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Kugane Castle", + "id": 1889, + "link": "duty/37d0e83919d/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Temple of the Fist", + "id": 1890, + "link": "duty/23edcb0d626/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Drowned City of Skalla", + "id": 1988, + "link": "duty/47ef709fb04/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Hells' Lid", + "id": 2021, + "link": "duty/f9ab5899e9d/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Fractal Continuum (Hard)", + "id": 2022, + "link": "duty/b8bea03880d/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Swallow's Compass", + "id": 2057, + "link": "duty/35ed52cf463/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Burn", + "id": 2115, + "link": "duty/c8608c977a6/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Saint Mocianne's Arboretum (Hard)", + "id": 2116, + "link": "duty/25cf070eeb4/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Ghimlyt Dark", + "id": 2162, + "link": "duty/33a05f144e4/", + "isSpoiler": true, + "isMainStory": true + } + ], + "Shadowbringers": [ + { + "name": "Holminster Switch", + "id": 2377, + "link": "duty/a6165958a5c/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Dohn Mheg", + "id": 2378, + "link": "duty/5f9f024b774/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "The Qitana Ravel", + "id": 2379, + "link": "duty/3aff2d6760c/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Malikah's Well", + "id": 2380, + "link": "duty/f8fff809d77/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Mt. Gulg", + "id": 2381, + "link": "duty/72f2e86daba/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Amaurot", + "id": 2382, + "link": "duty/c5de427bfef/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "The Twinning", + "id": 2383, + "link": "duty/be6a14f45a6/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Akadaemia Anyder", + "id": 2384, + "link": "duty/d2b053e4e31/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Grand Cosmos", + "id": 2440, + "link": "duty/1f246825352/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Anamnesis Anyder", + "id": 2589, + "link": "duty/969e6501eb7/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "The Heroes' Gauntlet", + "id": 2618, + "link": "duty/44073b7449c/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Matoya's Relict", + "id": 2717, + "link": "duty/5a30eb6b20d/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Paglth'an", + "id": 2849, + "link": "duty/81f08141768/", + "isSpoiler": true, + "isMainStory": true + } + ], + "Endwalker": [ + { + "name": "The Tower of Zot", + "id": 2980, + "link": "duty/9c317b74e3a/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "The Tower of Babil", + "id": 2981, + "link": "duty/3297718033f/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Vanaspati", + "id": 2982, + "link": "duty/dadcd891cc1/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Ktisis Hyperboreia", + "id": 2983, + "link": "duty/8514a64c969/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "The Aitiascope", + "id": 2984, + "link": "duty/fd65b266f55/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "The Dead Ends", + "id": 2985, + "link": "duty/ba59c193b71/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Smileton", + "id": 2987, + "link": "duty/175e6a7245d/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Stigma Dreamscape", + "id": 2986, + "link": "duty/25f8ec27427/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Alzadaal’s Legacy", + "id": 3070, + "link": "duty/d56ff366a07/", + "isSpoiler": true, + "isMainStory": true + } + ] + }, + "Trials": { + "A Realm Reborn": [ + { + "name": "The Bowl of Embers", + "id": 856, + "link": "duty/6af1a94ccca/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Navel", + "id": 857, + "link": "duty/589c727302e/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Howling Eye", + "id": 855, + "link": "duty/7c17ae70cc6/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Thornmarch", + "id": 894, + "link": "duty/b7c47c44490/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Whorleater", + "id": 893, + "link": "duty/0850a8627aa/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Striking Tree", + "id": 994, + "link": "duty/4d8cae741db/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Akh Afah Amphitheatre", + "id": 1045, + "link": "duty/5f786d57228/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Urth's Fount", + "id": 1064, + "link": "duty/21d8c5bd54b/", + "isSpoiler": false, + "isMainStory": false + } + ], + "Heavensward": [ + { + "name": "Thok ask Thok", + "id": 1221, + "link": "duty/83f028575c8/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Limitless Blue", + "id": 1220, + "link": "duty/e9bb63551a4/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Thordan's Reign", + "id": 1400, + "link": "duty/a8a4860068c/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Containment Bay S1T7", + "id": 1485, + "link": "duty/e05c982993d/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Nidhogg's Rage", + "id": 1601, + "link": "duty/0e880006330/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Containment Bay P1T6", + "id": 1636, + "link": "duty/212ceb19a34/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Containment Bay Z1T9", + "id": 1685, + "link": "duty/26a86785be9/", + "isSpoiler": false, + "isMainStory": false + } + ], + "Stormblood": [ + { + "name": "The Pool of Tribute", + "id": 1902, + "link": "duty/b28f61e6212/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Emanation", + "id": 1901, + "link": "duty/7de6a27d145/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Shinryu's Domain", + "id": 1989, + "link": "duty/8c829b8dd8c/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "The Jade Stoa", + "id": 2023, + "link": "duty/44fc4abc0eb/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Tsukuyomi's Pain", + "id": 2060, + "link": "duty/0919d2674e0/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "The Great Hunt", + "id": 2109, + "link": "duty/52328939737/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Hell's Kier", + "id": 2117, + "link": "duty/628e9a05d34/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Wreath of Snakes", + "id": 2165, + "link": "duty/c79d50c803d/", + "isSpoiler": false, + "isMainStory": false + } + ], + "Shadowbringers": [ + { + "name": "The Dancing Plague", + "id": 2385, + "link": "duty/bad53f19540/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Crown of the Immaculate", + "id": 2386, + "link": "duty/55504d0495f/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Hade's Elegy", + "id": 2441, + "link": "duty/b2e08d16bce/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Cinder Drift", + "id": 2590, + "link": "duty/b388be5eb05/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Seat of Sacrifice", + "id": 2621, + "link": "duty/1543e03ff37/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Memoria Misera", + "id": 2586, + "link": "duty/841d3f69efd/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Castrum Marinum", + "id": 2718, + "link": "duty/16e9780d10e/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Cloud Deck", + "id": 2846, + "link": "duty/c4a2acf0912/", + "isSpoiler": false, + "isMainStory": false + } + ], + "Endwalker": [ + { + "name": "Zodiark's Fall", + "id": 2988, + "link": "duty/f40a8f14d6d/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Hydaelyn's Call", + "id": 2989, + "link": "duty/0dbba05fd0f/", + "isSpoiler": true, + "isMainStory": true + }, + { + "name": "Endsinger's Aria", + "id": 3072, + "link": "duty/0c90d0fd67b/", + "isSpoiler": true, + "isMainStory": true + } + ] + }, + "Raids": { + "Normal": [ + { + "name": "The Binding Coil of Bahamut", + "id": 747, + "link": "duty/7134211f501/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Second Coil of Bahamut", + "id": 887, + "link": "duty/1fed5f286f0/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Final Coil of Bahamut", + "id": 1040, + "link": "duty/f086f0517d0/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Alexander: Gordias", + "id": 1228, + "link": "duty/0e9dbcd3e88/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Alexander: Midas", + "id": 1476, + "link": "duty/d3146ecb9f8/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Alexander: The Creator", + "id": 1639, + "link": "duty/5516e8fd14f/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Omega: Deltascape", + "id": 1895, + "link": "duty/76f228004f2/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Omega: Sigmascape", + "id": 2024, + "link": "duty/f6bdf734a4a/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Omega: Alpahscape", + "id": 2118, + "link": "duty/6164ec419fe/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Eden's Gate", + "id": 2409, + "link": "duty/b2a4cb98763/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Eden's Verse", + "id": 2591, + "link": "duty/2024c23c039/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Eden's Promise", + "id": 2719, + "link": "duty/d53631d5202/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Asphodelos", + "id": 3035, + "link": "duty/8766feb6d35/", + "isSpoiler": false, + "isMainStory": false + } + ], + "Alliance": [ + { + "name": "Labyrinth of the Ancients", + "id": 883, + "link": "duty/d9f4e986d0e/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Syrcus Tower", + "id": 995, + "link": "duty/47eb1d018b6/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The World of Darkness", + "id": 1068, + "link": "duty/7f0a3551ab6/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Void Ark", + "id": 1399, + "link": "duty/07fc9cb5bc8/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Weeping City of Mhach", + "id": 1574, + "link": "duty/b0a1515fd3d/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Dun Scaith", + "id": 1689, + "link": "duty/35a8825ed8e/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Royal City of Rabanastre", + "id": 1992, + "link": "duty/56209386296/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Ridorana Lighthouse", + "id": 2106, + "link": "duty/390fb10fd68/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Orbonne Monastery", + "id": 2164, + "link": "duty/95dbcc947db/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Copied Factory", + "id": 2443, + "link": "duty/ed86e5291b2/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Puppet's Bunker", + "id": 2622, + "link": "duty/889b8d8cfa4/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Tower at Paradigm's Breach", + "id": 2847, + "link": "duty/f1a29897772/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Aglaia", + "id": 3073, + "link": "duty/f1a29897772/", + "isSpoiler": false, + "isMainStory": false + } + ] + }, + "High End": { + "Savage": [ + { + "name": "The Second Coil of Bahamut", + "id": 1000, + "link": "duty/0065196fbe4/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Alexander: Gordias", + "id": 1231, + "link": "duty/fd7aa157f29/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Alexander: Midas", + "id": 1479, + "link": "duty/f93d68d7d3c/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Alexander: The Creator", + "id": 1642, + "link": "duty/def61a894af/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Omega: Deltascape", + "id": 1898, + "link": "duty/0b519d7fd00/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Omega: Sigmascape", + "id": 2027, + "link": "duty/28d9a03c886/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Omega: Alpahscape", + "id": 2121, + "link": "duty/33360b27f26/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Eden's Gate", + "id": 2412, + "link": "duty/3f2c9ddbe05/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Eden's Verse", + "id": 2594, + "link": "duty/46c4d21a738/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Eden's Promise", + "id": 2722, + "link": "duty/6a038f0d5ef/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "Asphodelos", + "id": 3038, + "link": "duty/6a038f0d5ef/", + "isSpoiler": false, + "isMainStory": false + } + ], + "Ultimate": [ + { + "name": "The Unending Coils of Bahamut", + "id": 1993, + "link": "duty/1a863f1ea3b/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Weapon's Refrain", + "id": 2107, + "link": "duty/e6c2c586ba6/", + "isSpoiler": false, + "isMainStory": false + }, + { + "name": "The Epic of Alexander", + "id": 2444, + "link": "duty/b56710190e9/", + "isSpoiler": false, + "isMainStory": false + } + ] + } +} \ No newline at end of file diff --git a/src/hooks/useFetchData.jsx b/src/hooks/useFetchData.jsx new file mode 100644 index 0000000..21d47f2 --- /dev/null +++ b/src/hooks/useFetchData.jsx @@ -0,0 +1,29 @@ +import { useEffect, useState } from 'react'; + +/** + * @name useFetchData + * @description This hook requests data from the url parameter endpoint. + * @param {*} url The http endpoint to fetch from. + * @returns JSON object containing the response from the fetch request. + */ +export const useFetchData = (url) => { + + const [data, setData] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const fetchData = async (url) => { + + setLoading(true); + + await fetch(url, {mode: 'cors'}) + .then(response => response.json()) + .then(responseJson => setData(responseJson)) + + setLoading(false); + } + fetchData(url) + }, [url]) + + return { data, loading }; +} \ No newline at end of file diff --git a/src/images/brand-extended.png b/src/images/brand-extended.png index 19f6678..48da60c 100644 Binary files a/src/images/brand-extended.png and b/src/images/brand-extended.png differ diff --git a/src/pages/Character.css b/src/pages/Character.css index 9d29103..0366e13 100644 --- a/src/pages/Character.css +++ b/src/pages/Character.css @@ -11,12 +11,6 @@ border: 1px solid var(--c-mid-background); border-radius: .5rem; background-color: var(--c-content-background); - height: 4rem; -} - -.character__panel { - display: flex; - gap: 2rem; } .section { @@ -33,40 +27,29 @@ box-shadow: 0 0 .8rem var(--c-shadow); } -.completion-rate { +.profile__content { display: flex; - align-items: center; - gap: 1rem; - position: absolute; - top: 1.75rem; - right: 2rem; -} - -.completion-rate h3 { - background-color: var(--color-completed); - color: white; - padding: .5rem; - border-radius: .25rem; -} - -.completion-rate h4 { - color: var(--color-completed); + gap: 2rem; } -@media only screen and (max-width: 1050px) { +@media only screen and (max-width: 1150px) { .character { width: 100vw; padding: 0 1rem; } - .character__row { + .profile__content { flex-direction: column; } - .sidebar { - width: auto; - } +} + +@media only screen and (max-width: 700px) { + + .character__nav { + flex-direction: column; + } } \ No newline at end of file diff --git a/src/pages/Character.jsx b/src/pages/Character.jsx index 2e01ae6..9dc9053 100644 --- a/src/pages/Character.jsx +++ b/src/pages/Character.jsx @@ -1,32 +1,39 @@ +// Hooks import { useState, useEffect } from 'react'; import { useParams } from 'react-router-dom'; -import Banner from '../components/Banner'; -import Equipment from '../components/Equipment'; +import { useFetchData } from '../hooks/useFetchData'; + +// Components +import Profile from '../components/Profile'; import Jobs from '../components/Jobs'; import Collection from '../components/Collection'; -import Attributes from '../components/Attributes'; import Quests from '../components/Quests'; import Achievements from '../components/Achievements'; import Loading from '../components/utility/Loading'; -import './Character.css'; - +import Banner from '../components/Banner'; import Button from '../components/utility/Button'; +// Style +import './Character.css'; + +/** + * @name Character + * @description This component represents the character profile page. + * @param {*} props + * @returns + */ const Character = (props) => { - const [equipmentProps, setEquipmentProps] = useState(null); - const [freeCompanyProps, setFreeCompanyProps] = useState({isDisabled: true}); - const [jobsProps, setJobsProps] = useState(null); - const [attributeProps, setAttributeProps] = useState(null); - const [questsProps, setQuestsProps] = useState(null); - const [achievementProps, setAchievementProps] = useState(null); - const [characterProps, setCharacterProps] = useState(null); - const [isLoading, setIsLoading] = useState(true); - const [activePanel, setActivePanel] = useState(0); const { id } = useParams(); - - // This function stores the currently viewed character in the recent list - // for searchbar use. + const {data, loading} = useFetchData("https://xivapi.com/character/" + id + "?extended=1&data=FC"); + const [index, setIndex] = useState(0); + + /** + * @name storeRecent + * @description This function stores recently viewed characters into local + * storage to be viewed later. + * @param {*} data A JSON object from XIVAPI + */ const storeRecent = (data) => { // Define new character object. @@ -65,146 +72,80 @@ const Character = (props) => { localStorage.setItem("recent", JSON.stringify(recent)); } - - // On mount, fetch character data, update component props with retrieved data. - useEffect(async () => { - setIsLoading(true); - - // Enable searchbar on navbar. + useEffect(() => { props.setShowSearchbar(true); - - let characterData, freeCompanyData; - - // Get extended character data from xivapi.com - await fetch("https://xivapi.com/character/" + id + "?extended=1&data=FC", {mode: 'cors'}) - .then(response => response.json()) - .then(data => { - characterData = data.Character; - freeCompanyData = data.FreeCompany; - }); - - // Set window title to 'XIV Tracker | {character name}' - document.title = "XIV Tracker | " + characterData.Name; - - // Set new content width document.documentElement.style.setProperty('--content-width', '70rem'); - - // Add character to recently viewed list. - storeRecent(characterData); - - setCharacterProps({ - type: '', - avatar: , - name: characterData.Name, - title: characterData.Title.Name, - misc: characterData.Server - }) - - setEquipmentProps({ - portrait: characterData.Portrait, - level: characterData.ActiveClassJob.Level, - gear: characterData.GearSet.Gear, - name: characterData.ActiveClassJob.Job.Name, - exp: [characterData.ActiveClassJob.ExpLevel, characterData.ActiveClassJob.ExpLevelMax], - icon: characterData.ActiveClassJob.Job.Icon - }); - - // Only update free company props if the character belongs to - // a free company. - if (freeCompanyData !== null) { - setFreeCompanyProps({ - type: 'free-company', - avatar: -
    - - - -
    - , - fc: freeCompanyData.Crest, - name: freeCompanyData.Name, - content: freeCompanyData.Slogan, - misc: "Rank: " + freeCompanyData.Rank - }) + if (!loading) { + document.title = "XIV Tracker | " + data.Character.Name; + storeRecent(data.Character); } - - setJobsProps({jobs: characterData.ClassJobs}); - setAttributeProps({data: characterData}); - setQuestsProps ({ - id: characterData.ID, - referenceCharacter: props.referenceCharacter - }); - setAchievementProps({data: characterData}); - - // Completed loading, update. - setIsLoading(() => false); - }, [id]); + }, [data]); return ( - isLoading ? + loading ? :
    - - - + } + name={data.Character.Name} + title={data.Character.Title.Name} + misc={data.Character.Server} + /> - -
    - -
    - - -
    -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - + + + + +
    ); } diff --git a/src/pages/Help.jsx b/src/pages/Help.jsx new file mode 100644 index 0000000..8ea4baa --- /dev/null +++ b/src/pages/Help.jsx @@ -0,0 +1,52 @@ +import { useEffect } from "react"; +import Divider from "../components/utility/Divider"; +import Return from "../components/utility/Return"; + +const Help = (props) => { + + useEffect(() => { + document.documentElement.style.setProperty('--content-width', '70rem'); + props.setShowSearchbar(false); + document.title = "XIV Tracker | Help"; + }, []) + + return( +
    +

    FAQ

    + + +
    +
    +

    "Why can't I see my quests and achievements?"

    +

    + XIV Tracker uses a characters achievements to determine quest + completions. Lodestone has achievements set to private by + default. You can change these settings here +

    +

    "Is XIV Tracker spoiler safe?"

    +

    + By default XIV Tracker will blur out content that could be + considered a spoiler for the main scenario questline. + You must link a character from Lodestone (using the + character id on the url) to view spoiler content. +

    +
    +
    +

    "Wait a minute, where is my mount!?"

    +

    + XIV Tracker is dependent on resources such as the official + Lodestone site. Loadestone itself updates approximately every 12 + hours. XIV Tracker can only show data from the most recent update, + so XIV Tracker will not display updates to a character from the past + 12 hours. +

    +
    +
    +
    + ); +} + +export default Help; \ No newline at end of file diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx index 2154b91..6a7709b 100644 --- a/src/pages/Home.jsx +++ b/src/pages/Home.jsx @@ -1,7 +1,6 @@ import { useEffect, useState } from 'react'; import Searchbar from '../components/Searchbar'; import Banner from '../components/Banner'; -import Notice from '../components/Notice'; import Featured from '../components/Featured'; import Loading from '../components/utility/Loading'; import brandIcon from '../images/brand-extended.png'; @@ -9,8 +8,6 @@ import './Home.css'; const Home = (props) => { const [results, setResults] = useState(null); - const [noticeType, setNoticeType] = useState(0); - const [displayNotice, setDisplayNotice] = useState(false) const [isLoading, setIsLoading] = useState(false); // Function to request a character search from xivapi.com. @@ -26,24 +23,17 @@ const Home = (props) => { await fetch("https://xivapi.com/character/search?name="+ name + "&server=" + currentServer, {mode: 'cors'}) .then(response => response.json()) .then(data => { - setDisplayNotice(true); - if (data.Results.length == 0) { - setNoticeType(3); - } else { - - // Create character banners for each valid returned character. - setResults([data.Results.map(result => ( - } - link={"/" + result.ID} - key={result.ID} - /> - ))]) - setNoticeType(1); - } + // Create character banners for each valid returned character. + setResults([data.Results.map(result => ( + } + link={"/" + result.ID} + key={result.ID} + /> + ))]) }); setIsLoading(false); } @@ -62,12 +52,11 @@ const Home = (props) => { // Disable searchbar on navbar. props.setShowSearchbar(false); + console.log(results==null); }, []); return ( - -
    - +
    { } {results} -
    ); diff --git a/src/pages/Settings.css b/src/pages/Settings.css index 3870ce6..ef6c5fc 100644 --- a/src/pages/Settings.css +++ b/src/pages/Settings.css @@ -25,7 +25,7 @@ gap: 2rem; } -@media only screen and (max-width: 1050px) { +@media only screen and (max-width: 1150px) { .settings { margin: auto; diff --git a/src/pages/Settings.jsx b/src/pages/Settings.jsx index d868f7a..28496a0 100644 --- a/src/pages/Settings.jsx +++ b/src/pages/Settings.jsx @@ -1,5 +1,4 @@ import './Settings.css'; -import { useNavigate } from 'react-router-dom'; import { useState, useEffect } from 'react'; import { BsChevronDown, BsChevronUp } from 'react-icons/bs'; import Button from '../components/utility/Button'; @@ -7,6 +6,7 @@ import Banner from '../components/Banner'; import Divider from '../components/utility/Divider'; import Loading from '../components/utility/Loading'; import Checkbox from '../components/utility/Checkbox'; +import Return from '../components/utility/Return'; const splashName = [ 'None', @@ -29,7 +29,6 @@ const Settings = (props) => { const [displayDropdown, setDisplayDropdown] = useState(-1); const [referenceBanner, setReferenceBanner] = useState(null); const [isSearching, setIsSearching] = useState(false); - const navigate = useNavigate(); // Mount useEffect(() => { @@ -39,6 +38,11 @@ const Settings = (props) => { props.setShowSearchbar(false); document.title = "XIV Tracker | Settings"; + // Update reference character data. + if (props.referenceCharacter !== null) { + requestData(props.referenceCharacter.Character.ID); + } + }, []) const requestData = async(id) => { @@ -83,6 +87,7 @@ const Settings = (props) => {

    Settings

    +
    @@ -154,7 +159,6 @@ const Settings = (props) => {
    -
    ); }