diff --git a/src/components/Resources/Resources.css b/src/components/Resources/Resources.css index 5b058ff..4c8ec65 100644 --- a/src/components/Resources/Resources.css +++ b/src/components/Resources/Resources.css @@ -291,9 +291,21 @@ background-color: rgba(var(--text-rgb), 0.2); } -/* New toggle styling */ -.new-filter { - margin-left: auto; +/* Toggle filters container - New */ +.toggle-filters { + display: flex; + flex-wrap: wrap; + gap: 1.5rem; + width: 100%; + justify-content: center; + margin-top: 1rem; +} + +/* New and Score toggle styling */ +.new-filter, +.score-filter { + display: flex; + align-items: center; } .toggle-label { @@ -309,7 +321,7 @@ cursor: pointer; height: 20px; margin: 0 8px 0 0; - margin-right: 8px; + margin-right: 8px; position: relative; transition: background-color .3s; width: 38px; @@ -341,6 +353,11 @@ transform: translateX(14px); } +.toggle-label input[type="checkbox"]:focus { + outline: none; + box-shadow: 0 0 0 3px rgba(var(--button-bg-rgb), 0.2); +} + .toggle-text { font-size: .95rem; font-weight: 600; @@ -431,7 +448,8 @@ /* Improved resource header styling */ .resource-header { display: flex; - align-items: center; + align-items: flex-start; + justify-content: space-between; gap: 8px; margin-bottom: 10px; } @@ -443,11 +461,18 @@ line-height: 1.3; } -/* Improved NEW badge styling */ -.new-badge { +/* Resource badges container */ +.resource-badges { + display: flex; + flex-wrap: wrap; + gap: 6px; + flex-shrink: 0; + margin-top: 3px; +} + +/* Badge styling */ +.new-badge, .score-badge { align-items: center; - animation: pulse 2s infinite; - background-color: #666 !important; border-radius: 5.9px; color: var(--button-text); display: inline-flex; @@ -460,6 +485,17 @@ margin-bottom: 4px; } +/* NEW badge styling */ +.new-badge { + animation: pulse 2s infinite; + background-color: #666 !important; +} + +/* SCORE badge styling */ +.score-badge { + background-color: var(--button-bg) !important; +} + @keyframes pulse { 0% { transform: scale(1); @@ -495,6 +531,13 @@ margin-top: auto; } +/* Categories display */ +.resource-categories { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + .resource-category { font-size: 0.8rem; background-color: var(--card-border); @@ -503,6 +546,23 @@ color: var(--text); } +/* Tags display */ +.resource-tags { + display: flex; + flex-wrap: wrap; + gap: 4px; + margin-top: 8px; +} + +.resource-tag { + font-size: 0.75rem; + background-color: rgba(var(--button-bg-rgb), 0.1); + padding: 3px 6px; + border-radius: 4px; + color: var(--text); + opacity: 0.8; +} + /* Enhanced quality stars */ .resource-quality { display: flex; @@ -563,7 +623,8 @@ opacity: 0.8; } -#new-toggle:focus { +#new-toggle:focus, +#score-toggle:focus { border-color: var(--card-border); } @@ -604,9 +665,16 @@ gap: 1rem; } + .toggle-filters { + flex-direction: column; + align-items: center; + gap: 1rem; + } + .category-filter-dropdown, .quality-filter, - .new-filter { + .new-filter, + .score-filter { width: 100%; } diff --git a/src/components/Resources/Resources.js b/src/components/Resources/Resources.js index 4d76e7c..84f5b0e 100644 --- a/src/components/Resources/Resources.js +++ b/src/components/Resources/Resources.js @@ -1,4 +1,4 @@ -// src/components/Resources/Resources.jsx - Updated for multiple categories +// src/components/Resources/Resources.jsx import React, { useState, useEffect, useMemo } from 'react'; import './Resources.css'; import ResourceLoader from './ResourceLoader'; @@ -10,6 +10,7 @@ const Resources = () => { const [activeCategory, setActiveCategory] = useState('All'); const [searchQuery, setSearchQuery] = useState(''); const [showNewOnly, setShowNewOnly] = useState(false); + const [showScoreImpactOnly, setShowScoreImpactOnly] = useState(false); const [isLoading, setIsLoading] = useState(true); // Category emojis mapping @@ -36,6 +37,7 @@ const Resources = () => { const preferences = JSON.parse(savedPreferences); setActiveCategory(preferences.activeCategory || 'All'); setShowNewOnly(preferences.showNewOnly || false); + setShowScoreImpactOnly(preferences.showScoreImpactOnly || false); } catch (error) { console.error('Error loading preferences:', error); } @@ -46,17 +48,18 @@ const Resources = () => { useEffect(() => { const preferences = { activeCategory, - showNewOnly + showNewOnly, + showScoreImpactOnly }; localStorage.setItem('resourcesPreferences', JSON.stringify(preferences)); - }, [activeCategory, showNewOnly]); + }, [activeCategory, showNewOnly, showScoreImpactOnly]); // Fetch resources from Supabase useEffect(() => { async function fetchResources() { setIsLoading(true); try { - // First fetch all resources (removed subcategories) + // First fetch all resources const { data: resourcesData, error: resourcesError } = await supabase .from('resources') .select('*') @@ -78,6 +81,18 @@ const Resources = () => { throw categoriesError; } + // Then fetch the tags for each resource + const { data: resourceTags, error: tagsError } = await supabase + .from('resource_tags') + .select(` + resource_id, + tag:tags(id, name) + `); + + if (tagsError) { + throw tagsError; + } + // Group categories by resource_id const categoriesByResource = {}; resourceCategories.forEach(item => { @@ -91,10 +106,24 @@ const Resources = () => { }); }); + // Group tags by resource_id + const tagsByResource = {}; + resourceTags.forEach(item => { + if (!tagsByResource[item.resource_id]) { + tagsByResource[item.resource_id] = []; + } + tagsByResource[item.resource_id].push({ + id: item.tag.id, + name: item.tag.name + }); + }); + // Transform data to match the expected format const formattedResources = resourcesData.map(resource => { // Get categories for this resource const resourceCategoryList = categoriesByResource[resource.id] || []; + // Get tags for this resource + const resourceTagList = tagsByResource[resource.id] || []; return { ...resource, @@ -102,6 +131,8 @@ const Resources = () => { category: resourceCategoryList.length > 0 ? resourceCategoryList[0].name : 'Misc', // Store all categories categories: resourceCategoryList, + // Store all tags + tags: resourceTagList, emoji: resourceCategoryList.length > 0 ? resourceCategoryList[0].emoji : '🔮', url: addUTMParameters(resource.url) }; @@ -128,6 +159,12 @@ const Resources = () => { return daysDiff < 14; }; + // Check if a resource impacts score + const impactsScore = (resource) => { + if (!resource.tags) return false; + return resource.tags.some(tag => tag.name.toLowerCase() === 'score'); + }; + // Add UTM parameters to URLs const addUTMParameters = (url) => { const separator = url.includes('?') ? '&' : '?'; @@ -180,7 +217,7 @@ const Resources = () => { return resource.categories && resource.categories.some(cat => cat.name === categoryName); }; - // Filter resources based on active category, search query, and new filter + // Filter resources based on active category, search query, and filters const filteredResources = useMemo(() => { return resources.filter(resource => { // Filter by category @@ -195,9 +232,12 @@ const Resources = () => { // Filter by "new" status if the toggle is active const newMatch = !showNewOnly || isNewResource(resource.created_at); - return categoryMatch && searchMatch && newMatch; + // Filter by "impacts score" status if the toggle is active + const scoreMatch = !showScoreImpactOnly || impactsScore(resource); + + return categoryMatch && searchMatch && newMatch && scoreMatch; }); - }, [resources, activeCategory, searchQuery, showNewOnly]); + }, [resources, activeCategory, searchQuery, showNewOnly, showScoreImpactOnly]); // Get featured resources const featuredResources = useMemo(() => { @@ -225,7 +265,7 @@ const Resources = () => { if (!grouped[category.name]) { grouped[category.name] = []; } - // Avoid duplicates (could happen if we process the same resource multiple times) + // Avoid duplicates if (!grouped[category.name].some(r => r.id === resource.id)) { grouped[category.name].push(resource); } @@ -296,14 +336,12 @@ const Resources = () => { -
- +
{/* Improved Filter Bar */} -
-
-
+
+
{/* Category filter dropdown */} -
+
- {/* New resources toggle */} -
- + {/* Toggle filters */} +
+ {/* New resources toggle */} +
+ +
+ + {/* Score impact toggle */} +
+ +
-
⚠️

Disclaimer: These resources are not affiliated with cred.blue or Bluesky. Use them at your own risk and exercise caution when providing access to your data.

-
{/* Loading indication */} @@ -359,6 +412,7 @@ const Resources = () => { key={`featured-${index}`} resource={resource} isNew={isNewResource(resource.created_at)} + impactsScore={impactsScore(resource)} /> ))}
@@ -381,6 +435,7 @@ const Resources = () => { key={`${category}-${index}`} resource={resource} isNew={isNewResource(resource.created_at)} + impactsScore={impactsScore(resource)} /> ))}
@@ -398,6 +453,7 @@ const Resources = () => { key={index} resource={resource} isNew={isNewResource(resource.created_at)} + impactsScore={impactsScore(resource)} /> ))}
@@ -416,7 +472,7 @@ const Resources = () => { }; // ResourceCard component for displaying individual resources -const ResourceCard = ({ resource, isNew }) => { +const ResourceCard = ({ resource, isNew, impactsScore }) => { return ( {

{resource.name}

- {isNew && ( - NEW - )} +
+ {isNew && ( + NEW + )} + {impactsScore && ( + SCORE + )} +

{resource.description}

{resource.domain}

diff --git a/src/components/ScoringMethodology/ScoringMethodology.css b/src/components/ScoringMethodology/ScoringMethodology.css index 48b11c7..9d7a42d 100644 --- a/src/components/ScoringMethodology/ScoringMethodology.css +++ b/src/components/ScoringMethodology/ScoringMethodology.css @@ -25,10 +25,10 @@ margin-top: 30px; margin-bottom: 20px; } - + .methodology-page h3 { - margin-top: 25px; - margin-bottom: 15px; + margin-bottom: 10px; + margin-top: 20px; } .methodology-page p { @@ -39,6 +39,11 @@ .increase-score-list li { margin-bottom: 5px; } + + .related-pages-title h3 { + margin-bottom: 10px; + margin-top: 20px; + } .methodology-page-chart .score-gauge { margin: auto !important;