Skip to content

Commit

Permalink
Merge branch 'main' into 12ka4
Browse files Browse the repository at this point in the history
  • Loading branch information
bhagyashree980 authored Jan 24, 2025
2 parents 23b03ef + 2f389e4 commit 846ad28
Show file tree
Hide file tree
Showing 11 changed files with 1,127 additions and 1,050 deletions.
1 change: 1 addition & 0 deletions .github/workflows/pr-labeler.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ jobs:
- uses: actions/labeler@v5
with:
configuration-path: .github/labeler.yml
sync-labels: true
197 changes: 146 additions & 51 deletions backend/poetry.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ slack-bolt = "^1.22.0"
djlint = "^1.36.4"
isort = "^5.13.2"
pre-commit = "^4.1.0"
ruff = "^0.9.2"
ruff = "^0.9.3"

[tool.poetry.group.test.dependencies]
pytest = "^8.3.4"
Expand Down
1,398 changes: 715 additions & 683 deletions frontend/package-lock.json

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions frontend/src/components/InfoBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'

const InfoBlock = ({ icon, label = '', value, isLink = false, className = '' }) => (
<div className={`flex ${className}`}>
<FontAwesomeIcon icon={icon} className="mr-3 mt-1 w-5" />
<div>
<div className="text-sm md:text-base">
{label && <div className="text-sm font-medium">{label}</div>}
{isLink ? (
<a href={value} className="hover:underline dark:text-sky-600">
{value}
</a>
) : (
value
)}
</div>
</div>
</div>
)

export default InfoBlock
8 changes: 8 additions & 0 deletions frontend/src/components/SecondaryCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const SecondaryCard = ({ title, children, className = '' }) => (
<div className={`mb-8 rounded-lg bg-gray-100 p-6 shadow-md dark:bg-gray-800 ${className}`}>
<h2 className="mb-4 text-2xl font-semibold">{title}</h2>
{children}
</div>
)

export default SecondaryCard
66 changes: 66 additions & 0 deletions frontend/src/components/ToggleContributors.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { useState } from 'react'
import { useNavigate } from 'react-router-dom'
const TopContributors = ({
contributors,
label = 'Top Contributors',
maxInitialDisplay = 6,
className = '',
}) => {
const navigate = useNavigate()
const [showAllContributors, setShowAllContributors] = useState(false)

const toggleContributors = () => setShowAllContributors(!showAllContributors)

const displayContributors = showAllContributors
? contributors
: contributors.slice(0, maxInitialDisplay)

return (
<div className={`mb-8 rounded-lg bg-gray-100 p-6 shadow-md dark:bg-gray-800 ${className}`}>
<h2 className="mb-4 text-2xl font-semibold">{label}</h2>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3">
{displayContributors.map((contributor, index) => (
<div
key={index}
onClick={() => navigate(`/community/users/${contributor.login}`)}
className="flex cursor-pointer items-center space-x-3 rounded-lg p-3 hover:bg-gray-200 dark:hover:bg-gray-700"
>
<img
src={contributor.avatar_url}
alt={contributor.name || contributor.login}
className="mr-3 h-10 w-10 rounded-full"
/>
<div>
<p className="font-semibold text-blue-600 hover:underline dark:text-sky-400">
{contributor.name || contributor.login}
</p>
<p className="text-sm text-gray-600 dark:text-gray-400">
{contributor.contributions_count} contributions
</p>
</div>
</div>
))}
</div>
{contributors.length > maxInitialDisplay && (
<button
onClick={toggleContributors}
className="mt-4 flex items-center text-[#1d7bd7] hover:underline dark:text-sky-600"
>
{showAllContributors ? (
<>
Show less <FontAwesomeIcon icon={faChevronUp} className="ml-1" />
</>
) : (
<>
Show more <FontAwesomeIcon icon={faChevronDown} className="ml-1" />
</>
)}
</button>
)}
</div>
)
}

export default TopContributors
43 changes: 43 additions & 0 deletions frontend/src/components/ToogleList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { useState } from 'react'

