diff --git a/src/components/Projects/ListProjectSolutions.tsx b/src/components/Projects/ListProjectSolutions.tsx index adc9bf414..2f10ec202 100644 --- a/src/components/Projects/ListProjectSolutions.tsx +++ b/src/components/Projects/ListProjectSolutions.tsx @@ -14,6 +14,7 @@ import { showLoginPopup } from '../../lib/popup'; import { VoteButton } from './VoteButton.tsx'; import { GitHubIcon } from '../ReactIcons/GitHubIcon.tsx'; import { SelectLanguages } from './SelectLanguages.tsx'; +import type { ProjectFileType, ProjectFrontmatter } from '../../lib/project.ts'; export interface ProjectStatusDocument { _id?: string; @@ -59,12 +60,11 @@ type QueryParams = { type PageState = { currentPage: number; - languages: string[]; + language: string; }; -const VISITED_SOLUTIONS_KEY = 'visited-project-solutions'; - type ListProjectSolutionsProps = { + project: ProjectFrontmatter; projectId: string; }; @@ -93,12 +93,12 @@ const submittedAlternatives = [ ]; export function ListProjectSolutions(props: ListProjectSolutionsProps) { - const { projectId } = props; + const { projectId, project: projectData } = props; const toast = useToast(); const [pageState, setPageState] = useState({ currentPage: 0, - languages: [], + language: '', }); const [isLoading, setIsLoading] = useState(true); @@ -106,14 +106,13 @@ export function ListProjectSolutions(props: ListProjectSolutionsProps) { const [showLeavingRoadmapModal, setShowLeavingRoadmapModal] = useState< ListProjectSolutionsResponse['data'][number] | null >(null); - const [distinctLanguages, setDistinctLanguages] = useState([]); - const loadSolutions = async (page = 1, languages: string[] = []) => { + const loadSolutions = async (page = 1, language: string = '') => { const { response, error } = await httpGet( `${import.meta.env.PUBLIC_API_URL}/v1-list-project-solutions/${projectId}`, { currPage: page, - ...(languages.length > 0 ? { languages: languages.join(',') } : {}), + ...(language ? { languages: language } : {}), }, ); @@ -126,19 +125,6 @@ export function ListProjectSolutions(props: ListProjectSolutionsProps) { setSolutions(response); }; - const loadDistinctLanguages = async () => { - const { response, error } = await httpGet( - `${import.meta.env.PUBLIC_API_URL}/v1-list-project-languages/${projectId}`, - ); - - if (error || !response) { - toast.error(error?.message || 'Failed to load project languages'); - return; - } - - setDistinctLanguages(response); - }; - const handleSubmitVote = async ( solutionId: string, voteType: AllowedVoteType, @@ -190,7 +176,7 @@ export function ListProjectSolutions(props: ListProjectSolutionsProps) { const queryParams = getUrlParams() as QueryParams; setPageState({ currentPage: +(queryParams.p || '1'), - languages: (queryParams.l || '').split(',').filter(Boolean), + language: queryParams.l || '', }); }, []); @@ -200,25 +186,21 @@ export function ListProjectSolutions(props: ListProjectSolutionsProps) { return; } - if (pageState.currentPage !== 1 || pageState.languages.length > 0) { + if (pageState.currentPage !== 1 || pageState.language !== '') { setUrlParams({ p: String(pageState.currentPage), - l: pageState.languages.join(','), + l: pageState.language, }); } else { deleteUrlParam('p'); deleteUrlParam('l'); } - loadSolutions(pageState.currentPage, pageState.languages).finally(() => { + loadSolutions(pageState.currentPage, pageState.language).finally(() => { setIsLoading(false); }); }, [pageState]); - useEffect(() => { - loadDistinctLanguages().finally(() => {}); - }, []); - const isEmpty = solutions?.data.length === 0; if (isEmpty) { return ; @@ -231,26 +213,29 @@ export function ListProjectSolutions(props: ListProjectSolutionsProps) { /> ) : null; - const selectedLanguages = pageState.languages; + const selectedLanguage = pageState.language; return ( -
+
{leavingRoadmapModal} - { - const isAlreadySelected = selectedLanguages.includes(language); - const newLanguages = isAlreadySelected - ? selectedLanguages.filter((l) => l !== language) - : [...selectedLanguages, language]; - - setPageState({ - ...pageState, - languages: newLanguages, - }); - }} - /> +
+
+

+ {projectData.title} Solutions +

+ { + setPageState((prev) => ({ + ...prev, + language: prev.language === language ? '' : language, + })); + }} + /> +
+

{projectData.description}

+
{isLoading ? ( @@ -344,6 +329,6 @@ export function ListProjectSolutions(props: ListProjectSolutionsProps) { )} )} -
+ ); } diff --git a/src/components/Projects/SelectLanguages.tsx b/src/components/Projects/SelectLanguages.tsx index 933775eca..973a1b92c 100644 --- a/src/components/Projects/SelectLanguages.tsx +++ b/src/components/Projects/SelectLanguages.tsx @@ -1,6 +1,8 @@ -import { useRef, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { cn } from '../../lib/classname'; import { useOutsideClick } from '../../hooks/use-outside-click'; +import { httpGet } from '../../lib/http'; +import { useToast } from '../../hooks/use-toast'; const languageColors = new Map([ ['JavaScript', 'bg-[#f1e05a]'], @@ -37,69 +39,74 @@ const languageColors = new Map([ ]); type SelectLanguagesProps = { - languages: string[]; - selectedLanguages: string[]; + projectId: string; + selectedLanguage: string; onSelectLanguage: (language: string) => void; }; export function SelectLanguages(props: SelectLanguagesProps) { - const { languages, onSelectLanguage, selectedLanguages } = props; + const { projectId, onSelectLanguage, selectedLanguage } = props; const dropdownRef = useRef(null); + const toast = useToast(); + + const [distinctLanguages, setDistinctLanguages] = useState([]); const [isOpen, setIsOpen] = useState(false); + const loadDistinctLanguages = async () => { + const { response, error } = await httpGet( + `${import.meta.env.PUBLIC_API_URL}/v1-list-project-languages/${projectId}`, + ); + + if (error || !response) { + toast.error(error?.message || 'Failed to load project languages'); + return; + } + + setDistinctLanguages(response); + }; + useOutsideClick(dropdownRef, () => { setIsOpen(false); }); + useEffect(() => { + loadDistinctLanguages().finally(() => {}); + }, []); + return ( -
- {selectedLanguages.length > 0 && ( - <> - {selectedLanguages.map((language) => ( - - - {language} - - ))} - - )} +
+ -
- + {distinctLanguages.map((language) => { + const isSelected = selectedLanguage === language; - {isOpen && ( -
- {languages.map((language) => { - return ( - - ); - })} -
- )} -
+ return ( + + ); + })} +
+ )}
); } diff --git a/src/pages/projects/[projectId]/solutions.astro b/src/pages/projects/[projectId]/solutions.astro index 09031abaf..c345bb348 100644 --- a/src/pages/projects/[projectId]/solutions.astro +++ b/src/pages/projects/[projectId]/solutions.astro @@ -49,18 +49,11 @@ const githubUrl = `https://github.com/kamranahmedse/developer-roadmap/tree/maste
-
- - - -
+