@@ -61,7 +61,7 @@ Alternatively, you can use the CDN bundle by adding these tags to your HTML:
6161## Quickstart
6262
6363<details >
64- <summary >< h3 >Bounty board ( HTML & CSS)</ h3 ></summary >
64+ <summary class = " list-none cursor-pointer flex items-center gap-1 " >< svg xmlns = " http://www.w3.org/2000/svg " width = " 24 " height = " 24 " viewBox = " 0 0 24 24 " fill = " none " stroke = " currentColor " stroke-width = " 2 " stroke-linecap = " round " stroke-linejoin = " round " class = " icon icon-tabler icons-tabler-outline icon-tabler-chevron-right " >< path stroke = " none " d = " M0 0h24v24H0z " fill = " none " />< path d = " M9 6l6 6l-6 6 " /></ svg >< div class = " font-semibold text-lg text-foreground " > HTML & CSS</ div ></summary >
6565
6666Add these tags to the ` <head> ` of your HTML:
6767
@@ -108,7 +108,7 @@ Customize the styling as you like:
108108</details >
109109
110110<details >
111- <summary >< h3 >Bounty board ( React & Tailwind)</ h3 ></summary >
111+ <summary class = " mt-4 list-none cursor-pointer flex items-center gap-1 " >< svg xmlns = " http://www.w3.org/2000/svg " width = " 24 " height = " 24 " viewBox = " 0 0 24 24 " fill = " none " stroke = " currentColor " stroke-width = " 2 " stroke-linecap = " round " stroke-linejoin = " round " class = " icon icon-tabler icons-tabler-outline icon-tabler-chevron-right " >< path stroke = " none " d = " M0 0h24v24H0z " fill = " none " />< path d = " M9 6l6 6l-6 6 " /></ svg >< div class = " font-semibold text-lg text-foreground " > React & Tailwind</ div ></summary >
112112
113113``` tsx
114114import { useEffect , useState } from " react" ;
@@ -227,7 +227,7 @@ function Callout() {
227227</details >
228228
229229<details >
230- <summary >< h3 >Bounty board ( React & CSS)</ h3 ></summary >
230+ <summary class = " mt-4 list-none cursor-pointer flex items-center gap-1 " >< svg xmlns = " http://www.w3.org/2000/svg " width = " 24 " height = " 24 " viewBox = " 0 0 24 24 " fill = " none " stroke = " currentColor " stroke-width = " 2 " stroke-linecap = " round " stroke-linejoin = " round " class = " icon icon-tabler icons-tabler-outline icon-tabler-chevron-right " >< path stroke = " none " d = " M0 0h24v24H0z " fill = " none " />< path d = " M9 6l6 6l-6 6 " /></ svg >< div class = " font-semibold text-lg text-foreground " > React & CSS</ div ></summary >
231231
232232``` tsx
233233import { algora , type AlgoraOutput } from " @algora/sdk" ;
@@ -541,234 +541,3 @@ const BountyCardSkeleton = () => (
541541 background-color : hsl (var (--gray-700 ));
542542}
543543```
544-
545- </details >
546-
547- <details >
548- <summary ><h3 >Leaderboard (React & Tailwind)</h3 ></summary >
549-
550- ``` tsx
551- import { useEffect , useMemo , useState } from " react" ;
552- import Image from " next/image" ;
553- import Link from " next/link" ;
554- import { IconBrandTwitter , IconMapPin } from " @tabler/icons-react" ;
555- import { algora , type AlgoraOutput } from " @algora/sdk" ;
556-
557- import { Tabs , TabsList , TabsTrigger } from " ~/components/ui/tabs" ;
558- import { capitalize , range } from " ~/utils/util" ;
559-
560- // TODO: Use your own Algora handle
561- const org = " acme" ;
562-
563- type RemoteData <T > =
564- | { _tag: " loading" }
565- | { _tag: " failure" ; error: Error }
566- | { _tag: " success" ; data: T };
567-
568- type Entry = AlgoraOutput [" org" ][" getLeaderboard" ][number ];
569-
570- const usdFormatter = Intl .NumberFormat (" en-US" , {
571- style: " currency" ,
572- currency: " USD" ,
573- maximumFractionDigits: 0 ,
574- });
575-
576- export function Leaderboard() {
577- const [leaderboard, setLeaderboard] = useState <RemoteData <Entry []>>({
578- _tag: " loading" ,
579- });
580-
581- const [rewardType, setRewardType] = useState <Entry [" reward_type" ]>();
582-
583- const rewardTypes = useMemo (
584- () =>
585- leaderboard ._tag === " success"
586- ? [... new Set (leaderboard .data .map ((x ) => x .reward_type ))]
587- : undefined ,
588- [leaderboard ]
589- );
590-
591- useEffect (() => {
592- const ac = new AbortController ();
593-
594- algora .org .getLeaderboard
595- .query ({ org }, { signal: ac .signal })
596- .then ((data ) => {
597- setLeaderboard ({ _tag: " success" , data });
598- setRewardType (data .at (0 )?.reward_type );
599- })
600- .catch ((error ) => setLeaderboard ({ _tag: " failure" , error }));
601-
602- return () => ac .abort ();
603- }, []);
604-
605- return (
606- <div className = " space-y-2" >
607- <Tabs
608- value = { rewardType }
609- onValueChange = { setRewardType as (_ : string ) => void }
610- >
611- { rewardTypes && (
612- <TabsList
613- className = " grid w-full gap-1 bg-white/5 text-white/50"
614- style = { {
615- gridTemplateColumns: ` repeat(${rewardTypes .length }, minmax(0, 1fr)) ` ,
616- }}
617- >
618- { rewardTypes .map ((t ) => (
619- <TabsTrigger
620- key = { t }
621- className = " hover:bg-purple-600/50 data-[state=active]:bg-purple-600 data-[state=active]:text-white"
622- value = { t }
623- >
624- { capitalize (t )}
625- </TabsTrigger >
626- ))}
627- </TabsList >
628- )}
629- </Tabs >
630- <ul className = " w-full divide-y-2 divide-gray-400/50 overflow-hidden rounded-lg border-2 border-gray-400/50 text-left text-sm text-gray-500 dark:divide-white/10 dark:border-white/10 dark:text-gray-400" >
631- { leaderboard ._tag === " loading" &&
632- range (10 ).map ((i ) => (
633- <LeaderboardEntrySkeleton key = { i } isFirst = { i === 0 } />
634- ))}
635-
636- { leaderboard ._tag === " success" &&
637- leaderboard .data
638- .filter ((entry ) => entry .reward_type === rewardType )
639- .map ((entry , i ) => (
640- <li key = { entry .uid } >
641- <LeaderboardEntry entry = { entry } isFirst = { i === 0 } />
642- </li >
643- ))}
644- </ul >
645- </div >
646- );
647- }
648-
649- function LeaderboardEntry({
650- entry ,
651- isFirst ,
652- }: {
653- entry: Entry ;
654- isFirst: boolean ;
655- }) {
656- const { user, stats, reward_type } = entry ;
657- return (
658- <div className = " group/card relative flex h-full flex-col justify-center gap-4 rounded-none bg-white px-6 py-8 transition-colors dark:bg-white/[2%] dark:bg-gradient-to-br dark:from-white/[2%] dark:via-white/[2%] dark:to-white/[2%] md:flex-row md:justify-between md:gap-4 md:px-12" >
659- <div className = " flex w-full items-center gap-3 text-gray-900 dark:text-white" >
660- <Link
661- href = { ` https://github.com/${user .login } ` }
662- rel = " noopener noreferrer"
663- target = " _blank"
664- className = " relative mr-3 h-16 w-16 shrink-0"
665- >
666- <Image
667- className = " rounded-full"
668- src = { user .avatar_url }
669- alt = { user .login }
670- fill
671- />
672- { isFirst && (
673- <div className = " absolute bottom-12 left-0 right-0" >
674- <div className = " relative mx-auto h-12 w-12" >
675- <Image
676- className = " rounded-full"
677- src = " https://algora.io/emojis/crown.png"
678- alt = " Crown"
679- fill
680- />
681- </div >
682- </div >
683- )}
684- </Link >
685- <div className = " w-full shrink truncate md:max-w-[10rem] lg:max-w-sm xl:max-w-xl" >
686- <Link
687- href = { ` https://github.com/${user .login } ` }
688- rel = " noopener noreferrer"
689- target = " _blank"
690- className = " flex flex-col truncate"
691- >
692- <div className = " truncate text-base font-semibold" >
693- { user .name ?? user .login }
694- </div >
695- <div className = " truncate font-medium text-gray-400 dark:text-gray-400" >
696- @{ user .login }
697- </div >
698- </Link >
699- <div className = " mt-1 flex flex-wrap gap-x-4 gap-y-1 font-normal text-gray-500 dark:text-gray-300" >
700- { user .location && (
701- <div className = " flex items-center gap-1" >
702- <IconMapPin className = " h-4 w-4" aria-hidden = " true" />
703- <span >{ user .location } </span >
704- </div >
705- )}
706- { user .twitter_username && (
707- <Link
708- href = { ` https://twitter.com/${user .twitter_username } ` }
709- rel = " noopener noreferrer"
710- target = " _blank"
711- className = " flex items-center gap-1"
712- >
713- <IconBrandTwitter className = " h-4 w-4" aria-hidden = " true" />
714- <span >{ user .twitter_username } </span >
715- </Link >
716- )}
717- </div >
718- </div >
719- </div >
720- <dl className = " grid w-full max-w-md grid-cols-3 gap-8 whitespace-nowrap" >
721- <div className = " flex flex-col text-left md:text-center" >
722- <dt className = " order-2 text-sm font-medium leading-6 text-gray-500 dark:text-gray-400" >
723- Bount{ stats .num_completed_bounties === 1 ? " y" : " ies" } { " " }
724- <span className = " hidden sm:inline-block" >Completed</span >
725- </dt >
726- <dd className = " order-1 font-mono text-4xl font-bold tracking-tight text-gray-900 dark:text-white" >
727- { stats .num_completed_bounties }
728- </dd >
729- </div >
730- <div className = " col-span-2 flex flex-col text-left md:text-center" >
731- <dt className = " order-2 text-sm font-medium leading-6 text-gray-500 dark:text-gray-400" >
732- Total { reward_type === " cash" ? " Earnings" : " Points" }
733- </dt >
734- <dd className = " order-1 font-mono text-4xl font-bold tracking-tight text-green-500 dark:text-green-400" >
735- { reward_type === " cash"
736- ? usdFormatter .format (stats .total_earnings / 100 )
737- : stats .total_earnings }
738- </dd >
739- </div >
740- </dl >
741- </div >
742- );
743- }
744-
745- function LeaderboardEntrySkeleton(props : { isFirst: boolean }) {
746- return (
747- <div className = " h-[212px] w-full animate-pulse bg-white px-6 py-8 dark:bg-gray-900 md:h-[132px] md:px-12" >
748- <div className = " flex items-center gap-3" >
749- <div className = " relative mr-3 h-16 w-16 shrink-0 " >
750- <div className = " h-full w-full rounded-full bg-gray-200 dark:bg-gray-800" />
751- { props .isFirst && (
752- <div className = " absolute bottom-12 left-0 right-0" >
753- <div className = " relative mx-auto -mb-[2px] h-12 w-12" >
754- <Image
755- className = " rounded-full"
756- src = " https://algora.io/emojis/crown.png"
757- alt = " Crown"
758- fill
759- />
760- </div >
761- </div >
762- )}
763- </div >
764- <div className = " flex flex-col space-y-2" >
765- <div className = " h-6 w-36 rounded-md bg-gray-200 dark:bg-gray-800" />
766- <div className = " h-4 w-20 rounded-md bg-gray-200 dark:bg-gray-800" />
767- </div >
768- </div >
769- </div >
770- );
771- }
772- ```
773-
774- </details >
0 commit comments