1+ "use client" ;
2+
13import { Job } from "@/lib/csv" ;
24import {
35 Table ,
@@ -7,72 +9,153 @@ import {
79 TableHeader ,
810 TableRow ,
911} from "@/components/ui/table" ;
12+ import {
13+ Select ,
14+ SelectContent ,
15+ SelectItem ,
16+ SelectTrigger ,
17+ SelectValue ,
18+ } from "@/components/ui/select" ;
19+ import { useState , useMemo } from "react" ;
1020
1121interface JobListProps {
1222 jobs : Job [ ] ;
1323}
1424
1525export function JobList ( { jobs } : JobListProps ) {
26+ const [ selectedLanguage , setSelectedLanguage ] = useState < string > ( "all" ) ;
27+ const [ selectedTag , setSelectedTag ] = useState < string > ( "all" ) ;
28+
29+ const languages = useMemo ( ( ) => {
30+ const langs = new Set ( jobs . map ( ( job ) => job . language ) . filter ( Boolean ) ) ;
31+ return Array . from ( langs ) . sort ( ) ;
32+ } , [ jobs ] ) ;
33+
34+ const tags = useMemo ( ( ) => {
35+ const t = new Set ( jobs . flatMap ( ( job ) => job . tags ) ) ;
36+ return Array . from ( t ) . sort ( ) ;
37+ } , [ jobs ] ) ;
38+
39+ const filteredAndSortedJobs = useMemo ( ( ) => {
40+ let result = [ ...jobs ] ;
41+
42+ if ( selectedLanguage && selectedLanguage !== "all" ) {
43+ result = result . filter ( ( job ) => job . language === selectedLanguage ) ;
44+ }
45+
46+ if ( selectedTag && selectedTag !== "all" ) {
47+ result = result . filter ( ( job ) => job . tags . includes ( selectedTag ) ) ;
48+ }
49+
50+ result . sort ( ( a , b ) => {
51+ const repoA = a . repository . split ( "/" ) [ 1 ] || a . repository ;
52+ const repoB = b . repository . split ( "/" ) [ 1 ] || b . repository ;
53+ return repoA . localeCompare ( repoB ) ;
54+ } ) ;
55+
56+ return result ;
57+ } , [ jobs , selectedLanguage , selectedTag ] ) ;
58+
1659 return (
17- < div className = "w-full" >
60+ < div className = "w-full space-y-4" >
61+ < div className = "flex gap-4" >
62+ < Select value = { selectedLanguage } onValueChange = { setSelectedLanguage } >
63+ < SelectTrigger className = "w-[180px]" >
64+ < SelectValue placeholder = "Filter by Language" />
65+ </ SelectTrigger >
66+ < SelectContent >
67+ < SelectItem value = "all" > All Languages</ SelectItem >
68+ { languages . map ( ( lang ) => (
69+ < SelectItem key = { lang } value = { lang } >
70+ { lang }
71+ </ SelectItem >
72+ ) ) }
73+ </ SelectContent >
74+ </ Select >
75+
76+ < Select value = { selectedTag } onValueChange = { setSelectedTag } >
77+ < SelectTrigger className = "w-[180px]" >
78+ < SelectValue placeholder = "Filter by Tag" />
79+ </ SelectTrigger >
80+ < SelectContent >
81+ < SelectItem value = "all" > All Tags</ SelectItem >
82+ { tags . map ( ( tag ) => (
83+ < SelectItem key = { tag } value = { tag } >
84+ { tag }
85+ </ SelectItem >
86+ ) ) }
87+ </ SelectContent >
88+ </ Select >
89+ </ div >
90+
1891 < Table >
1992 < TableHeader >
2093 < TableRow >
2194 < TableHead className = "w-[300px]" > Repository</ TableHead >
95+ < TableHead > Language</ TableHead >
96+ < TableHead > Tags</ TableHead >
2297 < TableHead > Description</ TableHead >
2398 < TableHead className = "text-right" > Job Page</ TableHead >
2499 </ TableRow >
25100 </ TableHeader >
26101 < TableBody >
27- { jobs . map ( ( job ) => (
28- < TableRow key = { job . repository } >
29- < TableCell className = "font-medium" >
30- < div className = "flex flex-col gap-2" >
102+ { filteredAndSortedJobs . map ( ( job ) => {
103+ const [ org , repo ] = job . repository . split ( "/" ) ;
104+ return (
105+ < TableRow key = { job . repository } >
106+ < TableCell className = "font-medium" >
31107 < div className = "flex items-center gap-2 flex-wrap" >
32108 < a
33109 href = { `https://github.com/${ job . repository } ` }
34110 target = "_blank"
35111 rel = "noopener noreferrer"
36- className = "font-semibold hover:underline text-base "
112+ className = "text-base hover:underline"
37113 >
38- { job . repository }
114+ < span className = "text-zinc-500 dark:text-zinc-400" >
115+ { org } /
116+ </ span >
117+ < span className = "font-semibold" > { repo } </ span >
39118 </ a >
40119 < img
41120 src = { `https://img.shields.io/github/stars/${ job . repository } .svg?style=social&label=%20` }
42121 alt = { `${ job . repository } stars` }
43122 className = "h-5"
44123 />
45124 </ div >
125+ </ TableCell >
126+ < TableCell >
127+ { job . language && (
128+ < span className = "inline-flex items-center rounded-md bg-blue-50 px-2 py-1 text-xs font-medium text-blue-700 ring-1 ring-inset ring-blue-700/10 dark:bg-blue-900/30 dark:text-blue-400 dark:ring-blue-400/30" >
129+ { job . language }
130+ </ span >
131+ ) }
132+ </ TableCell >
133+ < TableCell >
46134 < div className = "flex flex-wrap gap-1" >
47- { job . language && (
48- < span className = "inline-flex items-center rounded-md bg-blue-50 px-2 py-1 text-xs font-medium text-blue-700 ring-1 ring-inset ring-blue-700/10" >
49- { job . language }
50- </ span >
51- ) }
52135 { job . tags . slice ( 0 , 3 ) . map ( ( tag ) => (
53136 < span
54137 key = { tag }
55- className = "inline-flex items-center rounded-md bg-gray -50 px-2 py-1 text-xs font-medium text-gray -600 ring-1 ring-inset ring-gray -500/10"
138+ className = "inline-flex items-center rounded-md bg-zinc -50 px-2 py-1 text-xs font-medium text-zinc -600 ring-1 ring-inset ring-zinc -500/10 dark:bg-zinc-800 dark:text-zinc-400 dark:ring-zinc-700 "
56139 >
57140 { tag }
58141 </ span >
59142 ) ) }
60143 </ div >
61- </ div >
62- </ TableCell >
63- < TableCell > { job . description } </ TableCell >
64- < TableCell className = "text-right" >
65- < a
66- href = { job . jobPage }
67- target = "_blank "
68- rel = "noopener noreferrer "
69- className = "text-blue-600 hover:underline dark:text-blue-400"
70- >
71- Apply
72- </ a >
73- </ TableCell >
74- </ TableRow >
75- ) ) }
144+ </ TableCell >
145+ < TableCell > { job . description } </ TableCell >
146+ < TableCell className = "text-right" >
147+ < a
148+ href = { job . jobPage }
149+ target = "_blank"
150+ rel = "noopener noreferrer "
151+ className = "text-blue-600 hover:underline dark:text-blue-400 "
152+ >
153+ Apply
154+ </ a >
155+ </ TableCell >
156+ </ TableRow >
157+ ) ;
158+ } ) }
76159 </ TableBody >
77160 </ Table >
78161 </ div >
0 commit comments