Skip to content

Commit

Permalink
ODP-399 (#614)
Browse files Browse the repository at this point in the history
* ODP-399

* Fix build
  • Loading branch information
luccasmmg authored Dec 18, 2024
1 parent 5e6408f commit 0ac2fb0
Show file tree
Hide file tree
Showing 4 changed files with 280 additions and 81 deletions.
108 changes: 108 additions & 0 deletions deployment/frontend/src/components/home/ApplicationCarousel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { Navigation } from 'swiper/modules'
import { Swiper, SwiperSlide } from 'swiper/react'
import 'swiper/css'
import 'swiper/css/navigation'
import 'swiper/css/pagination'
import CarouselNavButton from '../_shared/CarouselNavButton'
import Image from 'next/image'
import { AutoCarousel } from '../_shared/AutoCarousel'
import Link from 'next/link'
import { api } from '@/utils/api'
import { ErrorAlert } from '../_shared/Alerts'
import Spinner from '../_shared/Spinner'
import { Application, GroupTree, GroupsmDetails } from '@/schema/ckan.schema'
import { Group } from '@portaljs/ckan'

function ApplicationCard({
application,
}: {
application: Application
}) {
const datasetCount = application.package_count
return (
<Link
href={`/applications/${application.name}`}
className="flex w-full flex-col gap-1 font-acumin shadow-wri pb-6"
>
<div className="relative aspect-square h-72 w-full bg-white">
<Image
src={`${
application.image_display_url !== ''
? application?.image_display_url
: '/images/placeholders/applications/applicationsdefault.png'
}`}
alt={`Application - ${application.title}`}
fill
className="object-cover"
/>
</div>
<p className="font-['Acumin Pro SemiCondensed'] text-xl font-semibold text-black pl-4 ">
{application.title}
</p>
<p className="font-['Acumin Pro SemiCondensed'] w-24 text-base font-semibold text-green-700 pl-4 ">
{datasetCount && datasetCount > 1
? `${datasetCount} datasets`
: `${datasetCount} dataset`}
</p>
</Link>
)
}

export function ApplicationsCarousel() {
const { data, isLoading, error } = api.applications.getAllApplications.useQuery()
return (
<div className="relative">
<div className="peer">
<AutoCarousel
name="applications"
prevButton={<PrevButton />}
nextButton={<NextButton />}
>
{error && (
<ErrorAlert
title="Error loading applications"
text={error.message}
/>
)}

{isLoading && (
<div className="w-full flex justify-center">
<Spinner />
</div>
)}

{data && data
.filter((application) => {
const datasetCount = application.package_count
return datasetCount > 0
})
.map((application, index) => (
<SwiperSlide key={index} className="">
<div className=" w-80 pr-6">
<ApplicationCard
application={application}
/>
</div>
</SwiperSlide>
))}
</AutoCarousel>
</div>
</div>
)
}

const PrevButton = () => (
<div
className={`nav-prev-button--applications absolute top-[40%] z-50 ml-[-1.9rem] hidden -translate-y-2/4 opacity-0 transition-all hover:opacity-100 peer-hover:opacity-100 md:left-0 lg:block`}
>
<CarouselNavButton orientation="left" />
</div>
)

const NextButton = () => (
<div
className={`nav-next-button--applications right-[calc(0px + 3.3rem)] absolute top-[40%] z-50 hidden -translate-y-2/4 opacity-0 transition-all hover:opacity-100 peer-hover:opacity-100 md:right-0 lg:block`}
>
<CarouselNavButton orientation="right" />
</div>
)
2 changes: 1 addition & 1 deletion deployment/frontend/src/pages/applications/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
ctx: { session },
transformer: superjson,
})
await Promise.all([await helpers.applications.list.prefetch()])
await Promise.all([await helpers.applications.getAllApplications.prefetch()])