const ToggleableList = ({ items, label, limit = 10 }) => {
const [showAll, setShowAll] = useState(false)

const toggleShowAll = () => setShowAll(!showAll)

return (
<div className="rounded-lg bg-gray-100 p-6 shadow-md dark:bg-gray-800">
<h2 className="mb-4 text-2xl font-semibold">{label}</h2>
<div className="flex flex-wrap gap-2">
{(showAll ? items : items.slice(0, limit)).map((item, index) => (
<span
key={index}
className="rounded-lg border border-gray-400 px-2 py-1 text-sm dark:border-gray-300"
>
{item}
</span>
))}
</div>
{items.length > limit && (
<button
onClick={toggleShowAll}
className="mt-4 flex items-center text-[#1d7bd7] hover:underline dark:text-sky-600"
>
{showAll ? (
<>
Show less <FontAwesomeIcon icon={faChevronUp} className="ml-1" />
</>
) : (
<>
Show more <FontAwesomeIcon icon={faChevronDown} className="ml-1" />
</>
)}
</button>
)}
</div>
)
}

export default ToggleableList
207 changes: 58 additions & 149 deletions frontend/src/pages/ChapterDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,62 +1,38 @@
import {
faDiscord,
faInstagram,
faLinkedin,
faYoutube,
faMeetup,
faXTwitter,
} from '@fortawesome/free-brands-svg-icons'
import {
faGlobe,
faCalendarAlt,
faMapMarkerAlt,
faChevronDown,
faChevronUp,
faLink,
faTags,
faCalendarAlt,
faLink,
faGlobe,
} from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { fetchAlgoliaData } from 'api/fetchAlgoliaData'
import { useEffect, useState } from 'react'
import { useState, useEffect } from 'react'
import { useParams } from 'react-router-dom'
import { formatDate } from 'utils/dateFormatter'
import { getSocialIcon } from 'utils/urlIconMappings'
import { ErrorDisplay } from 'wrappers/ErrorWrapper'
import InfoBlock from 'components/InfoBlock'
import LoadingSpinner from 'components/LoadingSpinner'

const getSocialIcon = (url: string) => {
if (!/^https?:\/\//i.test(url)) {
url = 'http://' + url
}

const hostname = new URL(url).hostname.toLowerCase()

if (hostname === 'discord.com' || hostname.endsWith('.discord.com')) return faDiscord
if (hostname === 'instagram.com' || hostname.endsWith('.instagram.com')) return faInstagram
if (hostname === 'linkedin.com' || hostname.endsWith('.linkedin.com')) return faLinkedin
if (hostname === 'youtube.com' || hostname.endsWith('.youtube.com')) return faYoutube
if (hostname === 'twitter.com' || hostname === 'x.com' || hostname.endsWith('.x.com'))
return faXTwitter
if (hostname === 'meetup.com' || hostname.endsWith('.meetup.com')) return faMeetup
return faGlobe
}
import SecondaryCard from 'components/SecondaryCard'
import TopContributors from 'components/ToggleContributors'

