Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
129 changes: 98 additions & 31 deletions src/app/(public)/_components/hero.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
'use client';

import { useRouter } from 'next/navigation';
import { useMemo, useState } from 'react';

import { Search } from 'lucide-react';
import { LanguageButton } from './language-button';
import { Button } from './button';
import Link from 'next/link';

import { sortByName } from '@/lib/utils';
import languages from '@/assets/languages.json';
Expand All @@ -17,72 +16,140 @@ const { main: mainLanguages, others: otherLanguages } = languages;
export function Hero() {
const router = useRouter();

// Track selected languages as a string array
const [selected, setSelected] = useState<string[]>([]);

const toggleLanguage = (language: string) => {
setSelected(prev =>
prev.includes(language) ? prev.filter(l => l !== language) : [...prev, language]
);
};

const sortedOthers = useMemo(() => [...otherLanguages].sort(sortByName), []);

function handleSearch(e: React.FormEvent) {
e.preventDefault();
const formData = new FormData(e.target as HTMLFormElement);
const lang = formData.get('search') as string;
if (lang.trim() === '') return;
router.push(`/repos/${lang}`);
let chosenLanguages = selected;

// Fallback: if no checkbox selected, use the single input value
if (chosenLanguages.length === 0) {
const typed = String(formData.get('search') || '').trim();
if (typed) {
chosenLanguages = [typed];
}
}

if (chosenLanguages.length === 0) return; // nothing to search

const params = new URLSearchParams();
chosenLanguages.forEach(lang => params.append('l', lang.toLowerCase()));

router.push(`/repos?${params.toString()}`);
}

return (
<div className="relative bg-hero-gradient ">
<div className="z-50 flex flex-col space-y-8 justify-center items-center text-center min-h-screen pt-28 sm:pt-24">
<div className="max-w-md space-y-5 px-4">
<h1 className="text-3xl sm:text-4xl md:text-5xl font-medium uppercase heading-text">
Search your language
Search your language(s)
</h1>
<form
className="items-center w-full max-w-xs mx-auto form-control outline-none"
id="hero-search-form"
className="items-center w-full max-w-xs mx-auto form-control outline-none mt-8"
onSubmit={handleSearch}
>
<div className="flex w-full">
<div className="relative flex w-full">
<input
type="text"
placeholder="Search for your language"
placeholder="Type a language (optional)"
className="w-full max-w-xs bg-transparent rounded-tr-none rounded-br-none input input-bordered text-hacktoberfest-light border-hacktoberfest-light
focus:border-hacktoberfest-light focus:!outline-none focus-visible:!outline-none placeholder:text-hacktoberfest-light text-sm sm:text-base"
name="search"
/>
</div>
<button
type="submit"
className="bg-transparent rounded-tl-none rounded-bl-none group btn btn-square text-hacktoberfest-light border-hacktoberfest-light hover:!border-hacktoberfest-light hover:bg-primary-btn-hover-gradient flex-shrink-0"
>
<Search size={16} className="sm:w-5 sm:h-5" />
</button>
</div>
</form>
<p className="font-medium uppercase text-hacktoberfest-light text-sm sm:text-base">
Or select the programming language you would like to find
Or select one or more programming languages you would like to find
repositories for.
</p>

<div className="flex flex-wrap gap-4 sm:gap-6 items-center justify-center">
{mainLanguages.map(language => (
<LanguageButton key={language} language={language} />
))}
{mainLanguages.map(language => {
const id = `lang-${language}`;
const checked = selected.includes(language);
return (
<label
key={language}
htmlFor={id}
className="flex items-center gap-2 cursor-pointer select-none"
>
<input
id={id}
type="checkbox"
className="checkbox checkbox-primary"
checked={checked}
onChange={() => toggleLanguage(language)}
/>
<span className="text-hacktoberfest-light text-sm sm:text-base">
{language}
</span>
</label>
);
})}
</div>
<div className="dropdown dropdown-top">
<Button tabIndex={0} className="umami--click--otherlangs-button text-sm sm:text-base">

<div className="dropdown dropdown-top mt-4">
<Button
tabIndex={0}
className="umami--click--otherlangs-button text-sm sm:text-base"
>
Other languages
</Button>

<ul
tabIndex={0}
className="h-64 p-2 overflow-y-auto shadow-lg menu dropdown-content bg-white/95 backdrop-blur-sm rounded-xl w-60 border border-gray-200/50 z-[9999]"
className="h-64 p-2 overflow-y-auto shadow-lg menu dropdown-content bg-white/95 backdrop-blur-sm rounded-xl w-72 border border-gray-200/50 z-[9999]"
>
{otherLanguages.sort(sortByName).map(language => (
<li key={language}>
<Link
href={`/repos/${language.toLowerCase()}`}
className="text-gray-700 hover:text-white hover:bg-hacktoberfest-blue rounded-lg transition-colors duration-200 px-3 py-2 text-sm"
>
{language}
</Link>
</li>
))}
{sortedOthers.map(language => {
const id = `other-${language}`;
const checked = selected.includes(language);
return (
<li key={language} className="px-1">
<label
htmlFor={id}
className="flex items-center gap-3 rounded-lg px-3 py-2 hover:bg-hacktoberfest-blue/80 hover:text-white cursor-pointer"
>
<input
id={id}
type="checkbox"
className="checkbox checkbox-primary"
checked={checked}
onChange={() => toggleLanguage(language)}
/>
<span className="text-sm text-gray-800">{language}</span>
</label>
</li>
);
})}
</ul>
</div>
<div className="mt-6 flex items-center justify-center">
<button
type="submit"
form="hero-search-form"
aria-label="Search repositories"
title="Search"
className="bg-transparent rounded-tl-none rounded-bl-none group btn text-hacktoberfest-light border-hacktoberfest-light hover:!border-hacktoberfest-light hover:bg-primary-btn-hover-gradient flex items-center gap-2 px-5"
>
<Search size={16} className="sm:w-5 sm:h-5" />
<span className="text-sm sm:text-base">Search</span>
</button>
</div>

</div>
<MarqueTextAnimation />
</div>
Expand Down
10 changes: 5 additions & 5 deletions src/app/(public)/repos/[language]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import { env } from '@/env.mjs';
import { notFound } from 'next/navigation';
import { capitalize } from '@/lib/utils';
import { Header } from '@/app/(public)/_components/header';
import { ScrollToTop } from './_components/scroll-to-top';
import { RepoCard } from './_components/repo-card';
import { Sorter } from './_components/sorter';
import { StarsFilter } from './_components/stars-filter';
import { Pagination } from './_components/pagination';
import { ScrollToTop } from '../_components/scroll-to-top';
import { RepoCard } from '../_components/repo-card';
import { Sorter } from '../_components/sorter';
import { StarsFilter } from '../_components/stars-filter';
import { Pagination } from '../_components/pagination';
import type { RepoData, RepoItem, RepoResponse, SearchParams } from '@/types';
import type { Metadata } from 'next';
import { auth } from '@/auth';
Expand Down
109 changes: 109 additions & 0 deletions src/app/(public)/repos/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
export default function Loading() {
return (
<>
{/* Header Skeleton */}
<div className="border-b border-hacktoberfest-dark-green">
<div className="container mx-auto px-4 py-4">
<div className="flex items-center justify-between">
<div className="h-8 w-32 bg-shimmer animate-shimmer rounded-md"></div>
<div className="flex space-x-4">
<div className="h-8 w-20 bg-shimmer animate-shimmer rounded-md"></div>
<div className="h-8 w-24 bg-shimmer animate-shimmer rounded-md"></div>
</div>
</div>
</div>
</div>

<div className="container mx-auto">
<div className="min-h-screen pt-5">
{/* Title Section Skeleton */}
<div className="text-center">
<div className="w-5/6 max-w-md mx-auto">
<div className="mb-5 space-y-3">
<div className="h-12 w-full bg-shimmer animate-shimmer rounded-lg"></div>
<div className="h-8 w-3/4 mx-auto bg-shimmer animate-shimmer rounded-lg"></div>
</div>
</div>
</div>

{/* Sorter Skeleton */}
<div className="flex justify-center mb-6">
<div className="flex space-x-4">
<div className="h-10 w-24 bg-shimmer animate-shimmer rounded-md"></div>
<div className="h-10 w-28 bg-shimmer animate-shimmer rounded-md"></div>
<div className="h-10 w-20 bg-shimmer animate-shimmer rounded-md"></div>
</div>
</div>

{/* Stars Filter Skeleton */}
<div className="flex justify-center mb-8">
<div className="flex space-x-3">
<div className="h-10 w-32 bg-shimmer animate-shimmer rounded-md"></div>
<div className="h-10 w-16 bg-shimmer animate-shimmer rounded-md"></div>
<div className="h-10 w-32 bg-shimmer animate-shimmer rounded-md"></div>
</div>
</div>

{/* Repository Cards Grid Skeleton */}
<div className="grid grid-cols-1 gap-8 p-4 md:grid-cols-2 lg:grid-cols-3">
{Array.from({ length: 21 }).map((_, index) => (
<div
key={index}
className="bg-hacktoberfest-black border border-hacktoberfest-dark-green rounded-lg p-6 shadow-sm hover:shadow-md transition-shadow duration-200"
>
{/* Repository Title */}
<div className="mb-4">
<div className="h-6 w-3/4 bg-shimmer animate-shimmer rounded mb-2"></div>
<div className="h-4 w-full bg-shimmer animate-shimmer rounded"></div>
<div className="h-4 w-2/3 bg-shimmer animate-shimmer rounded mt-1"></div>
</div>

{/* Language and Stats */}
<div className="flex items-center justify-between mb-4">
<div className="flex items-center space-x-2">
<div className="h-3 w-3 bg-shimmer animate-shimmer rounded-full"></div>
<div className="h-4 w-16 bg-shimmer animate-shimmer rounded"></div>
</div>
<div className="flex space-x-4">
<div className="flex items-center space-x-1">
<div className="h-4 w-4 bg-shimmer animate-shimmer rounded"></div>
<div className="h-4 w-8 bg-shimmer animate-shimmer rounded"></div>
</div>
<div className="flex items-center space-x-1">
<div className="h-4 w-4 bg-shimmer animate-shimmer rounded"></div>
<div className="h-4 w-8 bg-shimmer animate-shimmer rounded"></div>
</div>
</div>
</div>

{/* Topics */}
<div className="flex flex-wrap gap-2 mb-4">
<div className="h-6 w-20 bg-shimmer animate-shimmer rounded-full"></div>
<div className="h-6 w-16 bg-shimmer animate-shimmer rounded-full"></div>
<div className="h-6 w-24 bg-shimmer animate-shimmer rounded-full"></div>
</div>

{/* Action Button */}
<div className="h-10 w-full bg-shimmer animate-shimmer rounded-md"></div>
</div>
))}
</div>
</div>

{/* Pagination Skeleton */}
<div className="flex justify-center items-center space-x-2 py-8">
<div className="h-10 w-20 bg-shimmer animate-shimmer rounded-md"></div>
<div className="flex space-x-1">
{Array.from({ length: 5 }).map((_, index) => (
<div
key={index}
className="h-10 w-10 bg-shimmer animate-shimmer rounded-md"
></div>
))}
</div>
<div className="h-10 w-20 bg-shimmer animate-shimmer rounded-md"></div>
</div>
</div>
</>
);
}
Loading
Loading