return {
props: {
Expand Down
235 changes: 171 additions & 64 deletions deployment/frontend/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ import { appRouter } from '@/server/api/root'
import { getServerAuthSession } from '@/server/auth'
import dynamic from 'next/dynamic'
import Spinner from '@/components/_shared/Spinner'
import { Tab } from '@headlessui/react'
import { Fragment } from 'react'
import { ArrowLeftStartOnRectangleIcon } from '@heroicons/react/20/solid'
import classNames from '@/utils/classnames'
import { ApplicationsCarousel } from '@/components/home/ApplicationCarousel'

const ErrorAlert = dynamic<{ text: string; title?: string }>(
() =>
Expand Down Expand Up @@ -79,6 +84,18 @@ export default function Home(
sortBy: 'metadata_modified desc',
removeUnecessaryDataInResources: true,
})
const {
data: featuredDatasets,
isLoading: isLoadingFeaturedDatasets,
error: errorFeaturedDatasets,
} = api.dataset.getFeaturedDatasets.useQuery({
removeUnecessaryDataInResources: true,
})
const {
data: application,
isLoading,
error,
} = api.applications.getAllApplications.useQuery()
return (
<>
<Head>
Expand Down Expand Up @@ -148,70 +165,160 @@ export default function Home(
</div>
</main>

<main className="flex min-h-screen flex-col items-center justify-center gap-y-8 bg-neutral-50 py-20">
<div className="topics-carousel relative !ml-auto w-full max-w-[94.5vw] py-10">
<h3 className="font-acumin text-2xl font-bold leading-loose text-stone-900">
Explore Topic
</h3>
<div className="py-4">
<TopicsCarousel />
</div>
</div>
<div className="highlights-carousel relative !ml-auto w-full max-w-[94.5vw]">
<div className="default-home-container w-full border-t-[4px] border-stone-900" />
<h3 className="pt-1 font-acumin text-2xl font-bold leading-loose text-stone-900">
Highlights
</h3>
<div className="py-4">
<HighlightsCarousel />
</div>
</div>
{isLoadingRecentlyAdded ? (
<div className="w-full flex justify-center items-center h-10">
<Spinner />
</div>
) : errorRecentlyAdded ? (
<ErrorAlert
title="Failed to load recently added datasets"
text={errorRecentlyAdded.message}
/>
) : (
<div
id="highlights"
className="max-w-[90.5vw] mx-auto flex flex-col font-acumin gap-y-6 mt-16"
>
<h1 className="font-bold text-[2rem] ml-2">
Recently Added
</h1>
<Recent
datasets={recentlyAdded.datasets}
title="Recently added"
/>
</div>
)}
{isLoadingRecentlyUpdated ? (
<div className="w-full flex justify-center items-center h-10">
<Spinner />
</div>
) : errorRecentlyUpdated ? (
<ErrorAlert
title="Failed to load recently updated datasets"
text={errorRecentlyUpdated.message}
/>
) : (
<div
id="highlights"
className="max-w-[90.8vw] mx-auto flex flex-col font-acumin gap-y-6 mt-16"
>
<h1 className="font-bold text-[2rem] ml-2">
Recently Updated
</h1>
<Recent
datasets={recentlyUpdated.datasets}
title="Recently updated"
/>
</div>
)}
<main className="flex min-h-screen flex-col gap-y-8 bg-neutral-50 py-20">
<Tab.Group>
<Tab.List className="flex gap-x-12 items-center max-w-[94.5vw] ml-auto w-full">
<Tab as={Fragment}>
{({ selected }) => (
<div
className={classNames(
'text-black text-2xl font-medium font-acumin',
selected
? 'text-[#32864b] underline underline-offset-8'
: ''
)}
>
Explore by Topic
</div>
)}
</Tab>
{application && application.length > 0 && (
<Tab as={Fragment}>
{({ selected }) => (
<div
className={classNames(
'text-black text-2xl font-medium font-acumin',
selected
? 'text-[#32864b] underline underline-offset-8'
: ''
)}
>
Explore by application
</div>
)}
</Tab>
)}
</Tab.List>
<Tab.Panels>
<Tab.Panel className="topics-carousel relative !ml-auto w-full max-w-[94.5vw]">
<TopicsCarousel />
</Tab.Panel>
<Tab.Panel className="highlights-carousel relative !ml-auto w-full max-w-[94.5vw]">
<ApplicationsCarousel />
</Tab.Panel>
</Tab.Panels>
</Tab.Group>
<Tab.Group>
<Tab.List className="flex gap-x-12 items-center max-w-[94.5vw] ml-auto w-full pt-20">
<Tab as={Fragment}>
{({ selected }) => (
<div
className={classNames(
'text-black text-2xl font-medium font-acumin',
selected
? 'text-[#32864b] underline underline-offset-8'
: ''
)}
>
Dataset Highlights
</div>
)}
</Tab>
<Tab as={Fragment}>
{({ selected }) => (
<div
className={classNames(
'text-black text-2xl font-medium font-acumin',
selected
? 'text-[#32864b] underline underline-offset-8'
: ''
)}
>
Recently added
</div>
)}
</Tab>
<Tab as={Fragment}>
{({ selected }) => (
<div
className={classNames(
'text-black text-2xl font-medium font-acumin',
selected
? 'text-[#32864b] underline underline-offset-8'
: ''
)}
>
Recently updated
</div>
)}
</Tab>
</Tab.List>
<Tab.Panels>
<Tab.Panel>
{isLoadingFeaturedDatasets ? (
<div className="w-full flex justify-center items-center h-10">
<Spinner />
</div>
) : errorFeaturedDatasets ? (
<ErrorAlert
title="Failed to load highlights"
text={errorFeaturedDatasets.message}
/>
) : (
<div
id="highlights"
className="max-w-[90.5vw] mx-auto flex flex-col font-acumin gap-y-6"
>
<HighlightsCarousel />
</div>
)}
</Tab.Panel>
<Tab.Panel>
{isLoadingRecentlyAdded ? (
<div className="w-full flex justify-center items-center h-10">
<Spinner />
</div>
) : errorRecentlyAdded ? (
<ErrorAlert
title="Failed to load recently added datasets"
text={errorRecentlyAdded.message}
/>
) : (
<div
id="highlights"
className="max-w-[90.5vw] mx-auto flex flex-col font-acumin gap-y-6"
>
<Recent
datasets={recentlyAdded.datasets}
title="Recently added"
/>
</div>
)}
</Tab.Panel>
<Tab.Panel>
{isLoadingRecentlyUpdated ? (
<div className="w-full flex justify-center items-center h-10">
<Spinner />
</div>
) : errorRecentlyUpdated ? (
<ErrorAlert
title="Failed to load recently updated datasets"
text={errorRecentlyUpdated.message}
/>
) : (
<div
id="highlights"
className="max-w-[90.8vw] mx-auto flex flex-col font-acumin gap-y-6"
>
<Recent
datasets={recentlyUpdated.datasets}
title="Recently updated"
/>
</div>
)}
</Tab.Panel>
</Tab.Panels>
</Tab.Group>
</main>
<HomeFooter />
</>
Expand Down
16 changes: 0 additions & 16 deletions deployment/frontend/src/server/api/routers/applications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,20 +170,4 @@ export const applicationRouter = createTRPCRouter({
throw Error(replaceNames(data.error.message))
return data
}),
list: publicProcedure.query(async ({ ctx, input }) => {
const applicationRes = await fetch(
`${env.CKAN_URL}/api/action/group_list?all_fields=True`,
{
headers: {
'Content-Type': 'application/json',
},
}
)
const application: CkanResponse<Group[]> = await applicationRes.json()
if (!application.success && application.error)
throw Error(replaceNames(application.error.message))
return {
applications: application.result,
}
}),
})

0 comments on commit 0ac2fb0

Please sign in to comment.