From 4cae53345f5d11547d23eeffc1cf096d6f43ca1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asl=C4=B1=20G=C3=B6k?= Date: Mon, 16 Dec 2024 17:34:58 +0300 Subject: [PATCH] add glossary and tag type pages --- frontend/src/components/NavbarLayout.tsx | 1 + frontend/src/components/SubtypeCard.tsx | 51 +++++++ frontend/src/components/TagCard.tsx | 17 ++- frontend/src/components/TagType.tsx | 181 +++++++++++++++++++++++ frontend/src/routes/glossary.tsx | 89 +++++++++++ frontend/src/routes/index.tsx | 10 ++ frontend/src/routes/tag.test.tsx | 18 +-- frontend/src/routes/tag.tsx | 20 ++- 8 files changed, 365 insertions(+), 22 deletions(-) create mode 100644 frontend/src/components/SubtypeCard.tsx create mode 100644 frontend/src/components/TagType.tsx create mode 100644 frontend/src/routes/glossary.tsx diff --git a/frontend/src/components/NavbarLayout.tsx b/frontend/src/components/NavbarLayout.tsx index b3964039..19a96377 100644 --- a/frontend/src/components/NavbarLayout.tsx +++ b/frontend/src/components/NavbarLayout.tsx @@ -32,6 +32,7 @@ import { Sheet, SheetContent, SheetTrigger } from "./ui/sheet"; const links = [ { name: "Home", path: "/" }, + { name: "Glossary", path: "/glossary" }, //add glossary page to the navbar { name: "Tags", path: "/tags" }, ] as const; diff --git a/frontend/src/components/SubtypeCard.tsx b/frontend/src/components/SubtypeCard.tsx new file mode 100644 index 00000000..92f0c085 --- /dev/null +++ b/frontend/src/components/SubtypeCard.tsx @@ -0,0 +1,51 @@ +import { Card } from "@/components/ui/card"; +import { ArrowRight, Tags } from "lucide-react"; +import React from "react"; +import { Link } from "react-router-dom"; + +interface TagSubtypeCardProps { + tagSubtype: { + typeId: string; + tagCount: number; + description: string; // Added description field + }; +} + +export const TagSubtypeCard = React.forwardRef< + HTMLDivElement, + TagSubtypeCardProps +>(({ tagSubtype }, ref) => { + return ( + +
+ {/* Subtype Name */} +

+ {tagSubtype.typeId} +

+ + {/* Description */} +

{tagSubtype.description}

+ + {/* Number of Tags */} +
+ + {tagSubtype.tagCount} tags +
+ + {/* Navigation Link */} +
+ + View tag type page + + +
+
+
+ ); +}); diff --git a/frontend/src/components/TagCard.tsx b/frontend/src/components/TagCard.tsx index 6e7682a8..cee0d471 100644 --- a/frontend/src/components/TagCard.tsx +++ b/frontend/src/components/TagCard.tsx @@ -33,12 +33,17 @@ export const TagCard = React.forwardRef( {tag.questionCount} questions - {tag.tagType && ( -
- - {tag.tagType} -
- )} + + {tag.tagType && ( +
+ + {tag.tagType} +
+ )} +
{tag.logoImage && ( diff --git a/frontend/src/components/TagType.tsx b/frontend/src/components/TagType.tsx new file mode 100644 index 00000000..4272fcfb --- /dev/null +++ b/frontend/src/components/TagType.tsx @@ -0,0 +1,181 @@ +import { useParams } from "react-router-dom"; +import { useEffect, useState } from "react"; +import { Loader2 } from "lucide-react"; +import { TagCard } from "@/components/TagCard"; +import { useSearchTags } from "@/services/api/programmingForumComponents"; +import { TagDetails } from "@/services/api/programmingForumSchemas"; +import ErrorAlert from "@/components/ErrorAlert"; +import InfiniteScroll from "@/components/InfiniteScroll"; + +export default function SubtypePage() { + const { typeId } = useParams<{ typeId: string }>(); + const [tags, setTags] = useState([]); + const [tagTypeId, setTagTypeId] = useState(); + + const [pageSize, setPageSize] = useState(20); + const [previousData, setPreviousData] = useState<{ + items: TagDetails[]; + totalItems: number; + }>({ items: [], totalItems: 0 }); + + const { + data: tagSearchData, + isLoading, + error, + } = useSearchTags( + { + queryParams: { q: "", pageSize }, + }, + { enabled: true }, + ); + + useEffect(() => { + if (tagSearchData?.data) { + const availableTags = (tagSearchData.data as { items: TagDetails[] }) + .items; + + // Filter tags by the typeId + const filteredTags = availableTags.filter( + (tag) => tag.tagType === typeId, + ); + + setTags(filteredTags); + + // Set the tag type name and description + if (filteredTags.length > 0) { + setTagTypeId(typeId); + //setTagDescription(`Description for ${typeId} tags.`); // Replace with actual description if available + } else { + setTagTypeId("Unknown Tag Type"); + //setTagDescription("No description available for this tag type."); + } + } + }, [tagSearchData, typeId]); + + useEffect(() => { + if (tagSearchData?.data && !isLoading) { + setPreviousData(tagSearchData.data as typeof previousData); + } + }, [tagSearchData, isLoading]); + + const searchResultData = + (tagSearchData?.data as { + items?: TagDetails[]; + totalItems?: number; + }) || previousData; + + const next = () => { + setPageSize(pageSize + 20); + }; + + //descriptions for the tag types + const getDescription = (TypeId: string) => { + switch (TypeId) { + case "Programming Language": + return ( +

+ A programming language is a formal system used to communicate + instructions to a machine, particularly a computer. It consists of + syntax, semantics, and rules that allow developers to write software + and algorithms. Programming languages enable the creation of + programs that can execute tasks ranging from simple calculations to + complex data processing and system management. Well-known examples + include Python, Java, and C++. +

+ ); + case "Programming Paradigm": + return ( +

+ A programming paradigm refers to a fundamental style or approach to + programming that influences how software is structured and + developed. It encompasses the methodologies and principles that + guide the design and implementation of programs, including + object-oriented programming, functional programming, and procedural + programming. Different paradigms offer distinct ways of thinking + about and solving problems in software development. +

+ ); + case "Computer Science Term": + return ( +

+ A computer science term is a word or phrase that is part of the + technical vocabulary of computer science. These terms represent + concepts, theories, tools, or techniques that are essential to + understanding the field. Examples include terms like algorithm, data + structure, machine learning, and artificial intelligence. These + terms form the foundation of communication and learning within the + computer science discipline. +

+ ); + case "Software Library": + return ( +

+ A software library is a collection of pre-written code, functions, + and resources that developers can use to perform common tasks + without having to write code from scratch. Libraries are designed to + provide reusable components for tasks such as data manipulation, + user interface design, and network communication. Popular software + libraries include jQuery for web development, TensorFlow for machine + learning, and NumPy for numerical computations. +

+ ); + } + }; + + const description = typeId ? ( + getDescription(typeId) + ) : ( +

Loading description...

+ ); + + if (error) { + return ; + } + + return ( +
+ {isLoading ? ( +
+ +
+ ) : ( + <> + {/* Header */} +

{tagTypeId}

+ + {/* Render the description based on typeId */} +
{description}
+ + {/* Tags in this Category Section */} +

+ Tags in This Category: +

+ + {/* Infinite Scroll for displaying Related Tags */} +
+ pageSize + : false + } + isLoading={isLoading} + > + {tags.length > 0 ? ( + tags.map((tag) => ) + ) : ( +

+ No related tags found for this tag type. +

+ )} +
+
+ + )} +
+ ); +} diff --git a/frontend/src/routes/glossary.tsx b/frontend/src/routes/glossary.tsx new file mode 100644 index 00000000..d05b2453 --- /dev/null +++ b/frontend/src/routes/glossary.tsx @@ -0,0 +1,89 @@ +import { TagSubtypeCard } from "@/components/SubtypeCard"; +import { useSearchTags } from "@/services/api/programmingForumComponents"; +import { TagDetails } from "@/services/api/programmingForumSchemas"; +import { Loader2 } from "lucide-react"; +import { useEffect, useState } from "react"; +import ErrorAlert from "@/components/ErrorAlert"; + +export default function GlossaryPage() { + const [tagCounts, setTagCounts] = useState< + { typeId: string; tagCount: number; description: string }[] // Added description field + >([]); + const [, setAvailableTags] = useState([]); + + const { + data: tagSearchData, + isLoading, + error, + } = useSearchTags( + { queryParams: { q: "", pageSize: 400 } }, + { enabled: true }, + ); + + useEffect(() => { + if (tagSearchData?.data) { + // Extract available tags + const tags = (tagSearchData.data as { items: TagDetails[] }).items; + setAvailableTags(tags); + + // Define the 4 tag types with descriptions from wikidata + const tagTypes = [ + { + typeId: "Programming Language", + description: "language for communicating instructions to a machine", + }, + { + typeId: "Software Library", + description: + "collection of non-volatile resources used by computer programs, often for software development", + }, + { + typeId: "Programming Paradigm", + description: + "category of programming languages according to what methodology of designing and implementing programs their features support", + }, + { + typeId: "Computer Science Topic", + description: + "technical term; word or phrase that is part of computer science terminology", + }, + ]; + + // group and count tags by tagType + const counts = tagTypes.map((type) => { + const filteredTags = tags.filter((tag) => tag.tagType === type.typeId); + return { + typeId: type.typeId, + tagCount: filteredTags.length, + description: type.description, + }; + }); + + setTagCounts(counts); + } + }, [tagSearchData]); + + if (error) { + return ; + } + + return ( +
+

Explore Various Tag Types

+
+ {tagCounts.map((tagCount) => ( + + ))} + + {isLoading && ( +
+ +
+ )} +
+
+ ); +} diff --git a/frontend/src/routes/index.tsx b/frontend/src/routes/index.tsx index cc042e4c..a89fddd0 100644 --- a/frontend/src/routes/index.tsx +++ b/frontend/src/routes/index.tsx @@ -13,6 +13,8 @@ import { Search } from "./search"; import Signup from "./signup"; import TagPage from "./tag"; import { BookmarkedQuestions } from "@/routes/bookmarks"; +import Glossary from "./glossary"; +import TagTypePage from "@/components/TagType"; export const routes: RouteObject[] = [ { @@ -66,6 +68,14 @@ export const routes: RouteObject[] = [ path: "/tags/new", Component: CreateTagPage, }, + { + path: "/glossary", + Component: Glossary, + }, + { + path: "/tagtype/:typeId", + Component: TagTypePage, + }, ]; export const routeConfig: RouteObject[] = [ diff --git a/frontend/src/routes/tag.test.tsx b/frontend/src/routes/tag.test.tsx index 38ce66b7..a19b0493 100644 --- a/frontend/src/routes/tag.test.tsx +++ b/frontend/src/routes/tag.test.tsx @@ -100,14 +100,14 @@ describe("TagPage", () => { token: "mock-token", }); - render( - - - } /> - - , - ); + render( + + + } /> + + , + ); - expect(screen.getByRole("button", { name: /follow/i })).toBeInTheDocument(); - }); + expect(screen.getByRole("button", { name: /follow/i })).toBeInTheDocument(); + }); }); diff --git a/frontend/src/routes/tag.tsx b/frontend/src/routes/tag.tsx index 6604933d..0fe839a6 100644 --- a/frontend/src/routes/tag.tsx +++ b/frontend/src/routes/tag.tsx @@ -106,7 +106,8 @@ export default function TagPage() { tagId: tagId!, following: tag.following, }} - />)} + /> + )}
{tag.logoImage && ( )} - {tag.tagType && ( -
- - {tag.tagType} -
- )} + + {tag.tagType && ( +
+ + {tag.tagType} +
+ )} +