diff --git a/backend/apps/common/utils.py b/backend/apps/common/utils.py index d0cc56443d..4ec451e572 100644 --- a/backend/apps/common/utils.py +++ b/backend/apps/common/utils.py @@ -12,6 +12,26 @@ from humanize import intword, naturaltime +def convert_to_camel_case(text: str) -> str: + """Convert a string to camelCase. + + Args: + text (str): The input string. + + Returns: + str: The converted string in camelCase. + + """ + parts = text.split("_") + offset = 1 if text.startswith("_") else 0 + head = parts[offset : offset + 1] or [text] + + segments = [f"_{head[0]}" if offset else head[0]] + segments.extend(word.capitalize() for word in parts[offset + 1 :]) + + return "".join(segments) + + def convert_to_snake_case(text: str) -> str: """Convert a string to snake_case. diff --git a/backend/apps/core/api/algolia.py b/backend/apps/core/api/algolia.py index 70ad7218b8..aa2361efb7 100644 --- a/backend/apps/core/api/algolia.py +++ b/backend/apps/core/api/algolia.py @@ -14,7 +14,7 @@ from apps.common.index import IndexBase from apps.common.utils import get_user_ip_address -from apps.core.utils.index import get_params_for_index +from apps.core.utils.index import deep_camelize, get_params_for_index from apps.core.validators import validate_search_params CACHE_PREFIX = "algolia_proxy" @@ -59,7 +59,7 @@ def get_search_results( search_result = response.results[0].to_dict() return { - "hits": search_result.get("hits", []), + "hits": deep_camelize(search_result["hits"]), "nbPages": search_result.get("nbPages", 0), } diff --git a/backend/apps/core/utils/index.py b/backend/apps/core/utils/index.py index 7a37b94dc1..0720619e64 100644 --- a/backend/apps/core/utils/index.py +++ b/backend/apps/core/utils/index.py @@ -6,6 +6,8 @@ from algoliasearch_django.registration import RegistrationError from django.apps import apps +from apps.common.utils import convert_to_camel_case + def get_params_for_index(index_name: str) -> dict: """Return search parameters based on the index name. @@ -159,3 +161,23 @@ def unregister_indexes(app_names: tuple[str, ...] = ("github", "owasp")) -> None for model in apps.get_app_config(app_name).get_models(): with contextlib.suppress(RegistrationError): unregister(model) + + +def deep_camelize(obj) -> dict | list: + """Deep camelize. + + Args: + obj: The object to camelize. + + Returns: + The camelize object. + + """ + if isinstance(obj, dict): + return { + convert_to_camel_case(key.removeprefix("idx_")): deep_camelize(value) + for key, value in obj.items() + } + if isinstance(obj, list): + return [deep_camelize(item) for item in obj] + return obj diff --git a/backend/poetry.lock b/backend/poetry.lock index 93d09c3001..9a6016d9f4 100644 --- a/backend/poetry.lock +++ b/backend/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" diff --git a/backend/tests/apps/common/utils_test.py b/backend/tests/apps/common/utils_test.py index 80f384cb16..30cfe659ee 100644 --- a/backend/tests/apps/common/utils_test.py +++ b/backend/tests/apps/common/utils_test.py @@ -5,6 +5,7 @@ from django.conf import settings from apps.common.utils import ( + convert_to_camel_case, convert_to_snake_case, get_absolute_url, get_user_ip_address, @@ -16,6 +17,23 @@ class TestUtils: + @pytest.mark.parametrize( + ("input_text", "expected_output"), + [ + ("simple", "simple"), + ("my_variable", "myVariable"), + ("long_variable_name", "longVariableName"), + ("top_contributor_project", "topContributorProject"), + ("_leading_underscore", "_leadingUnderscore"), + ("trailing_underscore_", "trailingUnderscore"), + ("multiple__underscores", "multipleUnderscores"), + ("multiple__under___scores", "multipleUnderScores"), + ("alreadyCamelCase", "alreadyCamelCase"), + ], + ) + def test_convert_to_camel_case(self, input_text, expected_output): + assert convert_to_camel_case(input_text) == expected_output + @pytest.mark.parametrize( ("text", "expected"), [ diff --git a/frontend/__tests__/unit/data/mockChapterData.ts b/frontend/__tests__/unit/data/mockChapterData.ts index d81255edec..b7fd1e62d7 100644 --- a/frontend/__tests__/unit/data/mockChapterData.ts +++ b/frontend/__tests__/unit/data/mockChapterData.ts @@ -2,28 +2,28 @@ export const mockChapterData = { active_chapters_count: 0, chapters: [ { - created_at: 1568320077, + createdAt: 1568320077, leaders: ['Isanori Sakanashi', 'Takeshi Murai', 'Yukiharu Niwa'], name: 'Chapter 1', - related_urls: [ + relatedUrls: [ 'https://join.slack.com/t/owaspnagoya/shared_invite/enQtMzM0OTkwMTM1NDQxLTI5ODQzZDViMDg4NDA2MzA4OGFmZmVmMWRjM2Y0ODBkMGM2YzVkMzU0MGU5Y2IxMTE5NmM2Yjg3Zjg3YzRhOWU', 'https://www.facebook.com/login/?next=https%3A%2F%2Fwww.facebook.com%2Fowaspnagoya', 'https://x.com/owaspnagoya', ], - top_contributors: [ + topContributors: [ { - avatar_url: 'https://avatars.githubusercontent.com/u/58754211?v=4', - contributions_count: 286, + avatarUrl: 'https://avatars.githubusercontent.com/u/58754211?v=4', + contributionsCount: 286, login: 'Isanori-Sakanashi', name: 'Isanori Sakanashi', }, ], summary: 'This is a summary of Chapter 1.', - updated_at: 1723121926, + updatedAt: 1723121926, key: 'chapter_1', url: 'https://owasp.org/www-chapter-nagoya', objectID: '539', - is_active: true, + isActive: true, _geoloc: { lat: 35.1815, lng: 136.9066, diff --git a/frontend/__tests__/unit/data/mockCommitteeData.ts b/frontend/__tests__/unit/data/mockCommitteeData.ts index 83262ce2e4..97cafd064f 100644 --- a/frontend/__tests__/unit/data/mockCommitteeData.ts +++ b/frontend/__tests__/unit/data/mockCommitteeData.ts @@ -1,23 +1,23 @@ export const mockCommitteeData = { committees: [ { - created_at: 1596744799, + createdAt: 1596744799, leaders: ['Edmond Momartin', 'Garth Boyd', 'Kyle Smith'], name: 'Committee 1', - related_urls: [ + relatedUrls: [ 'https://app.slack.com/client/T04T40NHX/C010AF25WSZ/details/top', 'https://www.youtube.com/channel/UCE2nt-oqRjwEAPSBtCtNVSw', ], - top_contributors: [ + topContributors: [ { - avatar_url: 'https://avatars.githubusercontent.com/u/20112179?v=4', - contributions_count: 165, + avatarUrl: 'https://avatars.githubusercontent.com/u/20112179?v=4', + contributionsCount: 165, login: 'securestep9', name: 'Sam Stepanyan', }, ], summary: 'This is a summary of Committee 1.', - updated_at: 1727390473, + updatedAt: 1727390473, key: 'committee_1', url: 'https://owasp.org/www-committee-chapter', objectID: '4', diff --git a/frontend/__tests__/unit/data/mockContributeData.ts b/frontend/__tests__/unit/data/mockContributeData.ts index 37bd52a1e8..b199e20e1e 100644 --- a/frontend/__tests__/unit/data/mockContributeData.ts +++ b/frontend/__tests__/unit/data/mockContributeData.ts @@ -6,17 +6,17 @@ dayjs.extend(relativeTime) export const mockContributeData = { issues: [ { - comments_count: 1, - created_at: dayjs().subtract(4, 'months').unix(), + commentsCount: 1, + createdAt: dayjs().subtract(4, 'months').unix(), hint: 'Hint', labels: [], objectID: '9180', - project_key: 'project-nest', - project_name: 'Owasp Nest', - repository_languages: ['Python', 'TypeScript'], + projectKey: 'project-nest', + projectName: 'Owasp Nest', + repositoryLanguages: ['Python', 'TypeScript'], summary: 'This is a summary of Contribution 1', title: 'Contribution 1', - updated_at: 1734727031, + updatedAt: 1734727031, url: 'https://github.com/OWASP/Nest/issues/225', }, ], diff --git a/frontend/__tests__/unit/data/mockHomeData.ts b/frontend/__tests__/unit/data/mockHomeData.ts index 7aaeaf045e..337558f1ae 100644 --- a/frontend/__tests__/unit/data/mockHomeData.ts +++ b/frontend/__tests__/unit/data/mockHomeData.ts @@ -162,13 +162,13 @@ export const mockAlgoliaData = { hits: [ { objectID: '539', - idx_name: 'OWASP Nagoya', - idx_suggested_location: 'Nagoya, Aichi Prefecture, Japan', - idx_region: 'Asia', - idx_top_contributors: [ + name: 'OWASP Nagoya', + suggestedLocation: 'Nagoya, Aichi Prefecture, Japan', + region: 'Asia', + topContributors: [ { - avatar_url: 'https://avatars.githubusercontent.com/u/58754211?v=4', - contributions_count: 286, + avatarUrl: 'https://avatars.githubusercontent.com/u/58754211?v=4', + contributionsCount: 286, login: 'isanori-sakanashi-owasp', name: 'Isanori Sakanashi', }, diff --git a/frontend/__tests__/unit/data/mockOrganizationData.ts b/frontend/__tests__/unit/data/mockOrganizationData.ts index 7ce5122f06..5a37e4045d 100644 --- a/frontend/__tests__/unit/data/mockOrganizationData.ts +++ b/frontend/__tests__/unit/data/mockOrganizationData.ts @@ -2,34 +2,34 @@ export const mockOrganizationData = { hits: [ { objectID: 'org1', - avatar_url: 'https://avatars.githubusercontent.com/u/1234567?v=4', - collaborators_count: 50, + avatarUrl: 'https://avatars.githubusercontent.com/u/1234567?v=4', + collaboratorsCount: 50, company: 'Test Company', - created_at: 1596744799, + createdAt: 1596744799, description: 'This is a test organization', email: 'org@example.com', - followers_count: 1000, + followersCount: 1000, location: 'San Francisco, CA', login: 'test-org', name: 'Test Organization', - public_repositories_count: 2000, - updated_at: 1727390473, + publicRepositoriesCount: 2000, + updatedAt: 1727390473, url: 'https://github.com/test-org', }, { objectID: 'org2', - avatar_url: 'https://avatars.githubusercontent.com/u/7654321?v=4', - collaborators_count: 75, + avatarUrl: 'https://avatars.githubusercontent.com/u/7654321?v=4', + collaboratorsCount: 75, company: 'Another Company', - created_at: 1496744799, + createdAt: 1496744799, description: 'Another test organization', email: 'another-org@example.com', - followers_count: 2000, + followersCount: 2000, location: 'New York, NY', login: 'another-org', name: 'Another Organization', - public_repositories_count: 1500, - updated_at: 1727390473, + publicRepositoriesCount: 1500, + updatedAt: 1727390473, url: 'https://github.com/another-org', }, ], diff --git a/frontend/__tests__/unit/data/mockProjectData.ts b/frontend/__tests__/unit/data/mockProjectData.ts index cbcaf2aa9c..983876db53 100644 --- a/frontend/__tests__/unit/data/mockProjectData.ts +++ b/frontend/__tests__/unit/data/mockProjectData.ts @@ -7,7 +7,7 @@ export const mockProjectData = { summary: 'This is a summary of Project 1.', level: '1', leaders: ['Leader 1'], - top_contributors: [ + topContributors: [ { avatarUrl: 'https://avatars.githubusercontent.com/avatar1.png', contributionsCount: 10, @@ -18,12 +18,12 @@ export const mockProjectData = { }, ], topics: ['Topic 1'], - updated_at: '2023-10-01', - forks_count: 10, + updatedAt: '2023-10-01', + forksCount: 10, key: 'project_1', - stars_count: 20, - contributors_count: 5, - is_active: true, + starsCount: 20, + contributorsCount: 5, + isActive: true, }, ], } diff --git a/frontend/__tests__/unit/data/mockUserData.ts b/frontend/__tests__/unit/data/mockUserData.ts index a20a3cc7c5..877970029d 100644 --- a/frontend/__tests__/unit/data/mockUserData.ts +++ b/frontend/__tests__/unit/data/mockUserData.ts @@ -1,22 +1,22 @@ export const mockUserData = { users: [ { - avatar_url: 'https://avatars.githubusercontent.com/avatar1.jpg', + avatarUrl: 'https://avatars.githubusercontent.com/avatar1.jpg', company: 'OWASP', key: 'user_1', login: 'user_1', name: 'John Doe', - followers_count: 1000, - public_repositories_count: 2000, + followersCount: 1000, + publicRepositoriesCount: 2000, }, { - avatar_url: 'https://avatars.githubusercontent.com/avatar2.jpg', + avatarUrl: 'https://avatars.githubusercontent.com/avatar2.jpg', company: 'Security Co', key: 'user_2', login: 'user_2', name: 'Jane Smith', - followers_count: 3000, - public_repositories_count: 4000, + followersCount: 3000, + publicRepositoriesCount: 4000, }, ], } diff --git a/frontend/src/app/about/page.tsx b/frontend/src/app/about/page.tsx index cb909c5da0..e9ffd22954 100644 --- a/frontend/src/app/about/page.tsx +++ b/frontend/src/app/about/page.tsx @@ -20,16 +20,16 @@ import FontAwesomeIconWrapper from 'wrappers/FontAwesomeIconWrapper' import { ErrorDisplay, handleAppError } from 'app/global-error' import { GET_PROJECT_METADATA, GET_TOP_CONTRIBUTORS } from 'server/queries/projectQueries' import { GET_LEADER_DATA } from 'server/queries/userQueries' -import { TopContributorsTypeGraphql } from 'types/contributor' -import { ProjectTypeGraphql } from 'types/project' -import { User } from 'types/user' +import type { Contributor } from 'types/contributor' +import type { Project } from 'types/project' +import type { User } from 'types/user' import { aboutText, technologies } from 'utils/aboutData' import AnchorTitle from 'components/AnchorTitle' import AnimatedCounter from 'components/AnimatedCounter' import LoadingSpinner from 'components/LoadingSpinner' import Markdown from 'components/MarkdownWrapper' import SecondaryCard from 'components/SecondaryCard' -import TopContributors from 'components/TopContributors' +import TopContributorsList from 'components/TopContributorsList' import UserCard from 'components/UserCard' const leaders = { @@ -54,8 +54,8 @@ const About = () => { } ) - const [projectMetadata, setProjectMetadata] = useState(null) - const [topContributors, setTopContributors] = useState([]) + const [projectMetadata, setProjectMetadata] = useState(null) + const [topContributors, setTopContributors] = useState([]) useEffect(() => { if (projectMetadataResponse?.project) { @@ -122,7 +122,7 @@ const About = () => { {topContributors && ( - ({} as ChapterTypeGraphQL) - const [topContributors, setTopContributors] = useState([]) + const [chapter, setChapter] = useState({} as Chapter) + const [topContributors, setTopContributors] = useState([]) const [isLoading, setIsLoading] = useState(true) const { data, error: graphQLRequestError } = useQuery(GET_CHAPTER_DATA, { @@ -62,8 +62,8 @@ export default function ChapterDetailsPage() { return ( { - const [geoLocData, setGeoLocData] = useState([]) + const [geoLocData, setGeoLocData] = useState([]) const { items: chapters, isLoaded, @@ -21,7 +21,7 @@ const ChaptersPage = () => { searchQuery, handleSearch, handlePageChange, - } = useSearchPage({ + } = useSearchPage({ indexName: 'chapters', pageTitle: 'OWASP Chapters', }) @@ -34,7 +34,7 @@ const ChaptersPage = () => { currentPage, hitsPerPage: currentPage === 1 ? 1000 : 25, } - const data: AlgoliaResponseType = await fetchAlgoliaData( + const data: AlgoliaResponse = await fetchAlgoliaData( searchParams.indexName, searchParams.query, searchParams.currentPage, @@ -46,16 +46,16 @@ const ChaptersPage = () => { }, [currentPage]) const router = useRouter() - const renderChapterCard = (chapter: ChapterTypeAlgolia) => { - const params: string[] = ['updated_at'] + const renderChapterCard = (chapter: Chapter) => { + const params: string[] = ['updatedAt'] const filteredIcons = getFilteredIcons(chapter, params) - const formattedUrls = handleSocialUrls(chapter.related_urls) + const formattedUrls = handleSocialUrls(chapter.relatedUrls) const handleButtonClick = () => { router.push(`/chapters/${chapter.key}`) } - const SubmitButton = { + const submitButton = { label: 'View Details', icon: , onclick: handleButtonClick, @@ -68,8 +68,8 @@ const ChaptersPage = () => { url={`/chapters/${chapter.key}`} summary={chapter.summary} icons={filteredIcons} - topContributors={chapter.top_contributors} - button={SubmitButton} + topContributors={chapter.topContributors} + button={submitButton} social={formattedUrls} /> ) @@ -100,7 +100,7 @@ const ChaptersPage = () => { }} /> )} - {chapters && chapters.filter((chapter) => chapter.is_active).map(renderChapterCard)} + {chapters && chapters.filter((chapter) => chapter.isActive).map(renderChapterCard)} ) } diff --git a/frontend/src/app/committees/[committeeKey]/page.tsx b/frontend/src/app/committees/[committeeKey]/page.tsx index 9307454d94..2450fcde97 100644 --- a/frontend/src/app/committees/[committeeKey]/page.tsx +++ b/frontend/src/app/committees/[committeeKey]/page.tsx @@ -12,15 +12,16 @@ import { useParams } from 'next/navigation' import { useState, useEffect } from 'react' import { ErrorDisplay, handleAppError } from 'app/global-error' import { GET_COMMITTEE_DATA } from 'server/queries/committeeQueries' -import type { CommitteeDetailsTypeGraphQL } from 'types/committee' -import { TopContributorsTypeGraphql } from 'types/contributor' +import type { Committee } from 'types/committee' +import type { Contributor } from 'types/contributor' import { formatDate } from 'utils/dateFormatter' import DetailsCard from 'components/CardDetailsPage' import LoadingSpinner from 'components/LoadingSpinner' + export default function CommitteeDetailsPage() { const { committeeKey } = useParams<{ committeeKey: string }>() - const [committee, setCommittee] = useState(null) - const [topContributors, setTopContributors] = useState([]) + const [committee, setCommittee] = useState(null) + const [topContributors, setTopContributors] = useState([]) const [isLoading, setIsLoading] = useState(true) const { data, error: graphQLRequestError } = useQuery(GET_COMMITTEE_DATA, { diff --git a/frontend/src/app/committees/page.tsx b/frontend/src/app/committees/page.tsx index 0f2dadec6e..e6d59dc054 100644 --- a/frontend/src/app/committees/page.tsx +++ b/frontend/src/app/committees/page.tsx @@ -2,7 +2,7 @@ import { useSearchPage } from 'hooks/useSearchPage' import { useRouter } from 'next/navigation' import FontAwesomeIconWrapper from 'wrappers/FontAwesomeIconWrapper' -import { CommitteeTypeAlgolia } from 'types/committee' +import type { Committee } from 'types/committee' import { getFilteredIcons, handleSocialUrls } from 'utils/utility' import Card from 'components/Card' import SearchPageLayout from 'components/SearchPageLayout' @@ -16,20 +16,20 @@ const CommitteesPage = () => { searchQuery, handleSearch, handlePageChange, - } = useSearchPage({ + } = useSearchPage({ indexName: 'committees', pageTitle: 'OWASP Committees', }) const router = useRouter() - const renderCommitteeCard = (committee: CommitteeTypeAlgolia) => { - const params: string[] = ['updated_at'] + const renderCommitteeCard = (committee: Committee) => { + const params: string[] = ['updatedAt'] const filteredIcons = getFilteredIcons(committee, params) - const formattedUrls = handleSocialUrls(committee.related_urls) + const formattedUrls = handleSocialUrls(committee.relatedUrls) const handleButtonClick = () => { router.push(`/committees/${committee.key}`) } - const SubmitButton = { + const submitButton = { label: 'View Details', icon: , onclick: handleButtonClick, @@ -42,8 +42,8 @@ const CommitteesPage = () => { url={`/committees/${committee.key}`} summary={committee.summary} icons={filteredIcons} - topContributors={committee.top_contributors} - button={SubmitButton} + topContributors={committee.topContributors} + button={submitButton} social={formattedUrls} tooltipLabel={`Learn more about ${committee.name}`} /> diff --git a/frontend/src/app/contribute/page.tsx b/frontend/src/app/contribute/page.tsx index b9cee29dd3..5d07bf3a52 100644 --- a/frontend/src/app/contribute/page.tsx +++ b/frontend/src/app/contribute/page.tsx @@ -4,7 +4,7 @@ import { useSearchPage } from 'hooks/useSearchPage' import React, { useState } from 'react' import FontAwesomeIconWrapper from 'wrappers/FontAwesomeIconWrapper' -import { IssueType } from 'types/issue' +import type { Issue } from 'types/issue' import { getFilteredIcons } from 'utils/utility' import Card from 'components/Card' @@ -20,15 +20,15 @@ const ContributePage = () => { searchQuery, handleSearch, handlePageChange, - } = useSearchPage({ + } = useSearchPage({ indexName: 'issues', pageTitle: 'OWASP Issues', }) const [modalOpenIndex, setModalOpenIndex] = useState(null) - const renderContributeCard = (issue: IssueType, index: number) => { - const params: string[] = ['created_at', 'comments_count'] + const renderContributeCard = (issue: Issue, index: number) => { + const params: string[] = ['createdAt', 'commentsCount'] const filteredIcons = getFilteredIcons(issue, params) const SubmitButton = { @@ -49,8 +49,8 @@ const ContributePage = () => { key={issue.objectID} title={issue.title} url={issue.url} - projectName={issue.project_name} - projectLink={issue.project_url} + projectName={issue.projectName} + projectLink={issue.projectUrl} summary={issue.summary} icons={filteredIcons} button={SubmitButton} diff --git a/frontend/src/app/members/[memberKey]/page.tsx b/frontend/src/app/members/[memberKey]/page.tsx index f69ad3edfd..b97a1e773f 100644 --- a/frontend/src/app/members/[memberKey]/page.tsx +++ b/frontend/src/app/members/[memberKey]/page.tsx @@ -12,13 +12,12 @@ import { useParams } from 'next/navigation' import React, { useState, useEffect, useRef } from 'react' import { handleAppError, ErrorDisplay } from 'app/global-error' import { GET_USER_DATA } from 'server/queries/userQueries' -import type { - ProjectIssuesType, - ProjectMilestonesType, - ProjectReleaseType, - RepositoryCardProps, -} from 'types/project' -import type { ItemCardPullRequests, UserDetailsProps } from 'types/user' +import type { Issue } from 'types/issue' +import type { Milestone } from 'types/milestone' +import type { RepositoryCardProps } from 'types/project' +import type { PullRequest } from 'types/pullRequest' +import type { Release } from 'types/release' +import type { UserDetails } from 'types/user' import { formatDate } from 'utils/dateFormatter' import { drawContributions, fetchHeatmapData, HeatmapData } from 'utils/helpers/githubHeatmap' import DetailsCard from 'components/CardDetailsPage' @@ -26,12 +25,12 @@ import LoadingSpinner from 'components/LoadingSpinner' const UserDetailsPage: React.FC = () => { const { memberKey } = useParams() - const [user, setUser] = useState() - const [issues, setIssues] = useState([]) + const [user, setUser] = useState() + const [issues, setIssues] = useState([]) const [topRepositories, setTopRepositories] = useState([]) - const [milestones, setMilestones] = useState([]) - const [pullRequests, setPullRequests] = useState([]) - const [releases, setReleases] = useState([]) + const [milestones, setMilestones] = useState([]) + const [pullRequests, setPullRequests] = useState([]) + const [releases, setReleases] = useState([]) const [data, setData] = useState({} as HeatmapData) const [isLoading, setIsLoading] = useState(true) const [username, setUsername] = useState('') diff --git a/frontend/src/app/members/page.tsx b/frontend/src/app/members/page.tsx index 5fb910ea55..af5f1ef98a 100644 --- a/frontend/src/app/members/page.tsx +++ b/frontend/src/app/members/page.tsx @@ -2,7 +2,7 @@ import { useSearchPage } from 'hooks/useSearchPage' import { useRouter } from 'next/navigation' import FontAwesomeIconWrapper from 'wrappers/FontAwesomeIconWrapper' -import { User } from 'types/user' +import type { User } from 'types/user' import SearchPageLayout from 'components/SearchPageLayout' import UserCard from 'components/UserCard' @@ -28,7 +28,7 @@ const UsersPage = () => { } const renderUserCard = (user: User) => { - const SubmitButton = { + const submitButton = { label: 'View Details', icon: , onclick: () => handleButtonClick(user), @@ -36,15 +36,15 @@ const UsersPage = () => { return ( ) } diff --git a/frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/page.tsx b/frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/page.tsx index cb416b49ac..abfaeb7912 100644 --- a/frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/page.tsx +++ b/frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/page.tsx @@ -13,7 +13,7 @@ import { useParams } from 'next/navigation' import { useEffect, useState } from 'react' import { handleAppError, ErrorDisplay } from 'app/global-error' import { GET_REPOSITORY_DATA } from 'server/queries/repositoryQueries' -import { TopContributorsTypeGraphql } from 'types/contributor' +import type { Contributor } from 'types/contributor' import { formatDate } from 'utils/dateFormatter' import DetailsCard from 'components/CardDetailsPage' import LoadingSpinner from 'components/LoadingSpinner' @@ -21,7 +21,7 @@ import LoadingSpinner from 'components/LoadingSpinner' const RepositoryDetailsPage = () => { const { repositoryKey, organizationKey } = useParams() const [repository, setRepository] = useState(null) - const [topContributors, setTopContributors] = useState([]) + const [topContributors, setTopContributors] = useState([]) const [recentPullRequests, setRecentPullRequests] = useState(null) const [isLoading, setIsLoading] = useState(true) const { data, error: graphQLRequestError } = useQuery(GET_REPOSITORY_DATA, { diff --git a/frontend/src/app/organizations/page.tsx b/frontend/src/app/organizations/page.tsx index 147f24228d..754d517045 100644 --- a/frontend/src/app/organizations/page.tsx +++ b/frontend/src/app/organizations/page.tsx @@ -2,7 +2,7 @@ import { useSearchPage } from 'hooks/useSearchPage' import { useRouter } from 'next/navigation' import FontAwesomeIconWrapper from 'wrappers/FontAwesomeIconWrapper' -import { OrganizationTypeAlgolia } from 'types/organization' +import type { Organization } from 'types/organization' import SearchPageLayout from 'components/SearchPageLayout' import UserCard from 'components/UserCard' @@ -15,7 +15,7 @@ const OrganizationPage = () => { searchQuery, handleSearch, handlePageChange, - } = useSearchPage({ + } = useSearchPage({ indexName: 'organizations', pageTitle: 'GitHub Organizations', hitsPerPage: 24, @@ -23,12 +23,12 @@ const OrganizationPage = () => { const router = useRouter() - const renderOrganizationCard = (organization: OrganizationTypeAlgolia) => { + const renderOrganizationCard = (organization: Organization) => { const handleButtonClick = () => { router.push(`/organizations/${organization.login}`) } - const SubmitButton = { + const submitButton = { label: 'View Profile', icon: , onclick: handleButtonClick, @@ -36,16 +36,16 @@ const OrganizationPage = () => { return ( ) } diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index ec766d89a2..15e2c85f60 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -21,10 +21,10 @@ import Link from 'next/link' import { useEffect, useState } from 'react' import { fetchAlgoliaData } from 'server/fetchAlgoliaData' import { GET_MAIN_PAGE_DATA } from 'server/queries/homeQueries' -import { AlgoliaResponseType } from 'types/algolia' -import { ChapterTypeAlgolia } from 'types/chapter' -import { EventType } from 'types/event' -import { MainPageData } from 'types/home' +import type { AlgoliaResponse } from 'types/algolia' +import type { Chapter } from 'types/chapter' +import type { Event } from 'types/event' +import type { MainPageData } from 'types/home' import { capitalize } from 'utils/capitalize' import { formatDate, formatDateRange } from 'utils/dateFormatter' import AnchorTitle from 'components/AnchorTitle' @@ -40,7 +40,7 @@ import RecentIssues from 'components/RecentIssues' import RecentPullRequests from 'components/RecentPullRequests' import RecentReleases from 'components/RecentReleases' import SecondaryCard from 'components/SecondaryCard' -import TopContributors from 'components/TopContributors' +import TopContributorsList from 'components/TopContributorsList' import { TruncatedText } from 'components/TruncatedText' export default function Home() { @@ -50,7 +50,7 @@ export default function Home() { variables: { distinct: true }, }) - const [geoLocData, setGeoLocData] = useState([]) + const [geoLocData, setGeoLocData] = useState([]) const [modalOpenIndex, setModalOpenIndex] = useState(null) useEffect(() => { @@ -79,7 +79,7 @@ export default function Home() { currentPage: 1, hitsPerPage: 1000, } - const data: AlgoliaResponseType = await fetchAlgoliaData( + const data: AlgoliaResponse = await fetchAlgoliaData( searchParams.indexName, searchParams.query, searchParams.currentPage, @@ -163,7 +163,7 @@ export default function Home() { className="overflow-hidden" >
- {data.upcomingEvents.map((event: EventType, index: number) => ( + {data.upcomingEvents.map((event: Event, index: number) => (
- { const { projectKey } = useParams() const [isLoading, setIsLoading] = useState(true) - const [project, setProject] = useState(null) - const [topContributors, setTopContributors] = useState([]) + const [project, setProject] = useState(null) + const [topContributors, setTopContributors] = useState([]) const { data, error: graphQLRequestError } = useQuery(GET_PROJECT_DATA, { variables: { key: projectKey }, }) @@ -91,16 +91,16 @@ const ProjectDetailsPage = () => { return ( { handlePageChange, handleSortChange, handleOrderChange, - } = useSearchPage({ + } = useSearchPage({ indexName: 'projects', pageTitle: 'OWASP Projects', defaultSortBy: 'default', @@ -30,14 +30,14 @@ const ProjectsPage = () => { }) const router = useRouter() - const renderProjectCard = (project: ProjectTypeAlgolia) => { - const params: string[] = ['forks_count', 'stars_count', 'contributors_count'] + const renderProjectCard = (project: Project) => { + const params: string[] = ['forksCount', 'starsCount', 'contributorsCount'] const filteredIcons = getFilteredIcons(project, params) const handleButtonClick = () => { router.push(`/projects/${project.key}`) } - const SubmitButton = { + const submitButton = { label: 'View Details', icon: , onclick: handleButtonClick, @@ -45,13 +45,13 @@ const ProjectsPage = () => { return ( ) @@ -78,7 +78,7 @@ const ProjectsPage = () => { } totalPages={totalPages} > - {projects && projects.filter((project) => project.is_active).map(renderProjectCard)} + {projects && projects.filter((project) => project.isActive).map(renderProjectCard)} ) } diff --git a/frontend/src/app/snapshots/[id]/page.tsx b/frontend/src/app/snapshots/[id]/page.tsx index 65d3e82af3..02bfe38e45 100644 --- a/frontend/src/app/snapshots/[id]/page.tsx +++ b/frontend/src/app/snapshots/[id]/page.tsx @@ -7,19 +7,19 @@ import React, { useState, useEffect } from 'react' import FontAwesomeIconWrapper from 'wrappers/FontAwesomeIconWrapper' import { handleAppError, ErrorDisplay } from 'app/global-error' import { GET_SNAPSHOT_DETAILS } from 'server/queries/snapshotQueries' -import { ChapterTypeGraphQL } from 'types/chapter' -import { ProjectTypeGraphql } from 'types/project' -import { SnapshotDetailsProps } from 'types/snapshot' +import type { Chapter } from 'types/chapter' +import type { Project } from 'types/project' +import type { SnapshotDetails } from 'types/snapshot' import { level } from 'utils/data' import { formatDate } from 'utils/dateFormatter' -import { getFilteredIconsGraphql, handleSocialUrls } from 'utils/utility' +import { getFilteredIcons, handleSocialUrls } from 'utils/utility' import Card from 'components/Card' import ChapterMapWrapper from 'components/ChapterMapWrapper' import LoadingSpinner from 'components/LoadingSpinner' const SnapshotDetailsPage: React.FC = () => { const { id: snapshotKey } = useParams() - const [snapshot, setSnapshot] = useState(null) + const [snapshot, setSnapshot] = useState(null) const [isLoading, setIsLoading] = useState(true) const router = useRouter() @@ -38,15 +38,15 @@ const SnapshotDetailsPage: React.FC = () => { } }, [graphQLData, graphQLRequestError, snapshotKey]) - const renderProjectCard = (project: ProjectTypeGraphql) => { + const renderProjectCard = (project: Project) => { const params: string[] = ['forksCount', 'starsCount', 'contributorsCount'] - const filteredIcons = getFilteredIconsGraphql(project, params) + const filteredIcons = getFilteredIcons(project, params) const handleButtonClick = () => { router.push(`/projects/${project.key}`) } - const SubmitButton = { + const submitButton = { label: 'View Details', icon: , onclick: handleButtonClick, @@ -54,7 +54,7 @@ const SnapshotDetailsPage: React.FC = () => { return ( { ) } - const renderChapterCard = (chapter: ChapterTypeGraphQL) => { + const renderChapterCard = (chapter: Chapter) => { const params: string[] = ['updatedAt'] - const filteredIcons = getFilteredIconsGraphql(chapter, params) + const filteredIcons = getFilteredIcons(chapter, params) const formattedUrls = handleSocialUrls(chapter.relatedUrls) const handleButtonClick = () => { router.push(`/chapters/${chapter.key}`) } - const SubmitButton = { + const submitButton = { label: 'View Details', icon: , onclick: handleButtonClick, @@ -83,13 +83,13 @@ const SnapshotDetailsPage: React.FC = () => { return ( ) } diff --git a/frontend/src/app/snapshots/page.tsx b/frontend/src/app/snapshots/page.tsx index 1a01dfc8ce..d9d4bfc548 100644 --- a/frontend/src/app/snapshots/page.tsx +++ b/frontend/src/app/snapshots/page.tsx @@ -5,12 +5,12 @@ import { useRouter } from 'next/navigation' import React, { useState, useEffect } from 'react' import FontAwesomeIconWrapper from 'wrappers/FontAwesomeIconWrapper' import { GET_COMMUNITY_SNAPSHOTS } from 'server/queries/snapshotQueries' -import { Snapshots } from 'types/snapshot' +import type { Snapshot } from 'types/snapshot' import LoadingSpinner from 'components/LoadingSpinner' import SnapshotCard from 'components/SnapshotCard' const SnapshotsPage: React.FC = () => { - const [snapshots, setSnapshots] = useState(null) + const [snapshots, setSnapshots] = useState(null) const [isLoading, setIsLoading] = useState(true) const { data: graphQLData, error: graphQLRequestError } = useQuery(GET_COMMUNITY_SNAPSHOTS) @@ -35,11 +35,11 @@ const SnapshotsPage: React.FC = () => { const router = useRouter() - const handleButtonClick = (snapshot: Snapshots) => { + const handleButtonClick = (snapshot: Snapshot) => { router.push(`/snapshots/${snapshot.key}`) } - const renderSnapshotCard = (snapshot: Snapshots) => { + const renderSnapshotCard = (snapshot: Snapshot) => { const SubmitButton = { label: 'View Details', icon: , @@ -68,7 +68,7 @@ const SnapshotsPage: React.FC = () => { {!snapshots?.length ? (
No Snapshots found
) : ( - snapshots.map((snapshot: Snapshots) => ( + snapshots.map((snapshot: Snapshot) => (
{renderSnapshotCard(snapshot)}
)) )} diff --git a/frontend/src/components/Card.tsx b/frontend/src/components/Card.tsx index 6ae9df3c40..09e28da50b 100644 --- a/frontend/src/components/Card.tsx +++ b/frontend/src/components/Card.tsx @@ -2,7 +2,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { Tooltip } from '@heroui/tooltip' import Link from 'next/link' import FontAwesomeIconWrapper from 'wrappers/FontAwesomeIconWrapper' -import { CardProps } from 'types/card' +import type { CardProps } from 'types/card' import { Icons } from 'utils/data' import { getSocialIcon } from 'utils/urlIconMappings' import ActionButton from 'components/ActionButton' diff --git a/frontend/src/components/CardDetailsPage.tsx b/frontend/src/components/CardDetailsPage.tsx index 95fd57f8da..9fb2c1752b 100644 --- a/frontend/src/components/CardDetailsPage.tsx +++ b/frontend/src/components/CardDetailsPage.tsx @@ -9,7 +9,7 @@ import { faRectangleList, } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { DetailsCardProps } from 'types/card' +import type { DetailsCardProps } from 'types/card' import { capitalize } from 'utils/capitalize' import { getSocialIcon } from 'utils/urlIconMappings' import AnchorTitle from 'components/AnchorTitle' @@ -23,11 +23,11 @@ import RecentReleases from 'components/RecentReleases' import RepositoriesCard from 'components/RepositoriesCard' import SecondaryCard from 'components/SecondaryCard' import ToggleableList from 'components/ToggleableList' -import TopContributors from 'components/TopContributors' +import TopContributorsList from 'components/TopContributorsList' const DetailsCard = ({ title, - is_active = true, + isActive = true, summary, description, heatmap, @@ -52,7 +52,7 @@ const DetailsCard = ({

{title}

{description}

- {!is_active && ( + {!isActive && ( Inactive )} {summary && ( @@ -123,7 +123,7 @@ const DetailsCard = ({ {type === 'chapter' && geolocationData && (
)} {topContributors && ( - { const mapRef = useRef(null) const markerClusterRef = useRef(null) - const chapters = useMemo(() => { - return geoLocData.map((chapter) => ({ - lat: '_geoloc' in chapter ? chapter._geoloc.lat : chapter.geoLocation.lat, - lng: '_geoloc' in chapter ? chapter._geoloc.lng : chapter.geoLocation.lng, - key: chapter.key, - name: chapter.name, - })) - }, [geoLocData]) - useEffect(() => { - if (typeof window === 'undefined') return if (!mapRef.current) { mapRef.current = L.map('chapter-map', { worldCopyJump: false, @@ -58,7 +48,7 @@ const ChapterMap = ({ const markerClusterGroup = markerClusterRef.current - const markers = chapters.map((chapter) => { + const markers = geoLocData.map((chapter) => { const markerIcon = new L.Icon({ iconAnchor: [12, 41], iconRetinaUrl: '/img/marker-icon-2x.png', @@ -69,7 +59,13 @@ const ChapterMap = ({ shadowUrl: '/img/marker-shadow.png', }) - const marker = L.marker([chapter.lat, chapter.lng], { icon: markerIcon }) + const marker = L.marker( + [ + chapter._geoloc?.lat || chapter.geoLocation?.lat, + chapter._geoloc?.lng || chapter.geoLocation?.lng, + ], + { icon: markerIcon } + ) const popup = L.popup() const popupContent = document.createElement('div') popupContent.className = 'popup-content' @@ -84,16 +80,27 @@ const ChapterMap = ({ markerClusterGroup.addLayers(markers) - if (showLocal && chapters.length > 0) { + if (showLocal && geoLocData.length > 0) { const maxNearestChapters = 5 - const localChapters = chapters.slice(0, maxNearestChapters - 1) - const localBounds = L.latLngBounds(localChapters.map((ch) => [ch.lat, ch.lng])) + const localChapters = geoLocData.slice(0, maxNearestChapters - 1) + const localBounds = L.latLngBounds( + localChapters.map((chapter) => [ + chapter._geoloc?.lat || chapter.geoLocation?.lat, + chapter._geoloc?.lng || chapter.geoLocation?.lng, + ]) + ) const maxZoom = 7 - const nearestChapter = chapters[0] - map.setView([nearestChapter.lat, nearestChapter.lng], maxZoom) + const nearestChapter = geoLocData[0] + map.setView( + [ + nearestChapter._geoloc?.lat || nearestChapter.geoLocation?.lat, + nearestChapter._geoloc?.lng || nearestChapter.geoLocation?.lng, + ], + maxZoom + ) map.fitBounds(localBounds, { maxZoom: maxZoom }) } - }, [chapters, showLocal]) + }, [geoLocData, showLocal]) return
} diff --git a/frontend/src/components/ChapterMapWrapper.tsx b/frontend/src/components/ChapterMapWrapper.tsx index 0f7a27dfe3..48fb313e4a 100644 --- a/frontend/src/components/ChapterMapWrapper.tsx +++ b/frontend/src/components/ChapterMapWrapper.tsx @@ -1,11 +1,11 @@ import dynamic from 'next/dynamic' import React from 'react' -import { GeoLocDataAlgolia, GeoLocDataGraphQL } from 'types/chapter' +import type { Chapter } from 'types/chapter' const ChapterMap = dynamic(() => import('./ChapterMap'), { ssr: false }) const ChapterMapWrapper = (props: { - geoLocData: GeoLocDataGraphQL[] | GeoLocDataAlgolia[] + geoLocData: Chapter[] showLocal: boolean style: React.CSSProperties }) => { diff --git a/frontend/src/components/ContributorAvatar.tsx b/frontend/src/components/ContributorAvatar.tsx index 16eed05199..ae887eb3e5 100644 --- a/frontend/src/components/ContributorAvatar.tsx +++ b/frontend/src/components/ContributorAvatar.tsx @@ -2,16 +2,14 @@ import { Tooltip } from '@heroui/tooltip' import Image from 'next/image' import Link from 'next/link' import { memo } from 'react' -import { TopContributorsTypeAlgolia, TopContributorsTypeGraphql } from 'types/contributor' +import type { Contributor } from 'types/contributor' type ContributorProps = { - contributor: TopContributorsTypeAlgolia | TopContributorsTypeGraphql + contributor: Contributor uniqueKey: string } -const isAlgoliaContributor = ( - contributor: TopContributorsTypeAlgolia | TopContributorsTypeGraphql -): contributor is TopContributorsTypeAlgolia => { +const isAlgoliaContributor = (contributor: Contributor): contributor is Contributor => { return ( typeof contributor === 'object' && contributor !== null && @@ -23,20 +21,18 @@ const isAlgoliaContributor = ( const ContributorAvatar = memo(({ contributor, uniqueKey }: ContributorProps) => { const isAlgolia = isAlgoliaContributor(contributor) - const avatarUrl = isAlgolia - ? contributor.avatar_url - : (contributor as TopContributorsTypeGraphql).avatarUrl + const avatarUrl = isAlgolia ? contributor.avatarUrl : (contributor as Contributor).avatarUrl const contributionsCount = isAlgolia - ? contributor.contributions_count - : (contributor as TopContributorsTypeGraphql).contributionsCount + ? contributor.contributionsCount + : (contributor as Contributor).contributionsCount const { login, name } = contributor const displayName = name || login const repositoryInfo = - !isAlgolia && (contributor as TopContributorsTypeGraphql).projectName - ? ` in ${(contributor as TopContributorsTypeGraphql).projectName}` + !isAlgolia && (contributor as Contributor).projectName + ? ` in ${(contributor as Contributor).projectName}` : '' return ( diff --git a/frontend/src/components/DisplayIcon.tsx b/frontend/src/components/DisplayIcon.tsx index d01597ba4b..bb54cfbef1 100644 --- a/frontend/src/components/DisplayIcon.tsx +++ b/frontend/src/components/DisplayIcon.tsx @@ -1,10 +1,10 @@ import { Tooltip } from '@heroui/tooltip' import { millify } from 'millify' import FontAwesomeIconWrapper from 'wrappers/FontAwesomeIconWrapper' -import { IconType } from 'types/icon' +import type { Icon } from 'types/icon' import { IconKeys, Icons } from 'utils/data' -export default function DisplayIcon({ item, icons }: { item: string; icons: IconType }) { +export default function DisplayIcon({ item, icons }: { item: string; icons: Icon }) { // className for the container const containerClassName = [ 'flex flex-row-reverse items-center justify-center gap-1 px-4 pb-1 -ml-2', diff --git a/frontend/src/components/Footer.tsx b/frontend/src/components/Footer.tsx index 65b77b26b2..acb61e71f7 100644 --- a/frontend/src/components/Footer.tsx +++ b/frontend/src/components/Footer.tsx @@ -4,7 +4,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { Button } from '@heroui/button' import Link from 'next/link' import { useState, useCallback } from 'react' -import { Section } from 'types/section' +import type { Section } from 'types/section' import { footerIcons } from 'utils/constants' import { footerSections } from 'utils/constants' diff --git a/frontend/src/components/InfoBlock.tsx b/frontend/src/components/InfoBlock.tsx index 95d853ef96..477ed4f352 100644 --- a/frontend/src/components/InfoBlock.tsx +++ b/frontend/src/components/InfoBlock.tsx @@ -1,4 +1,4 @@ -import { IconProp } from '@fortawesome/fontawesome-svg-core' +import type { IconProp } from '@fortawesome/fontawesome-svg-core' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import millify from 'millify' import { pluralize } from 'utils/pluralize' diff --git a/frontend/src/components/ItemCardList.tsx b/frontend/src/components/ItemCardList.tsx index d29c4a6f48..1efbe17e90 100644 --- a/frontend/src/components/ItemCardList.tsx +++ b/frontend/src/components/ItemCardList.tsx @@ -3,8 +3,10 @@ import { Tooltip } from '@heroui/tooltip' import Image from 'next/image' import Link from 'next/link' import React, { JSX } from 'react' -import { ProjectIssuesType, ProjectReleaseType, ProjectMilestonesType } from 'types/project' -import { PullRequestsType } from 'types/user' +import type { Issue } from 'types/issue' +import type { Milestone } from 'types/milestone' +import type { PullRequest } from 'types/pullRequest' +import type { Release } from 'types/release' import SecondaryCard from 'components/SecondaryCard' import { TruncatedText } from 'components/TruncatedText' @@ -17,7 +19,7 @@ const ItemCardList = ({ showSingleColumn = true, }: { title: React.ReactNode - data: ProjectReleaseType[] | ProjectIssuesType[] | PullRequestsType[] | ProjectMilestonesType[] + data: Issue[] | Milestone[] | PullRequest[] | Release[] icon?: IconProp showAvatar?: boolean showSingleColumn?: boolean diff --git a/frontend/src/components/LeadersList.tsx b/frontend/src/components/LeadersList.tsx index 8cfff26a49..d79b3ae623 100644 --- a/frontend/src/components/LeadersList.tsx +++ b/frontend/src/components/LeadersList.tsx @@ -1,5 +1,5 @@ import Link from 'next/link' -import { LeadersListProps } from 'types/leaders' +import type { LeadersListProps } from 'types/leaders' import { TruncatedText } from 'components/TruncatedText' /** diff --git a/frontend/src/components/LogoCarousel.tsx b/frontend/src/components/LogoCarousel.tsx index 3640d970aa..2376622cc6 100644 --- a/frontend/src/components/LogoCarousel.tsx +++ b/frontend/src/components/LogoCarousel.tsx @@ -1,10 +1,10 @@ import Image from 'next/image' import Link from 'next/link' import { useEffect, useRef } from 'react' -import { SponsorType } from 'types/home' +import type { Sponsor } from 'types/home' interface MovingLogosProps { - sponsors: SponsorType[] + sponsors: Sponsor[] } export default function MovingLogos({ sponsors }: MovingLogosProps) { diff --git a/frontend/src/components/Milestones.tsx b/frontend/src/components/Milestones.tsx index 1457e31ad6..999af992ce 100644 --- a/frontend/src/components/Milestones.tsx +++ b/frontend/src/components/Milestones.tsx @@ -8,14 +8,14 @@ import { import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { useRouter } from 'next/navigation' import React from 'react' -import { ProjectMilestonesType } from 'types/project' +import type { Milestone } from 'types/milestone' import { formatDate } from 'utils/dateFormatter' import AnchorTitle from 'components/AnchorTitle' import ItemCardList from 'components/ItemCardList' import { TruncatedText } from 'components/TruncatedText' interface ProjectMilestonesProps { - data: ProjectMilestonesType[] + data: Milestone[] showAvatar?: boolean showSingleColumn?: boolean } diff --git a/frontend/src/components/Modal.tsx b/frontend/src/components/Modal.tsx index d7324bca19..9e401999d9 100644 --- a/frontend/src/components/Modal.tsx +++ b/frontend/src/components/Modal.tsx @@ -3,7 +3,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { Button } from '@heroui/button' import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter } from '@heroui/modal' import React from 'react' -import { ModalProps } from 'types/modal' +import type { ModalProps } from 'types/modal' import ActionButton from 'components/ActionButton' import Markdown from 'components/MarkdownWrapper' diff --git a/frontend/src/components/MultiSearch.tsx b/frontend/src/components/MultiSearch.tsx index 27de1f6bf4..bd76a392e3 100644 --- a/frontend/src/components/MultiSearch.tsx +++ b/frontend/src/components/MultiSearch.tsx @@ -14,12 +14,12 @@ import { useRouter } from 'next/navigation' import type React from 'react' import { useState, useEffect, useMemo, useCallback, useRef } from 'react' import { fetchAlgoliaData } from 'server/fetchAlgoliaData' -import { ChapterTypeAlgolia } from 'types/chapter' -import { EventType } from 'types/event' -import { OrganizationTypeAlgolia } from 'types/organization' -import { ProjectTypeAlgolia } from 'types/project' -import { MultiSearchBarProps, Suggestion } from 'types/search' -import { User } from 'types/user' +import type { Chapter } from 'types/chapter' +import type { Event } from 'types/event' +import type { Organization } from 'types/organization' +import type { Project } from 'types/project' +import type { MultiSearchBarProps, Suggestion } from 'types/search' +import type { User } from 'types/user' const MultiSearchBar: React.FC = ({ isLoaded, @@ -57,12 +57,7 @@ const MultiSearchBar: React.FC = ({ const data = await fetchAlgoliaData(index, query, pageCount, suggestionCount) return { indexName: index, - hits: data.hits as - | ChapterTypeAlgolia[] - | EventType[] - | OrganizationTypeAlgolia[] - | ProjectTypeAlgolia[] - | User[], + hits: data.hits as Chapter[] | Event[] | Organization[] | Project[] | User[], totalPages: data.totalPages || 0, } }) @@ -74,11 +69,11 @@ const MultiSearchBar: React.FC = ({ if (filteredEvents.length > 0) { results.push({ indexName: 'events', - hits: filteredEvents.slice(0, suggestionCount), + hits: filteredEvents.slice(0, suggestionCount) as Event[], totalPages: 1, }) } - setSuggestions(results.filter((result) => result.hits.length > 0)) + setSuggestions(results.filter((result) => result.hits.length > 0) as Suggestion[]) setShowSuggestions(true) } else { setSuggestions([]) @@ -95,15 +90,7 @@ const MultiSearchBar: React.FC = ({ }, [debouncedSearch]) const handleSuggestionClick = useCallback( - ( - suggestion: - | ChapterTypeAlgolia - | ProjectTypeAlgolia - | User - | EventType - | OrganizationTypeAlgolia, - indexName: string - ) => { + (suggestion: Chapter | Project | User | Event | Organization, indexName: string) => { setSearchQuery(suggestion.name ?? '') setShowSuggestions(false) @@ -112,7 +99,7 @@ const MultiSearchBar: React.FC = ({ router.push(`/chapters/${suggestion.key}`) break case 'events': - window.open((suggestion as EventType).url, '_blank') + window.open((suggestion as Event).url, '_blank') break case 'organizations': // Use type guard to safely access login property @@ -140,12 +127,7 @@ const MultiSearchBar: React.FC = ({ const { index, subIndex } = highlightedIndex const suggestion = suggestions[index].hits[subIndex] handleSuggestionClick( - suggestion as - | ChapterTypeAlgolia - | OrganizationTypeAlgolia - | ProjectTypeAlgolia - | User - | EventType, + suggestion as Chapter | Organization | Project | User | Event, suggestions[index].indexName ) } else if (event.key === 'ArrowDown') { diff --git a/frontend/src/components/NavButton.tsx b/frontend/src/components/NavButton.tsx index 97ffb06181..f328a734f7 100644 --- a/frontend/src/components/NavButton.tsx +++ b/frontend/src/components/NavButton.tsx @@ -1,7 +1,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import Link from 'next/link' import { useState } from 'react' -import { NavButtonProps } from 'types/button' +import type { NavButtonProps } from 'types/button' import { cn } from 'utils/utility' const NavButton = ({ diff --git a/frontend/src/components/NavDropDown.tsx b/frontend/src/components/NavDropDown.tsx index a14fc34feb..b18504cfc5 100644 --- a/frontend/src/components/NavDropDown.tsx +++ b/frontend/src/components/NavDropDown.tsx @@ -2,7 +2,7 @@ import { faChevronDown } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import Link from 'next/link' import { useState, useRef, useEffect, useId } from 'react' -import { Link as LinkType } from 'types/link' +import type { Link as LinkType } from 'types/link' import { cn } from 'utils/utility' interface NavDropDownProps { diff --git a/frontend/src/components/RecentIssues.tsx b/frontend/src/components/RecentIssues.tsx index a5a16df6bf..e19064d56b 100644 --- a/frontend/src/components/RecentIssues.tsx +++ b/frontend/src/components/RecentIssues.tsx @@ -2,14 +2,14 @@ import { faCalendar, faFolderOpen, faCircleExclamation } from '@fortawesome/free import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { useRouter } from 'next/navigation' import React from 'react' -import { ProjectIssuesType } from 'types/project' +import type { Issue } from 'types/issue' import { formatDate } from 'utils/dateFormatter' import AnchorTitle from 'components/AnchorTitle' import ItemCardList from 'components/ItemCardList' import { TruncatedText } from 'components/TruncatedText' interface RecentIssuesProps { - data: ProjectIssuesType[] + data: Issue[] showAvatar?: boolean } diff --git a/frontend/src/components/RecentPullRequests.tsx b/frontend/src/components/RecentPullRequests.tsx index e5ddcc979c..7684180dd1 100644 --- a/frontend/src/components/RecentPullRequests.tsx +++ b/frontend/src/components/RecentPullRequests.tsx @@ -2,15 +2,14 @@ import { faCalendar, faCodePullRequest, faFolderOpen } from '@fortawesome/free-s import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { useRouter } from 'next/navigation' import React from 'react' -import { PullRequestsType } from 'types/home' -import { ItemCardPullRequests } from 'types/user' +import type { PullRequest } from 'types/pullRequest' import { formatDate } from 'utils/dateFormatter' import AnchorTitle from 'components/AnchorTitle' import ItemCardList from 'components/ItemCardList' import { TruncatedText } from 'components/TruncatedText' interface RecentPullRequestsProps { - data: ItemCardPullRequests[] | PullRequestsType[] + data: PullRequest[] showAvatar?: boolean } diff --git a/frontend/src/components/RecentReleases.tsx b/frontend/src/components/RecentReleases.tsx index 35630fdb33..e97280838f 100644 --- a/frontend/src/components/RecentReleases.tsx +++ b/frontend/src/components/RecentReleases.tsx @@ -5,14 +5,14 @@ import Image from 'next/image' import Link from 'next/link' import { useRouter } from 'next/navigation' import React from 'react' -import { ProjectReleaseType } from 'types/project' +import type { Release } from 'types/release' import { formatDate } from 'utils/dateFormatter' import AnchorTitle from 'components/AnchorTitle' import SecondaryCard from 'components/SecondaryCard' import { TruncatedText } from 'components/TruncatedText' interface RecentReleasesProps { - data: ProjectReleaseType[] + data: Release[] showAvatar?: boolean showSingleColumn?: boolean } diff --git a/frontend/src/components/RepositoriesCard.tsx b/frontend/src/components/RepositoriesCard.tsx index 88e15f12f7..eeb712828d 100644 --- a/frontend/src/components/RepositoriesCard.tsx +++ b/frontend/src/components/RepositoriesCard.tsx @@ -10,7 +10,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { useRouter } from 'next/navigation' import type React from 'react' import { useState } from 'react' -import { RepositoriesCardProps, RepositoryCardProps } from 'types/project' +import type { RepositoriesCardProps, RepositoryCardProps } from 'types/project' import InfoItem from 'components/InfoItem' import { TruncatedText } from 'components/TruncatedText' diff --git a/frontend/src/components/SnapshotCard.tsx b/frontend/src/components/SnapshotCard.tsx index 78da8c999d..e80cb65255 100644 --- a/frontend/src/components/SnapshotCard.tsx +++ b/frontend/src/components/SnapshotCard.tsx @@ -1,7 +1,7 @@ import { faChevronRight, faCalendar } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { Button } from '@heroui/button' -import { SnapshotCardProps } from 'types/card' +import type { SnapshotCardProps } from 'types/card' import { formatDate } from 'utils/dateFormatter' const SnapshotCard = ({ title, button, startAt, endAt }: SnapshotCardProps) => { diff --git a/frontend/src/components/SortBy.tsx b/frontend/src/components/SortBy.tsx index eabebc262a..f8f7766678 100644 --- a/frontend/src/components/SortBy.tsx +++ b/frontend/src/components/SortBy.tsx @@ -2,7 +2,7 @@ import { faArrowDownWideShort, faArrowUpWideShort } from '@fortawesome/free-soli import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { Select, SelectItem } from '@heroui/select' import { Tooltip } from '@heroui/tooltip' -import { SortByProps } from 'types/sortBy' +import type { SortByProps } from 'types/sortBy' const SortBy = ({ sortOptions, diff --git a/frontend/src/components/TopContributors.tsx b/frontend/src/components/TopContributorsList.tsx similarity index 95% rename from frontend/src/components/TopContributors.tsx rename to frontend/src/components/TopContributorsList.tsx index 08c21a5563..218cbd6a36 100644 --- a/frontend/src/components/TopContributors.tsx +++ b/frontend/src/components/TopContributorsList.tsx @@ -5,21 +5,21 @@ import { Button } from '@heroui/button' import Image from 'next/image' import Link from 'next/link' import { useState } from 'react' -import { TopContributorsTypeGraphql } from 'types/contributor' +import type { Contributor } from 'types/contributor' import { capitalize } from 'utils/capitalize' import { pluralize } from 'utils/pluralize' import { getMemberUrl, getProjectUrl } from 'utils/urlFormatter' import AnchorTitle from 'components/AnchorTitle' import SecondaryCard from 'components/SecondaryCard' -const TopContributors = ({ +const TopContributorsList = ({ contributors, label = 'Top Contributors', maxInitialDisplay = 6, type, icon, }: { - contributors: TopContributorsTypeGraphql[] + contributors: Contributor[] label?: string maxInitialDisplay?: number type: string @@ -109,4 +109,4 @@ const TopContributors = ({ ) } -export default TopContributors +export default TopContributorsList diff --git a/frontend/src/components/UserCard.tsx b/frontend/src/components/UserCard.tsx index 92705aed42..26681afdd0 100644 --- a/frontend/src/components/UserCard.tsx +++ b/frontend/src/components/UserCard.tsx @@ -3,7 +3,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { Button } from '@heroui/button' import millify from 'millify' import Image from 'next/image' -import { UserCardProps } from 'types/card' +import type { UserCardProps } from 'types/card' const UserCard = ({ avatar, @@ -12,10 +12,10 @@ const UserCard = ({ company, description, email, - followers_count, + followersCount, location, name, - repositories_count, + repositoriesCount, }: UserCardProps) => { return (