export default function ChapterDetailsPage() {
const { chapterKey } = useParams()
const [chapter, setchapter] = useState(null)
const [chapter, setChapter] = useState(null)
const [isLoading, setIsLoading] = useState(true)
const [showAllContributors, setShowAllContributors] = useState(false)

useEffect(() => {
const fetchchapterData = async () => {
const fetchChapterData = async () => {
setIsLoading(true)
const { hits } = await fetchAlgoliaData('chapters', chapterKey, 1, chapterKey)
if (hits && hits.length > 0) {
setchapter(hits[0])
setChapter(hits[0])
}
setIsLoading(false)
}

fetchchapterData()
fetchChapterData()
}, [chapterKey])

if (isLoading)
Expand All @@ -81,122 +57,55 @@ export default function ChapterDetailsPage() {
<h1 className="mb-6 text-3xl font-bold md:text-4xl">{chapter.name}</h1>

<div className="grid gap-6 md:grid-cols-2">
<div className="rounded-lg bg-gray-100 p-4 shadow-lg dark:bg-gray-800 md:p-6">
<h2 className="mb-4 text-xl font-semibold md:text-2xl">Chapter Details</h2>
<div className="space-y-3">
<div className="flex items-start">
<FontAwesomeIcon icon={faMapMarkerAlt} className="mr-3 mt-1 w-5" />
<div>
<div className="text-sm font-medium">Location</div>
<div className="text-sm md:text-base">{chapter.suggested_location}</div>
</div>
</div>

<div className="flex items-start">
<FontAwesomeIcon icon={faGlobe} className="mr-3 mt-1 w-5" />
<div>
<div className="text-sm font-medium">Region</div>
<div className="text-sm md:text-base">{chapter.region}</div>
</div>
</div>

<div className="flex items-start">
<FontAwesomeIcon icon={faTags} className="mr-3 mt-1 w-5" />
<div>
<div className="text-sm font-medium">Tags</div>
<div className="text-sm md:text-base">
{chapter.tags[0].toUpperCase() + chapter.tags.slice(1)}
</div>
</div>
</div>

<div className="flex items-start">
<FontAwesomeIcon icon={faCalendarAlt} className="mr-3 mt-1 w-5" />
<div>
<div className="text-sm font-medium">Last Updated</div>
<div className="text-sm md:text-base">{formatDate(chapter.updated_at)}</div>
</div>
</div>

<div className="flex items-start">
<FontAwesomeIcon icon={faLink} className="mr-3 mt-1 w-5" />
<div>
<div className="text-sm font-medium">URL</div>
<a href={chapter.url} className="hover:underline dark:text-sky-600">
{chapter.url}
</a>
</div>
</div>

<div>
<div className="text-sm font-medium">Social Links</div>
<div className="mt-2 flex flex-wrap gap-3">
{chapter.related_urls.map((url, index) => (
<a
key={index}
href={url}
target="_blank"
rel="noopener noreferrer"
className="text-gray-600 transition-colors hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-200"
>
<FontAwesomeIcon icon={getSocialIcon(url)} className="h-5 w-5" />
</a>
))}
</div>
</div>
</div>
</div>

<div className="rounded-lg bg-gray-100 p-4 shadow-md dark:bg-gray-800 md:p-6">
<h2 className="mb-4 text-xl font-semibold md:text-2xl">Summary</h2>
<p className="text-sm leading-relaxed md:text-base">{chapter.summary}</p>
</div>
<SecondaryCard title="Chapter Details">
<InfoBlock
className="pb-1"
icon={faMapMarkerAlt}
label="Location"
value={chapter.suggested_location}
/>
<InfoBlock className="pb-1" icon={faGlobe} label="Region" value={chapter.region} />
<InfoBlock
className="pb-1"
icon={faTags}
label="Tags"
value={chapter.tags[0].toUpperCase() + chapter.tags.slice(1)}
/>
<InfoBlock
className="pb-1"
icon={faCalendarAlt}
label="Last Updated"
value={formatDate(chapter.updated_at)}
/>
<InfoBlock className="pb-1" icon={faLink} label="URL" value={chapter.url} isLink />
<SocialLinks urls={chapter.related_urls} />
</SecondaryCard>
<SecondaryCard title="Summary">
<div className="text-sm leading-relaxed md:text-base">{chapter.summary}</div>
</SecondaryCard>
</div>

<div className="mt-6 rounded-lg bg-gray-100 p-4 shadow-md dark:bg-gray-800 md:p-6">
<h2 className="mb-4 text-xl font-semibold md:text-2xl">Top Contributors</h2>
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{(showAllContributors
? chapter.top_contributors
: chapter.top_contributors.slice(0, 6)
).map((contributor, index) => (
<a
key={index}
href={`https://github.com/${contributor.login}`}
target="_blank"
rel="noopener noreferrer"
className="flex items-center space-x-3 rounded-lg p-3 hover:bg-gray-200 dark:hover:bg-gray-700"
>
<img
src={contributor.avatar_url}
alt={contributor.name || contributor.login}
className="mr-3 h-10 w-10 rounded-full"
/>
<div>
<p className="font-semibold text-blue-600 hover:underline dark:text-sky-400">
{contributor.name || contributor.login}
</p>
<p className="text-sm text-gray-600 dark:text-gray-400">
{contributor.contributions_count} contributions
</p>
</div>
</a>
))}
</div>
{chapter.top_contributors.length > 6 && (
<button
onClick={() => setShowAllContributors(!showAllContributors)}
className="mt-4 flex items-center text-[#1d7bd7] hover:underline dark:text-sky-600"
>
{showAllContributors ? 'Show Less' : 'Show All'}
<FontAwesomeIcon
icon={showAllContributors ? faChevronUp : faChevronDown}
className="ml-2"
/>
</button>
)}
</div>
<TopContributors contributors={chapter.top_contributors} maxInitialDisplay={6} />
</div>
</div>
)
}

const SocialLinks = ({ urls }) => (
<div>
<div className="text-sm font-medium">Social Links</div>
<div className="mt-2 flex flex-wrap gap-3">
{urls.map((url, index) => (
<a
key={index}
href={url}
target="_blank"
rel="noopener noreferrer"
className="text-gray-600 transition-colors hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-200"
>
<FontAwesomeIcon icon={getSocialIcon(url)} className="h-5 w-5" />
</a>
))}
</div>
</div>
)
Loading

0 comments on commit 846ad28

Please sign in to comment.