diff --git a/frontend/src/components/format/FormatPage.jsx b/frontend/src/components/format/FormatPage.jsx
index 7586628..d3d7358 100644
--- a/frontend/src/components/format/FormatPage.jsx
+++ b/frontend/src/components/format/FormatPage.jsx
@@ -2,27 +2,24 @@ import React, {useState, useEffect} from 'react';
import {useNavigate} from 'react-router-dom';
import FormatCard from './FormatCard';
import FormatModal from './FormatModal';
-import AddButton from '@ui/AddButton';
import {getGitStatus} from '@api/api';
import {CustomFormats} from '@api/data';
-import FilterMenu from '@ui/FilterMenu';
-import SortMenu from '@ui/SortMenu';
-import {Loader, CheckSquare} from 'lucide-react';
+import {Loader} from 'lucide-react';
import Alert from '@ui/Alert';
-import SearchBar from '@ui/SearchBar';
import {useFormatModal} from '@hooks/useFormatModal';
import {useMassSelection} from '@hooks/useMassSelection';
import {useKeyboardShortcut} from '@hooks/useKeyboardShortcut';
import MassActionsBar from '@ui/MassActionsBar';
import ImportModal from '@ui/ImportModal';
import {importFormats} from '@api/import';
+import DataBar from '@ui/DataBar/DataBar';
function FormatPage() {
// Basic state management
const [formats, setFormats] = useState([]);
const [isModalOpen, setIsModalOpen] = useState(false);
const [selectedFormat, setSelectedFormat] = useState(null);
- const [sortBy, setSortBy] = useState('title');
+ const [sortBy, setSortBy] = useState('name');
const [filterType, setFilterType] = useState('none');
const [filterValue, setFilterValue] = useState('');
const [allTags, setAllTags] = useState([]);
@@ -213,7 +210,6 @@ function FormatPage() {
);
}
- // Rest of the sorting logic remains the same...
return filtered.sort((a, b) => {
switch (sortBy) {
case 'dateModified':
@@ -276,37 +272,21 @@ function FormatPage() {
return (
-
-
-
-
-
-
-
-
-
-
-
-
+
handleOpenModal()}
+ addButtonLabel='Add New Format'
+ />
{filteredFormats.map((format, index) => (
@@ -330,15 +310,6 @@ function FormatPage() {
))}
- {!isSelectionMode && (
- handleOpenModal()}
- label='Add New Format'
- top='5vh'
- left='75vw'
- />
- )}
-
{isSelectionMode && (
({
- id: item.content.name, // Use name as ID
+ id: item.content.name,
name: item.content.name,
description: item.content.description || '',
tags: item.content.tags || []
@@ -175,7 +172,6 @@ function ProfilePage() {
const handleMassImport = async arr => {
try {
- // Use the identifier to find the correct profile
const selectedProfiles = Array.from(selectedItems).map(identifier =>
profiles.find(p => p.content.name === identifier)
);
@@ -193,48 +189,61 @@ function ProfilePage() {
}
};
+ const getFilteredAndSortedProfiles = () => {
+ return profiles
+ .filter(profile => {
+ // Apply search filter
+ if (searchQuery) {
+ return (
+ profile.content.name
+ .toLowerCase()
+ .includes(searchQuery.toLowerCase()) ||
+ profile.content.tags?.some(tag =>
+ tag
+ .toLowerCase()
+ .includes(searchQuery.toLowerCase())
+ )
+ );
+ }
+
+ // Apply existing filters
+ if (filterType === 'tag') {
+ return (
+ profile.content.tags &&
+ profile.content.tags.includes(filterValue)
+ );
+ }
+ if (filterType === 'date') {
+ const profileDate = new Date(profile.modified_date);
+ const filterDate = new Date(filterValue);
+ return (
+ profileDate.toDateString() === filterDate.toDateString()
+ );
+ }
+ return true;
+ })
+ .sort((a, b) => {
+ switch (sortBy) {
+ case 'dateModified':
+ return (
+ new Date(b.modified_date) -
+ new Date(a.modified_date)
+ );
+ case 'dateCreated':
+ return (
+ new Date(b.created_date) - new Date(a.created_date)
+ );
+ case 'name':
+ default:
+ return a.content.name.localeCompare(b.content.name);
+ }
+ });
+ };
+
const formatDate = dateString => {
return new Date(dateString).toLocaleString();
};
- const sortedAndFilteredProfiles = profiles
- .filter(profile => {
- // Apply search filter
- if (searchQuery) {
- return (
- profile.content.name
- .toLowerCase()
- .includes(searchQuery.toLowerCase()) ||
- profile.content.tags?.some(tag =>
- tag.toLowerCase().includes(searchQuery.toLowerCase())
- )
- );
- }
-
- // Apply existing filters
- if (filterType === 'tag') {
- return (
- profile.content.tags &&
- profile.content.tags.includes(filterValue)
- );
- }
- if (filterType === 'date') {
- const profileDate = new Date(profile.modified_date);
- const filterDate = new Date(filterValue);
- return profileDate.toDateString() === filterDate.toDateString();
- }
- return true;
- })
- .sort((a, b) => {
- if (sortBy === 'name')
- return a.content.name.localeCompare(b.content.name);
- if (sortBy === 'dateCreated')
- return new Date(b.created_date) - new Date(a.created_date);
- if (sortBy === 'dateModified')
- return new Date(b.modified_date) - new Date(a.modified_date);
- return 0;
- });
-
if (isLoading) {
return (
@@ -278,42 +287,28 @@ function ProfilePage() {
);
}
+ const filteredProfiles = getFilteredAndSortedProfiles();
+
return (
-
-
-
-
-
-
-
-
-
-
-
-
+
handleOpenModal()}
+ addButtonLabel='Add New Profile'
+ />
- {sortedAndFilteredProfiles.map((profile, index) => (
+ {filteredProfiles.map((profile, index) => (
))}
- {!isSelectionMode && (
- handleOpenModal()}
- label='Add New Profile'
- top='5vh'
- left='75vw'
- />
- )}
-
{isSelectionMode && (
{
loadPatterns();
}, []);
@@ -143,7 +138,6 @@ function RegexPage() {
);
}
- // Rest of the sorting logic remains the same...
return filtered.sort((a, b) => {
switch (sortBy) {
case 'dateModified':
@@ -173,37 +167,21 @@ function RegexPage() {
return (
-
-
-
-
-
-
-
-
-
-
-
-
+
handleOpenModal()}
+ addButtonLabel='Add New Pattern'
+ />
{getFilteredAndSortedPatterns().map((pattern, index) => (
@@ -221,14 +199,6 @@ function RegexPage() {
}
/>
))}
- {!isSelectionMode && (
-
handleOpenModal()}
- label='Add New Pattern'
- top='5vh'
- left='75vw'
- />
- )}
{isSelectionMode && (
diff --git a/frontend/src/components/settings/arrs/ArrContainer.jsx b/frontend/src/components/settings/arrs/ArrContainer.jsx
index 0b339ed..af0af82 100644
--- a/frontend/src/components/settings/arrs/ArrContainer.jsx
+++ b/frontend/src/components/settings/arrs/ArrContainer.jsx
@@ -2,7 +2,7 @@ import React, {useState, useEffect} from 'react';
import {Loader} from 'lucide-react';
import ArrModal from './ArrModal';
import ArrCard from './ArrCard';
-import AddButton from '@ui/AddButton';
+import AddButton from '@ui/DataBar/AddButton';
import {getArrConfigs} from '@api/arr';
const ArrContainer = () => {
diff --git a/frontend/src/components/ui/AddButton.jsx b/frontend/src/components/ui/AddButton.jsx
deleted file mode 100644
index 014d210..0000000
--- a/frontend/src/components/ui/AddButton.jsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import React from 'react';
-import {Plus} from 'lucide-react';
-
-const AddButton = ({onClick, label = 'Add New', top, right, bottom, left}) => {
- const positionStyle = {
- position: 'fixed',
- zIndex: 50,
- ...(top !== undefined && {
- top: typeof top === 'number' ? `${top}px` : top
- }),
- ...(right !== undefined && {
- right: typeof right === 'number' ? `${right}px` : right
- }),
- ...(bottom !== undefined && {
- bottom: typeof bottom === 'number' ? `${bottom}px` : bottom
- }),
- ...(left !== undefined && {
- left: typeof left === 'number' ? `${left}px` : left
- })
- };
-
- return (
-
- );
-};
-
-export default AddButton;
diff --git a/frontend/src/components/ui/DataBar/AddButton.jsx b/frontend/src/components/ui/DataBar/AddButton.jsx
new file mode 100644
index 0000000..4060d63
--- /dev/null
+++ b/frontend/src/components/ui/DataBar/AddButton.jsx
@@ -0,0 +1,16 @@
+import React from 'react';
+import {Plus} from 'lucide-react';
+
+const AddButton = ({onClick, label = 'Add New'}) => {
+ return (
+
+ );
+};
+
+export default AddButton;
diff --git a/frontend/src/components/ui/DataBar/DataBar.jsx b/frontend/src/components/ui/DataBar/DataBar.jsx
new file mode 100644
index 0000000..b595971
--- /dev/null
+++ b/frontend/src/components/ui/DataBar/DataBar.jsx
@@ -0,0 +1,102 @@
+import React, {useState, useEffect} from 'react';
+import SearchBar from './SearchBar';
+import FilterMenu from './FilterMenu';
+import {SortDropdown} from './SortDropdown';
+import ToggleSelectButton from './ToggleSelectButton';
+import AddButton from './AddButton';
+
+const DataBar = ({
+ onSearch,
+ searchPlaceholder = 'Search by name or tag...',
+ filterType,
+ setFilterType,
+ filterValue,
+ setFilterValue,
+ allTags,
+ sortBy,
+ setSortBy,
+ isSelectionMode,
+ toggleSelectionMode,
+ onAdd,
+ addButtonLabel = 'Add New',
+ showAddButton = true
+}) => {
+ const [isFloating, setIsFloating] = useState(false);
+ const [isVisible, setIsVisible] = useState(true);
+ const [lastScrollY, setLastScrollY] = useState(0);
+
+ useEffect(() => {
+ const handleScroll = () => {
+ const currentScrollY = window.scrollY;
+
+ // Show/hide based on scroll direction
+ setIsVisible(currentScrollY <= 0 || currentScrollY < lastScrollY);
+
+ // Start floating once we scroll past the initial position
+ setIsFloating(currentScrollY > 64); // Approximately the height of the bar
+
+ setLastScrollY(currentScrollY);
+ };
+
+ window.addEventListener('scroll', handleScroll, {passive: true});
+ return () => window.removeEventListener('scroll', handleScroll);
+ }, [lastScrollY]);
+
+ return (
+
+
+
+
+
+ setSortBy(key)}
+ />
+
+
+
+
+
+
+
+
+
+
+ {showAddButton && !isSelectionMode && (
+
+ )}
+
+
+ );
+};
+
+export default DataBar;
diff --git a/frontend/src/components/ui/FilterMenu.jsx b/frontend/src/components/ui/DataBar/FilterMenu.jsx
similarity index 100%
rename from frontend/src/components/ui/FilterMenu.jsx
rename to frontend/src/components/ui/DataBar/FilterMenu.jsx
diff --git a/frontend/src/components/ui/SearchBar.jsx b/frontend/src/components/ui/DataBar/SearchBar.jsx
similarity index 100%
rename from frontend/src/components/ui/SearchBar.jsx
rename to frontend/src/components/ui/DataBar/SearchBar.jsx
diff --git a/frontend/src/components/ui/SortDropdown.jsx b/frontend/src/components/ui/DataBar/SortDropdown.jsx
similarity index 100%
rename from frontend/src/components/ui/SortDropdown.jsx
rename to frontend/src/components/ui/DataBar/SortDropdown.jsx
diff --git a/frontend/src/components/ui/DataBar/ToggleSelectButton.jsx b/frontend/src/components/ui/DataBar/ToggleSelectButton.jsx
new file mode 100644
index 0000000..fef7935
--- /dev/null
+++ b/frontend/src/components/ui/DataBar/ToggleSelectButton.jsx
@@ -0,0 +1,24 @@
+import React from 'react';
+import {CheckSquare} from 'lucide-react';
+
+const ToggleSelectButton = ({
+ isSelectionMode,
+ onClick,
+ shortcutKey = 'A' // Default to 'A' since that's what's used in the pages
+}) => {
+ return (
+
+ );
+};
+
+export default ToggleSelectButton;