diff --git a/src/components/Admin/AdminPanel.css b/src/components/Admin/AdminPanel.css
index 9276191..cfca4a5 100644
--- a/src/components/Admin/AdminPanel.css
+++ b/src/components/Admin/AdminPanel.css
@@ -38,7 +38,7 @@
.resources-sidebar {
flex-direction: column;
height: 93.8%;
- overflow: hidden;
+ overflow: scroll;
padding: 10px;
background: var(--navbar-bg);
border: 5px solid var(--card-border);
@@ -570,6 +570,138 @@
animation: spin 1s linear infinite;
margin-bottom: 15px;
}
+
+ /* Tab navigation */
+.nav-tabs {
+ display: flex;
+ margin: 0 auto;
+ }
+
+ .nav-tab {
+ padding: 10px 20px;
+ background: #f5f5f5;
+ border: none;
+ border-bottom: 2px solid transparent;
+ cursor: pointer;
+ font-size: 16px;
+ }
+
+ .nav-tab.active {
+ border-bottom: 2px solid #4a90e2;
+ font-weight: bold;
+ }
+
+ /* Reorder container */
+ .reorder-container {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 20px;
+ }
+
+ .reorder-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+ }
+
+ .reorder-controls {
+ display: flex;
+ align-items: center;
+ gap: 15px;
+ }
+
+ .reorder-mode-selector {
+ display: flex;
+ gap: 10px;
+ }
+
+ .mode-button {
+ padding: 8px 16px;
+ background: #f5f5f5;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ cursor: pointer;
+ }
+
+ .mode-button.active {
+ background: #4a90e2;
+ color: white;
+ border-color: #4a90e2;
+ }
+
+ .category-select {
+ padding: 8px;
+ min-width: 200px;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ }
+
+ /* Sortable resources list */
+ .sortable-resources {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ }
+
+ .sortable-resource-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 15px;
+ background: white;
+ border: 1px solid #eee;
+ border-radius: 4px;
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
+ }
+
+ .resource-info {
+ flex: 1;
+ }
+
+ .resource-name {
+ font-weight: bold;
+ margin-bottom: 5px;
+ }
+
+ .resource-meta {
+ display: flex;
+ gap: 10px;
+ font-size: 0.9rem;
+ }
+
+ .position-indicator {
+ color: #666;
+ }
+
+ .reorder-actions {
+ display: flex;
+ gap: 5px;
+ }
+
+ .move-button {
+ width: 32px;
+ height: 32px;
+ background: #f5f5f5;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 18px;
+ }
+
+ .move-button:hover {
+ background: #e5e5e5;
+ }
+
+ .no-resources-message,
+ .select-category-message {
+ padding: 20px;
+ text-align: center;
+ color: #666;
+ }
@keyframes spin {
0% { transform: rotate(0deg); }
diff --git a/src/components/Admin/AdminPanel.js b/src/components/Admin/AdminPanel.js
index 87b11e4..b765f05 100644
--- a/src/components/Admin/AdminPanel.js
+++ b/src/components/Admin/AdminPanel.js
@@ -18,6 +18,11 @@ const AdminPanel = () => {
const [categoryFilter, setCategoryFilter] = useState('all');
const [tagFilter, setTagFilter] = useState('all');
const [featuredFilter, setFeaturedFilter] = useState('all');
+
+ // View management
+ const [activeView, setActiveView] = useState('resources'); // 'resources', 'reorder'
+ const [reorderMode, setReorderMode] = useState('featured'); // 'featured', 'category'
+ const [selectedCategoryForReorder, setSelectedCategoryForReorder] = useState(null);
// Login state
const [email, setEmail] = useState('');
@@ -256,6 +261,97 @@ const AdminPanel = () => {
});
};
+ // Reorder resources (move up or down in list)
+ const handleReorderResource = async (resourceId, direction) => {
+ const resourceIndex = resources.findIndex(r => r.id === resourceId);
+
+ if (resourceIndex === -1) return;
+
+ let filteredResources = resources;
+
+ // Filter resources based on reorder mode
+ if (reorderMode === 'featured') {
+ filteredResources = resources.filter(r => r.featured);
+ } else if (reorderMode === 'category' && selectedCategoryForReorder) {
+ filteredResources = resources.filter(r =>
+ r.categoryIds && r.categoryIds.includes(selectedCategoryForReorder)
+ );
+ }
+
+ const resourceToMoveIndex = filteredResources.findIndex(r => r.id === resourceId);
+
+ if (resourceToMoveIndex === -1) return;
+
+ const adjacentIndex = direction === 'up'
+ ? Math.max(0, resourceToMoveIndex - 1)
+ : Math.min(filteredResources.length - 1, resourceToMoveIndex + 1);
+
+ if (adjacentIndex === resourceToMoveIndex) return;
+
+ const resourceToMove = filteredResources[resourceToMoveIndex];
+ const adjacentResource = filteredResources[adjacentIndex];
+
+ setIsLoading(true);
+
+ try {
+ // Swap positions
+ const tempPosition = resourceToMove.position;
+
+ await supabase
+ .from('resources')
+ .update({ position: adjacentResource.position })
+ .eq('id', resourceToMove.id);
+
+ await supabase
+ .from('resources')
+ .update({ position: tempPosition })
+ .eq('id', adjacentResource.id);
+
+ // Refresh data
+ await fetchAllData();
+
+ showAlert(`Resources reordered successfully!`);
+ } catch (error) {
+ console.error('Error reordering resources:', error);
+ showAlert(`Error: ${error.message}`, 'error');
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ // Save all positions at once
+ const saveAllPositions = async (orderedResources) => {
+ setIsLoading(true);
+
+ try {
+ // Create an array of update operations
+ const updates = orderedResources.map((resource, index) => ({
+ id: resource.id,
+ position: index + 1
+ }));
+
+ // Execute updates in parallel
+ const promises = updates.map(update =>
+ supabase
+ .from('resources')
+ .update({ position: update.position })
+ .eq('id', update.id)
+ );
+
+ await Promise.all(promises);
+
+ // Refresh data
+ await fetchAllData();
+
+ showAlert(`Resource positions updated successfully!`);
+ } catch (error) {
+ console.error('Error updating positions:', error);
+ showAlert(`Error: ${error.message}`, 'error');
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
// Filter resources based on status, search query, completeness, category, tag, and featured status
const filteredResources = resources.filter(resource => {
// Status filter
@@ -598,6 +694,20 @@ const AdminPanel = () => {
{/* Header */}
Resources Admin Panel
+
No resources match your filters.
+