Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 68 additions & 82 deletions frontend/src/components/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import DisplayIcon from 'components/DisplayIcon'
import Markdown from 'components/MarkdownWrapper'
import { Tooltip } from 'components/ui/tooltip'

// Initial check for mobile screen size
const isMobileInitial = typeof window !== 'undefined' && window.innerWidth < desktopViewMinWidth

const Card = ({
Expand All @@ -32,21 +31,16 @@ const Card = ({
}: CardProps) => {
const [isMobile, setIsMobile] = useState(isMobileInitial)

// Resize listener to adjust display based on screen width
useEffect(() => {
const checkMobile = () => {
const mobile = window.innerWidth < desktopViewMinWidth
setIsMobile(mobile)
}
const checkMobile = () => setIsMobile(window.innerWidth < desktopViewMinWidth)
window.addEventListener('resize', checkMobile)
return () => window.removeEventListener('resize', checkMobile)
}, [])

return (
<div className="mx-auto mb-2 mt-4 flex w-full max-w-[95%] flex-col items-start rounded-md border border-border bg-white px-4 pb-4 pl-4 dark:bg-[#212529] md:max-w-6xl">
<div className="mt-2 flex w-full flex-col items-start gap-4 pt-2 sm:flex-col sm:gap-4 md:pt-0">
<div className="mx-auto mb-2 mt-4 w-full max-w-[95%] rounded-md border border-border bg-white px-4 pb-4 dark:bg-[#212529] md:max-w-6xl">
<div className="mt-2 flex w-full flex-col gap-4 pt-2 sm:flex-col md:pt-0">
<div className="flex items-center gap-3">
{/* Display project level badge (if available) */}
{level && (
<Tooltip
closeDelay={100}
Expand All @@ -58,112 +52,105 @@ const Card = ({
showArrow
>
<span
className={cn(
'flex h-8 w-8 items-center justify-center rounded-full text-xs shadow'
)}
className="flex h-8 w-8 items-center justify-center rounded-full shadow"
style={{ backgroundColor: level.color }}
>
<FontAwesomeIconWrapper icon={level.icon} className="text-white" />
</span>
</Tooltip>
)}
{/* Project title and link */}

<Link href={url} target="_blank" rel="noopener noreferrer" className="flex-1">
<h1
className="max-w-full break-words text-base font-semibold dark:text-sky-600 sm:break-normal sm:text-lg lg:text-2xl"
style={{
transition: 'color 0.3s ease',
}}
>
<h1 className="break-words text-base font-semibold dark:text-sky-600 sm:text-lg lg:text-2xl transition-colors hover:text-blue-500">
{title}
</h1>
</Link>
</div>
{/* Icons associated with the project */}
{icons && Object.keys(Icons).some((key) => icons[key]) ? (
<div className="-ml-1.5 flex flex-grow">
{Object.keys(Icons).map((key, index) =>
icons[key] ? (
<DisplayIcon
key={`${key}-${index}`}
item={key}
icons={Object.fromEntries(Object.entries(icons).filter(([_, value]) => value))}
/>
) : null

{icons && Object.keys(Icons).some((key) => icons[key]) && (
<div className="flex flex-wrap items-center gap-2">
{Object.keys(Icons).map(
(key, index) =>
icons[key] && (
<DisplayIcon
key={`${key}-${index}`}
item={key}
icons={Object.fromEntries(Object.entries(icons).filter(([_, value]) => value))}
/>
)
)}
</div>
) : null}
)}
</div>
{/* Link to project name if provided */}

{projectName && (
<Link href={projectLink} rel="noopener noreferrer" className="mt-2 font-medium">
<Link href={projectLink} rel="noopener noreferrer" className="mt-2 font-medium text-blue-600 hover:underline">
{projectName}
</Link>
)}
{/* Render project summary using Markdown */}

<Markdown content={summary} className="py-2 pr-4 text-gray-600 dark:text-gray-300" />
<div
className={
social && social.length > 0
? 'flex w-full flex-col gap-2 pr-4'
: 'flex w-full items-center justify-between'
}
>
{/* Render top contributors as avatars */}
<div className="mt-3 flex w-full flex-wrap items-center gap-2">
{topContributors?.map((contributor, index) => (
<ContributorAvatar
key={contributor.login || `contributor-${index}`}
contributor={contributor}
uniqueKey={index.toString()}
/>
))}
</div>

<div className={cn(social?.length ? 'flex w-full flex-col gap-2 pr-4' : 'flex w-full items-center justify-between')}>
{topContributors && topContributors.length > 0 && (
<div className="mt-3 flex w-full flex-wrap items-center gap-2">
{topContributors.map((contributor, index) => (
<ContributorAvatar
key={contributor.login || `contributor-${index}`}
contributor={contributor}
uniqueKey={index.toString()}
/>
))}
</div>
)}

{!social || social.length === 0 ? (
<div
className={cn(
'mt-3 flex items-center pr-4',
isMobile && 'mt-4 w-full justify-end pr-4'
)}
>
<div className={cn('mt-3 flex items-center pr-4', isMobile && 'mt-4 w-full justify-end')}>
<ActionButton tooltipLabel={tooltipLabel} url={button.url} onClick={button.onclick}>
{button.icon}
{button.label}
<FontAwesomeIconWrapper icon={button.icon} /> {button.label}
</ActionButton>
</div>
) : (
<div
className={cn(
'flex w-full flex-wrap items-center justify-between gap-6',
isMobile && 'items-start'
)}
>
<div
className={cn('flex w-full items-center justify-between', isMobile && 'flex-wrap')}
>
{/* Render social links if available */}
<div className={cn('flex w-full flex-wrap items-center justify-between gap-6', isMobile && 'items-start')}>
<div className={cn('flex w-full items-center justify-between', isMobile && 'flex-wrap')}>
{social && social.length > 0 && (
<HStack id="social" mt={2}>
{social.map((item) => (
<Link
<Tooltip
key={`${item.title}-${item.url}`}
href={item.url}
target="_blank"
rel="noopener noreferrer"
display="flex"
alignItems="center"
gap={2}
content={item.title}
recipe={TooltipRecipe}
openDelay={150}
closeDelay={100}
showArrow
positioning={{ placement: 'top' }}
>
<FontAwesomeIcon icon={getSocialIcon(item.url)} className="h-5 w-5" />
</Link>
<Link
href={item.url}
target="_blank"
rel="noopener noreferrer"
className="transition-transform duration-300 hover:scale-110"
>
<FontAwesomeIcon
icon={getSocialIcon(item.url)}
className={`h-5 w-5 transition-colors duration-300 ${
{
discord: 'text-[#5865F2] hover:text-[#5865F2]',
instagram: 'text-[#E4405F] hover:text-[#E4405F]',
linkedin: 'text-[#0077B5] hover:text-[#0077B5]',
youtube: 'text-[#FF0000] hover:text-[#FF0000]',
}[item.title.toLowerCase()] || 'text-gray-500'
}`}
/>
</Link>
</Tooltip>
))}
</HStack>
)}
{/* Action Button */}

<div className="flex items-center">
<ActionButton tooltipLabel={tooltipLabel} url={button.url} onClick={button.onclick}>
{button.icon}
{button.label}
<FontAwesomeIconWrapper icon={button.icon} /> {button.label}
</ActionButton>
</div>
</div>
Expand All @@ -173,5 +160,4 @@ const Card = ({
</div>
)
}

export default Card
2 changes: 1 addition & 1 deletion frontend/src/components/ContributorAvatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const ContributorAvatar = memo(({ contributor, uniqueKey }: ContributorProps) =>
>
<Link href={`/community/users/${login}`} target="_blank" rel="noopener noreferrer">
<img
className="h-[30px] w-[30px] rounded-full grayscale hover:grayscale-0"
className="h-[30px] w-[30px] rounded-full grayscale transition-all duration-300 hover:grayscale-0 hover:scale-110"
src={`${avatarUrl}${isAlgolia ? '&s=60' : ''}`}
alt={`${displayName}'s avatar`}
/>
Expand Down
21 changes: 16 additions & 5 deletions frontend/src/components/DisplayIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,17 @@ import FontAwesomeIconWrapper from 'wrappers/FontAwesomeIconWrapper'
import { Tooltip } from 'components/ui/tooltip'

export default function DisplayIcon({ item, icons }: { item: string; icons: IconType }) {
// className for the container

const brandColors: Record<string, string> = {
discord: 'text-[#7289DA] hover:text-[#5b6eae]',
instagram: 'text-[#E4405F] hover:text-[#c13548]',
linkedin: 'text-[#0077B5] hover:text-[#005582]',
youtube: 'text-[#FF0000] hover:text-[#cc0000]',
}

const isSocialMedia = Object.keys(brandColors).includes(item.toLowerCase())


const containerClassName = [
'flex flex-row-reverse items-center justify-center gap-1 px-4 pb-1 -ml-2',
item === 'stars_count' || item === 'starsCount' ? 'rotate-container' : '',
Expand All @@ -20,9 +30,10 @@ export default function DisplayIcon({ item, icons }: { item: string; icons: Icon
.filter(Boolean)
.join(' ')

// className for the FontAwesome icon

const iconClassName = [
'text-gray-600 dark:text-gray-300',
isSocialMedia ? brandColors[item.toLowerCase()] : 'text-gray-600 dark:text-gray-300',
'transition-transform transform hover:scale-110',
item === 'stars_count' || item === 'starsCount' ? 'icon-rotate' : '',
item === 'forks_count' ||
item === 'contributors_count' ||
Expand All @@ -47,13 +58,13 @@ export default function DisplayIcon({ item, icons }: { item: string; icons: Icon
{/* Display formatted number if the value is a number */}
<span className="text-gray-600 dark:text-gray-300">
{typeof icons[item] === 'number'
? millify(icons[item], { precision: 1 }) // Format large numbers using 'millify' library
? millify(icons[item], { precision: 1 })
: icons[item]}
</span>
<span>
<FontAwesomeIconWrapper
className={iconClassName}
icon={Icons[item as IconKeys]?.icon} // Display corresponding icon
icon={Icons[item as IconKeys]?.icon}
/>
</span>
</div>
Expand Down
Loading