diff --git a/src/components/composite/Hero.tsx b/src/components/composite/Hero.tsx index aeeb677b..d3d89753 100644 --- a/src/components/composite/Hero.tsx +++ b/src/components/composite/Hero.tsx @@ -123,7 +123,10 @@ export default function Hero({ navigation, breadcrumbs, icons, title, descriptio {!!tabs && ( {tabs?.map(tab => ( - ))} diff --git a/src/components/element/leaderboard/LeaderboardLibrary.tsx b/src/components/element/leaderboard/LeaderboardLibrary.tsx index 6c6ddd23..5a26b522 100644 --- a/src/components/element/leaderboard/LeaderboardLibrary.tsx +++ b/src/components/element/leaderboard/LeaderboardLibrary.tsx @@ -1,6 +1,6 @@ import { Text } from "dappkit"; import { useMemo } from "react"; -import type { DummyLeaderboard } from "src/routes/_merkl.opportunity.$chain.$type.$id.leaderboard"; +import type { DummyLeaderboard } from "src/routes/_merkl.opportunities.$chain.$type.$id.leaderboard"; import { LeaderboardTable } from "./LeaderboardTable"; import LeaderboardTableRow from "./LeaderboardTableRow"; diff --git a/src/routes/_merkl.opportunities.$chain.$type.$id.tsx b/src/routes/_merkl.opportunities.$chain.$type.$id.tsx index 06021a03..67578d3b 100644 --- a/src/routes/_merkl.opportunities.$chain.$type.$id.tsx +++ b/src/routes/_merkl.opportunities.$chain.$type.$id.tsx @@ -20,8 +20,6 @@ export async function loader({ params: { id, type, chain: chainId } }: LoaderFun identifier: id, }); - if (!opportunity) throw "DAZZ"; - return json({ opportunity }); } diff --git a/src/routes/_merkl.opportunity.$chain.$type.$id.tsx b/src/routes/_merkl.opportunity.$chain.$type.$id.tsx new file mode 100644 index 00000000..67578d3b --- /dev/null +++ b/src/routes/_merkl.opportunity.$chain.$type.$id.tsx @@ -0,0 +1,89 @@ +import type { Opportunity } from "@angleprotocol/merkl-api"; +import { type LoaderFunctionArgs, type MetaFunction, json } from "@remix-run/node"; +import { Meta, Outlet, useLoaderData } from "@remix-run/react"; +import { useMemo } from "react"; +import { ChainService } from "src/api/services/chain.service"; +import { OpportunityService } from "src/api/services/opportunity.service"; +import Hero from "src/components/composite/Hero"; +import Tag from "src/components/element/Tag"; +import { ErrorHeading } from "src/components/layout/ErrorHeading"; +import useOpportunity from "src/hooks/resources/useOpportunity"; + +export async function loader({ params: { id, type, chain: chainId } }: LoaderFunctionArgs) { + if (!chainId || !id || !type) throw ""; + + const chain = await ChainService.get({ search: chainId }); + + const opportunity = await OpportunityService.getCampaignsByParams({ + chainId: chain.id, + type: type, + identifier: id, + }); + + return json({ opportunity }); +} + +export const meta: MetaFunction = ({ data, error }) => { + if (error) return [{ title: error }]; + return [{ title: `${data?.name} on Merkl` }]; +}; + +export type OutletContextOpportunity = { + opportunity: Opportunity; +}; + +export default function Index() { + const { opportunity } = useLoaderData(); + const { tags, description, link } = useOpportunity(opportunity); + + const styleName = useMemo(() => { + const spaced = opportunity?.name.split(" "); + + return spaced + .map(str => { + const key = str + crypto.randomUUID(); + if (!str.match(/[\p{Letter}\p{Mark}]+/gu)) + return [ + + {str} + , + ]; + if (str.includes("-")) + return str + .split("-") + .flatMap((s, i, arr) => [s, i !== arr.length - 1 && -]); + if (str.includes("/")) + return str + .split("/") + .flatMap((s, i, arr) => [s, i !== arr.length - 1 && /]); + return [{str}]; + }) + .flatMap((str, index, arr) => [str, index !== arr.length - 1 && " "]); + }, [opportunity]); + + return ( + <> + + ({ src: t.icon }))} + breadcrumbs={[ + { link: "/", name: "Opportunities" }, + { link: "/", name: opportunity.name }, + ]} + title={styleName} + description={description} + tabs={[ + { label: "Overview", link }, + { label: "Leaderboard", link: `${link}/leaderboard` }, + ]} + tags={tags.map(tag => )} + opportunity={opportunity}> + + + + ); +} + +export function ErrorBoundary() { + return ; +}