refactor(nav): Implement React Router for navigation

- Replace button-based navigation with React Router Links in Navbar
- Add BrowserRouter, Routes, and Route components in App.js
- Update Navbar to determine active tab based on current route
- Remove setActiveTab prop from Navbar component
- Update RegexPage, FormatPage, and SettingsPage imports in App.js
- Adjust Navbar propTypes to remove activeTab and setActiveTab
This commit is contained in:
santiagosayshey
2024-08-24 00:35:16 +09:30
committed by Sam Chau
parent fd9d23a828
commit 330c162b0e
7 changed files with 457 additions and 109 deletions

View File

@@ -16,6 +16,8 @@
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.26.1",
"react-syntax-highlighter": "^15.5.0",
"react-toastify": "^10.0.5",
"tailwind-merge": "^2.5.2"
},
@@ -290,6 +292,17 @@
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/runtime": {
"version": "7.25.4",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.4.tgz",
"integrity": "sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==",
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/template": {
"version": "7.25.0",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz",
@@ -865,6 +878,22 @@
}
}
},
"node_modules/@remix-run/router": {
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.1.tgz",
"integrity": "sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg==",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@types/hast": {
"version": "2.3.10",
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz",
"integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==",
"dependencies": {
"@types/unist": "^2"
}
},
"node_modules/@types/prop-types": {
"version": "15.7.12",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
@@ -890,6 +919,11 @@
"@types/react": "*"
}
},
"node_modules/@types/unist": {
"version": "2.0.11",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
"integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="
},
"node_modules/@vitejs/plugin-react": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-3.1.0.tgz",
@@ -1105,6 +1139,33 @@
"node": ">=4"
}
},
"node_modules/character-entities": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz",
"integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/character-entities-legacy": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz",
"integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/character-reference-invalid": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz",
"integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
@@ -1183,6 +1244,15 @@
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
"dev": true
},
"node_modules/comma-separated-tokens": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz",
"integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/commander": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
@@ -1369,6 +1439,18 @@
"reusify": "^1.0.4"
}
},
"node_modules/fault": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz",
"integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==",
"dependencies": {
"format": "^0.2.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@@ -1416,6 +1498,14 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/format": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz",
"integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==",
"engines": {
"node": ">=0.4.x"
}
},
"node_modules/fraction.js": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
@@ -1538,6 +1628,61 @@
"node": ">= 0.4"
}
},
"node_modules/hast-util-parse-selector": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz",
"integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/hastscript": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz",
"integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==",
"dependencies": {
"@types/hast": "^2.0.0",
"comma-separated-tokens": "^1.0.0",
"hast-util-parse-selector": "^2.0.0",
"property-information": "^5.0.0",
"space-separated-tokens": "^1.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/highlight.js": {
"version": "10.7.3",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
"engines": {
"node": "*"
}
},
"node_modules/is-alphabetical": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz",
"integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/is-alphanumerical": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz",
"integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==",
"dependencies": {
"is-alphabetical": "^1.0.0",
"is-decimal": "^1.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@@ -1565,6 +1710,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-decimal": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz",
"integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -1595,6 +1749,15 @@
"node": ">=0.10.0"
}
},
"node_modules/is-hexadecimal": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz",
"integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@@ -1689,6 +1852,19 @@
"loose-envify": "cli.js"
}
},
"node_modules/lowlight": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz",
"integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==",
"dependencies": {
"fault": "^1.0.0",
"highlight.js": "~10.7.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -1831,6 +2007,23 @@
"integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==",
"dev": true
},
"node_modules/parse-entities": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz",
"integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
"dependencies": {
"character-entities": "^1.0.0",
"character-entities-legacy": "^1.0.0",
"character-reference-invalid": "^1.0.0",
"is-alphanumerical": "^1.0.0",
"is-decimal": "^1.0.0",
"is-hexadecimal": "^1.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
@@ -2076,6 +2269,14 @@
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"dev": true
},
"node_modules/prismjs": {
"version": "1.29.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz",
"integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==",
"engines": {
"node": ">=6"
}
},
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@@ -2086,6 +2287,18 @@
"react-is": "^16.13.1"
}
},
"node_modules/property-information": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz",
"integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==",
"dependencies": {
"xtend": "^4.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -2143,6 +2356,51 @@
"node": ">=0.10.0"
}
},
"node_modules/react-router": {
"version": "6.26.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.1.tgz",
"integrity": "sha512-kIwJveZNwp7teQRI5QmwWo39A5bXRyqpH0COKKmPnyD2vBvDwgFXSqDUYtt1h+FEyfnE8eXr7oe0MxRzVwCcvQ==",
"dependencies": {
"@remix-run/router": "1.19.1"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=16.8"
}
},
"node_modules/react-router-dom": {
"version": "6.26.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.1.tgz",
"integrity": "sha512-veut7m41S1fLql4pLhxeSW3jlqs+4MtjRLj0xvuCEXsxusJCbs6I8yn9BxzzDX2XDgafrccY6hwjmd/bL54tFw==",
"dependencies": {
"@remix-run/router": "1.19.1",
"react-router": "6.26.1"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=16.8",
"react-dom": ">=16.8"
}
},
"node_modules/react-syntax-highlighter": {
"version": "15.5.0",
"resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz",
"integrity": "sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==",
"dependencies": {
"@babel/runtime": "^7.3.1",
"highlight.js": "^10.4.1",
"lowlight": "^1.17.0",
"prismjs": "^1.27.0",
"refractor": "^3.6.0"
},
"peerDependencies": {
"react": ">= 0.14.0"
}
},
"node_modules/react-toastify": {
"version": "10.0.5",
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.5.tgz",
@@ -2176,6 +2434,33 @@
"node": ">=8.10.0"
}
},
"node_modules/refractor": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz",
"integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==",
"dependencies": {
"hastscript": "^6.0.0",
"parse-entities": "^2.0.0",
"prismjs": "~1.27.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/refractor/node_modules/prismjs": {
"version": "1.27.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz",
"integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==",
"engines": {
"node": ">=6"
}
},
"node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
},
"node_modules/reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@@ -2284,6 +2569,15 @@
"node": ">=0.10.0"
}
},
"node_modules/space-separated-tokens": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz",
"integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/string-width": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
@@ -2764,6 +3058,14 @@
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
}
},
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"engines": {
"node": ">=0.4"
}
},
"node_modules/yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",

