diff --git a/website/src/components/quickstartGuideCard/index.js b/website/src/components/quickstartGuideCard/index.js index dc961f85a45..3d5769c3246 100644 --- a/website/src/components/quickstartGuideCard/index.js +++ b/website/src/components/quickstartGuideCard/index.js @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState, useEffect } from "react"; import Link from "@docusaurus/Link"; import styles from "./styles.module.css"; import getIconType from "../../utils/get-icon-type"; @@ -32,17 +32,51 @@ export default function QuickstartGuideCard({ frontMatter }) { // Component that handles the information under the title on the quickstart guide page export function QuickstartGuideTitle({ frontMatter }) { - const { time_to_complete, tags, level, recently_updated } = - frontMatter; + const { id, time_to_complete, tags, level, recently_updated } = frontMatter; + const [isFavorite, setIsFavorite] = useState(false); + + useEffect(() => { + // Check if this guide is in favorites when component mounts + const favorites = JSON.parse(localStorage.getItem('favoriteGuides') || '[]'); + setIsFavorite(favorites.includes(id)); + }, [id]); + + const toggleFavorite = () => { + const favorites = JSON.parse(localStorage.getItem('favoriteGuides') || '[]'); + + if (isFavorite) { + const newFavorites = favorites.filter(fav => fav !== id); + localStorage.setItem('favoriteGuides', JSON.stringify(newFavorites)); + } else { + favorites.push(id); + localStorage.setItem('favoriteGuides', JSON.stringify(favorites)); + } + + setIsFavorite(!isFavorite); + }; return (
- {recently_updated && ( - Updated - )} - {time_to_complete && ( - {getSvgIcon('fa-clock')} {time_to_complete} - )} +
+
+ +
+ {recently_updated && ( + Updated + )} + {time_to_complete && ( + + {getSvgIcon('fa-clock')} {time_to_complete} + + )} +
{(tags || level) && (
diff --git a/website/src/components/quickstartGuideCard/styles.module.css b/website/src/components/quickstartGuideCard/styles.module.css index 55d24a96fa0..4df4a0aea77 100644 --- a/website/src/components/quickstartGuideCard/styles.module.css +++ b/website/src/components/quickstartGuideCard/styles.module.css @@ -119,6 +119,11 @@ padding-left: 0; } +.leftInfo { + display: flex; + align-items: center; +} + .infoContainer .tag_container { display: flex; flex-wrap: wrap; @@ -171,3 +176,33 @@ padding: 0; } } +.favoriteButton { + + top: 1.5rem; + right: 1.5rem; + background: none; + border: none; + cursor: pointer; + color: #ccc; + padding: 5px; + display: flex; + align-items: center; + justify-content: center; +} + +.favoriteButton svg { + width: 20px; + height: 20px; + fill: currentColor; +} + +.favoriteButton:hover { + color: var( --color-green-blue); +} + +.favoriteButton.favorited { + color: #ffd700; +} + + + diff --git a/website/src/components/quickstartGuideList/index.js b/website/src/components/quickstartGuideList/index.js index 5813409929a..58d35efc0ce 100644 --- a/website/src/components/quickstartGuideList/index.js +++ b/website/src/components/quickstartGuideList/index.js @@ -65,6 +65,7 @@ function QuickstartList({ quickstartData }) { const [selectedFilters, setSelectedFilters] = useState({}); const [searchInput, setSearchInput] = useState(''); const location = useLocation(); + const [favorites, setFavorites] = useState([]); // Replace individual filter states with a single object const getFilterOptions = (filterKey) => { @@ -198,6 +199,15 @@ function QuickstartList({ quickstartData }) { }, {}) || {}; }, [filteredData, selectedFilters, quickstartData]); + // Add this useEffect to load favorites + useEffect(() => { + const favoriteIds = JSON.parse(localStorage.getItem('favoriteGuides') || '[]'); + const favoriteGuides = quickstartData.filter(guide => + favoriteIds.includes(guide.data.id) + ); + setFavorites(favoriteGuides); + }, [quickstartData]); + return ( @@ -249,6 +259,12 @@ function QuickstartList({ quickstartData }) { {Object.values(selectedFilters).every(selected => !selected?.length) ? ( // Show categorized view when no filters are selected <> + {favorites.length > 0 && ( + + )} {CONFIG?.categories?.map((category) => ( ); + case icon.includes("fa-star"): + return ( + + + + ); default: return ""; }