mirror of
https://github.com/Dictionarry-Hub/profilarr.git
synced 2026-01-26 20:59:13 +01:00
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:
committed by
Sam Chau
parent
fd9d23a828
commit
330c162b0e
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user