View File

@@ -17,6 +17,8 @@
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.26.1",
"react-syntax-highlighter": "^15.5.0",
"react-toastify": "^10.0.5",
"tailwind-merge": "^2.5.2"
},

View File

@@ -1,41 +1,48 @@
import { useState, useEffect } from 'react';
import RegexManager from './components/regex/RegexManager';
import CustomFormatManager from './components/format/FormatManager';
import Settings from './components/settings/SettingsManager';
import Navbar from './components/ui/Navbar';
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { useState, useEffect } from "react";
import RegexPage from "./components/regex/RegexPage";
import FormatPage from "./components/format/FormatPage";
import SettingsPage from "./components/settings/SettingsPage";
import Navbar from "./components/ui/Navbar";
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
function App() {
const [activeTab, setActiveTab] = useState('settings');
const [darkMode, setDarkMode] = useState(true);
useEffect(() => {
if (darkMode) {
document.documentElement.classList.add('dark');
document.documentElement.classList.add("dark");
} else {
document.documentElement.classList.remove('dark');
document.documentElement.classList.remove("dark");
}
}, [darkMode]);
return (
<>
<div className="min-h-screen bg-gray-900 text-gray-100">
<Navbar
activeTab={activeTab}
setActiveTab={setActiveTab}
darkMode={darkMode}
setDarkMode={setDarkMode}
/>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 mt-6">
{activeTab === 'regex' && <RegexManager />}
{activeTab === 'format' && <CustomFormatManager />}
{activeTab === 'settings' && <Settings />}
</div>
<Router>
<div className="min-h-screen bg-gray-900 text-gray-100">
<Navbar darkMode={darkMode} setDarkMode={setDarkMode} />
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 mt-6">
<Routes>
<Route path="/regex" element={<RegexPage />} />
<Route path="/format" element={<FormatPage />} />
<Route path="/settings" element={<SettingsPage />} />
<Route path="/" element={<SettingsPage />} />
</Routes>
</div>
<ToastContainer position="top-right" autoClose={5000} hideProgressBar={false} newestOnTop={false} closeOnClick rtl={false} pauseOnFocusLoss draggable pauseOnHover />
</>
</div>
<ToastContainer
position="top-right"
autoClose={5000}
hideProgressBar={false}
newestOnTop={false}
closeOnClick
rtl={false}
pauseOnFocusLoss
draggable
pauseOnHover
/>
</Router>
);
}

View File

@@ -1,18 +1,18 @@
import { useState, useEffect } from 'react';
import FormatCard from './FormatCard';
import FormatModal from './FormatModal';
import AddNewCard from '../ui/AddNewCard';
import { getFormats } from '../../api/api';
import FilterMenu from '../ui/FilterMenu';
import SortMenu from '../ui/SortMenu';
import { useState, useEffect } from "react";
import FormatCard from "./FormatCard";
import FormatModal from "./FormatModal";
import AddNewCard from "../ui/AddNewCard";
import { getFormats } from "../../api/api";
import FilterMenu from "../ui/FilterMenu";
import SortMenu from "../ui/SortMenu";
function FormatManager() {
function FormatPage() {
const [formats, setFormats] = useState([]);
const [isModalOpen, setIsModalOpen] = useState(false);
const [selectedFormat, setSelectedFormat] = useState(null);
const [sortBy, setSortBy] = useState('title');
const [filterType, setFilterType] = useState('none');
const [filterValue, setFilterValue] = useState('');
const [sortBy, setSortBy] = useState("title");
const [filterType, setFilterType] = useState("none");
const [filterValue, setFilterValue] = useState("");
const [allTags, setAllTags] = useState([]);
useEffect(() => {
@@ -23,10 +23,12 @@ function FormatManager() {
try {
const fetchedFormats = await getFormats();
setFormats(fetchedFormats);
const tags = [...new Set(fetchedFormats.flatMap(format => format.tags || []))];
const tags = [
...new Set(fetchedFormats.flatMap((format) => format.tags || [])),
];
setAllTags(tags);
} catch (error) {
console.error('Error fetching formats:', error);
console.error("Error fetching formats:", error);
}
};
@@ -46,10 +48,10 @@ function FormatManager() {
};
const handleCloneFormat = (format) => {
const clonedFormat = {
...format,
const clonedFormat = {
...format,
id: 0, // Ensure the ID is 0 for a new entry
name: `${format.name} [COPY]`
name: `${format.name} [COPY]`,
};
setSelectedFormat(clonedFormat); // Set cloned format
setIsModalOpen(true); // Open modal in Add mode
@@ -60,11 +62,11 @@ function FormatManager() {
};
const sortedAndFilteredFormats = formats
.filter(format => {
if (filterType === 'tag') {
.filter((format) => {
if (filterType === "tag") {
return format.tags && format.tags.includes(filterValue);
}
if (filterType === 'date') {
if (filterType === "date") {
const formatDate = new Date(format.date_modified);
const filterDate = new Date(filterValue);
return formatDate.toDateString() === filterDate.toDateString();
@@ -72,9 +74,11 @@ function FormatManager() {
return true;
})
.sort((a, b) => {
if (sortBy === 'title') return a.name.localeCompare(b.name);
if (sortBy === 'dateCreated') return new Date(b.date_created) - new Date(a.date_created);
if (sortBy === 'dateModified') return new Date(b.date_modified) - new Date(a.date_modified);
if (sortBy === "title") return a.name.localeCompare(b.name);
if (sortBy === "dateCreated")
return new Date(b.date_created) - new Date(a.date_created);
if (sortBy === "dateModified")
return new Date(b.date_modified) - new Date(a.date_modified);
return 0;
});
@@ -93,12 +97,12 @@ function FormatManager() {
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 mb-4">
{sortedAndFilteredFormats.map((format) => (
<FormatCard
key={format.id}
format={format}
<FormatCard
key={format.id}
format={format}
onEdit={() => handleOpenModal(format)}
onClone={handleCloneFormat} // Pass the clone handler
showDate={sortBy !== 'title'}
showDate={sortBy !== "title"}
formatDate={formatDate}
/>
))}
@@ -115,4 +119,4 @@ function FormatManager() {
);
}
export default FormatManager;
export default FormatPage;

View File

@@ -1,18 +1,18 @@
import { useState, useEffect } from 'react';
import RegexCard from './RegexCard';
import RegexModal from './RegexModal';
import AddNewCard from '../ui/AddNewCard';
import { getRegexes } from '../../api/api';
import FilterMenu from '../ui/FilterMenu';
import SortMenu from '../ui/SortMenu';
import { useState, useEffect } from "react";
import RegexCard from "./RegexCard";
import RegexModal from "./RegexModal";
import AddNewCard from "../ui/AddNewCard";
import { getRegexes } from "../../api/api";
import FilterMenu from "../ui/FilterMenu";
import SortMenu from "../ui/SortMenu";
function RegexManager() {
function RegexPage() {
const [regexes, setRegexes] = useState([]);
const [isModalOpen, setIsModalOpen] = useState(false);
const [selectedRegex, setSelectedRegex] = useState(null);
const [sortBy, setSortBy] = useState('title');
const [filterType, setFilterType] = useState('none');
const [filterValue, setFilterValue] = useState('');
const [sortBy, setSortBy] = useState("title");
const [filterType, setFilterType] = useState("none");
const [filterValue, setFilterValue] = useState("");
const [allTags, setAllTags] = useState([]);
useEffect(() => {
@@ -23,10 +23,12 @@ function RegexManager() {
try {
const fetchedRegexes = await getRegexes();
setRegexes(fetchedRegexes);
const tags = [...new Set(fetchedRegexes.flatMap(regex => regex.tags || []))];
const tags = [
...new Set(fetchedRegexes.flatMap((regex) => regex.tags || [])),
];
setAllTags(tags);
} catch (error) {
console.error('Error fetching regexes:', error);
console.error("Error fetching regexes:", error);
}
};
@@ -46,11 +48,11 @@ function RegexManager() {
};
const handleCloneRegex = (regex) => {
const clonedRegex = {
...regex,
const clonedRegex = {
...regex,
id: 0, // Ensure the ID is 0 for a new entry
name: `${regex.name} [COPY]`,
regex101Link: '' // Remove the regex101 link
name: `${regex.name} [COPY]`,
regex101Link: "", // Remove the regex101 link
};
setSelectedRegex(clonedRegex); // Set cloned regex
setIsModalOpen(true); // Open modal in Add mode
@@ -61,11 +63,11 @@ function RegexManager() {
};
const sortedAndFilteredRegexes = regexes
.filter(regex => {
if (filterType === 'tag') {
.filter((regex) => {
if (filterType === "tag") {
return regex.tags && regex.tags.includes(filterValue);
}
if (filterType === 'date') {
if (filterType === "date") {
const regexDate = new Date(regex.date_modified);
const filterDate = new Date(filterValue);
return regexDate.toDateString() === filterDate.toDateString();
@@ -73,9 +75,11 @@ function RegexManager() {
return true;
})
.sort((a, b) => {
if (sortBy === 'title') return a.name.localeCompare(b.name);
if (sortBy === 'dateCreated') return new Date(b.date_created) - new Date(a.date_created);
if (sortBy === 'dateModified') return new Date(b.date_modified) - new Date(a.date_modified);
if (sortBy === "title") return a.name.localeCompare(b.name);
if (sortBy === "dateCreated")
return new Date(b.date_created) - new Date(a.date_created);
if (sortBy === "dateModified")
return new Date(b.date_modified) - new Date(a.date_modified);
return 0;
});
@@ -99,7 +103,7 @@ function RegexManager() {
regex={regex}
onEdit={() => handleOpenModal(regex)}
onClone={handleCloneRegex} // Pass the clone handler
showDate={sortBy !== 'title'}
showDate={sortBy !== "title"}
formatDate={formatDate}
/>
))}
@@ -116,4 +120,4 @@ function RegexManager() {
);
}
export default RegexManager;
export default RegexPage;

View File

@@ -33,7 +33,7 @@ import CommitSection from "./CommitSection";
import Tooltip from "../ui/Tooltip";
import DiffModal from "./DiffModal";
const SettingsManager = () => {
const SettingsPage = () => {
const [settings, setSettings] = useState(null);
const [status, setStatus] = useState(null);
const [showModal, setShowModal] = useState(false);
@@ -637,4 +637,4 @@ const SettingsManager = () => {
);
};
export default SettingsManager;
export default SettingsPage;

View File

@@ -1,16 +1,30 @@
import PropTypes from 'prop-types';
import { useState, useEffect, useRef } from 'react';
import PropTypes from "prop-types";
import { useState, useEffect, useRef } from "react";
import { Link, useLocation } from "react-router-dom";
function ToggleSwitch({ checked, onChange }) {
return (
<label className="flex items-center cursor-pointer">
<div className="relative">
<input type="checkbox" className="sr-only" checked={checked} onChange={onChange} />
<div className={`block w-14 h-8 rounded-full ${checked ? 'bg-blue-600' : 'bg-gray-600'} transition-colors duration-300`}></div>
<div className={`dot absolute left-1 top-1 bg-white w-6 h-6 rounded-full transition-transform duration-300 ${checked ? 'transform translate-x-6' : ''}`}></div>
<input
type="checkbox"
className="sr-only"
checked={checked}
onChange={onChange}
/>
<div
className={`block w-14 h-8 rounded-full ${
checked ? "bg-blue-600" : "bg-gray-600"
} transition-colors duration-300`}
></div>
<div
className={`dot absolute left-1 top-1 bg-white w-6 h-6 rounded-full transition-transform duration-300 ${
checked ? "transform translate-x-6" : ""
}`}
></div>
</div>
<div className="ml-3 text-gray-300 font-medium">
{checked ? 'Dark' : 'Light'}
{checked ? "Dark" : "Light"}
</div>
</label>
);
@@ -21,10 +35,20 @@ ToggleSwitch.propTypes = {
onChange: PropTypes.func.isRequired,
};
function Navbar({ activeTab, setActiveTab, darkMode, setDarkMode }) {
function Navbar({ darkMode, setDarkMode }) {
const [tabOffset, setTabOffset] = useState(0);
const [tabWidth, setTabWidth] = useState(0);
const tabsRef = useRef([]);
const tabsRef = useRef({});
const location = useLocation();
const getActiveTab = (pathname) => {
if (pathname.startsWith("/regex")) return "regex";
if (pathname.startsWith("/format")) return "format";
if (pathname.startsWith("/settings")) return "settings";
return "settings"; // default to settings if no match
};
const activeTab = getActiveTab(location.pathname);
useEffect(() => {
if (tabsRef.current[activeTab]) {
@@ -39,44 +63,51 @@ function Navbar({ activeTab, setActiveTab, darkMode, setDarkMode }) {
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative">
<div className="flex items-center justify-between h-16">
<div className="flex items-center space-x-8">
<h1 className="text-2xl font-bold text-white">
Profilarr
</h1>
<h1 className="text-2xl font-bold text-white">Profilarr</h1>
<div className="relative flex space-x-2">
<div
className="absolute top-0 bottom-0 bg-gray-900 rounded-md transition-all duration-300"
style={{ left: tabOffset, width: tabWidth }}
></div>
<button
ref={(el) => (tabsRef.current['regex'] = el)}
<Link
to="/regex"
ref={(el) => (tabsRef.current["regex"] = el)}
className={`px-3 py-2 rounded-md text-sm font-medium relative z-10 ${
activeTab === 'regex' ? 'text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white'
activeTab === "regex"
? "text-white"
: "text-gray-300 hover:bg-gray-700 hover:text-white"
}`}
onClick={() => setActiveTab('regex')}
>
Regex
</button>
<button
ref={(el) => (tabsRef.current['format'] = el)}
</Link>
<Link
to="/format"
ref={(el) => (tabsRef.current["format"] = el)}
className={`px-3 py-2 rounded-md text-sm font-medium relative z-10 ${
activeTab === 'format' ? 'text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white'
activeTab === "format"
? "text-white"
: "text-gray-300 hover:bg-gray-700 hover:text-white"
}`}
onClick={() => setActiveTab('format')}
>
Custom Format
</button>
<button
ref={(el) => (tabsRef.current['settings'] = el)}
</Link>
<Link
to="/settings"
ref={(el) => (tabsRef.current["settings"] = el)}
className={`px-3 py-2 rounded-md text-sm font-medium relative z-10 ${
activeTab === 'settings' ? 'text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white'
activeTab === "settings"
? "text-white"
: "text-gray-300 hover:bg-gray-700 hover:text-white"
}`}
onClick={() => setActiveTab('settings')}
>
Settings
</button>
</Link>
</div>
</div>
<ToggleSwitch checked={darkMode} onChange={() => setDarkMode(!darkMode)} />
<ToggleSwitch
checked={darkMode}
onChange={() => setDarkMode(!darkMode)}
/>
</div>
</div>
</nav>
@@ -84,8 +115,6 @@ function Navbar({ activeTab, setActiveTab, darkMode, setDarkMode }) {
}
Navbar.propTypes = {
activeTab: PropTypes.string.isRequired,
setActiveTab: PropTypes.func.isRequired,
darkMode: PropTypes.bool.isRequired,
setDarkMode: PropTypes.func.isRequired,
};