2025-07-10 08:01:11 +07:00
|
|
|
import { FC, ReactElement, useRef, useState } from 'react';
|
|
|
|
|
import { BannerSection } from './_components/banner-section';
|
|
|
|
|
import { Topics } from './_components/topics';
|
|
|
|
|
import { Input } from '@imphnen-frontend-service/ui/atoms';
|
|
|
|
|
import { SearchOutlined } from '@ant-design/icons';
|
2026-08-04 17:55:59 +07:00
|
|
|
import { For, Show } from '@imphnen-frontend-service/utils';
|
2025-07-10 08:01:11 +07:00
|
|
|
import { MentorCard } from './_components/mentor-card';
|
|
|
|
|
import { Pagination } from '@imphnen-frontend-service/ui/molecules';
|
2026-08-04 17:55:59 +07:00
|
|
|
import { useGetMentors } from '@imphnen-frontend-service/service';
|
2025-07-10 08:01:11 +07:00
|
|
|
import { getCoreRowModel, getPaginationRowModel, PaginationState, useReactTable } from '@tanstack/react-table';
|
|
|
|
|
import { motion, useInView, Variants } from 'framer-motion';
|
|
|
|
|
|
|
|
|
|
export const Components: FC = (): ReactElement => {
|
|
|
|
|
const [pagination, setPagination] = useState<PaginationState>({
|
|
|
|
|
pageIndex: 0,
|
2026-08-04 17:55:59 +07:00
|
|
|
pageSize: 8,
|
2025-07-10 08:01:11 +07:00
|
|
|
});
|
2026-08-04 17:55:59 +07:00
|
|
|
const [search, setSearch] = useState('');
|
|
|
|
|
|
|
|
|
|
const { data: mentors, isLoading } = useGetMentors({ search });
|
2025-07-10 08:01:11 +07:00
|
|
|
|
|
|
|
|
const table = useReactTable({
|
2026-08-04 17:55:59 +07:00
|
|
|
data: mentors ?? [],
|
2025-07-10 08:01:11 +07:00
|
|
|
columns: [],
|
|
|
|
|
state: { pagination },
|
|
|
|
|
getCoreRowModel: getCoreRowModel(),
|
|
|
|
|
getPaginationRowModel: getPaginationRowModel(),
|
|
|
|
|
onPaginationChange: setPagination,
|
|
|
|
|
});
|
|
|
|
|
|
2026-08-04 17:55:59 +07:00
|
|
|
const pageMentors = table.getRowModel().rows.map((row) => row.original);
|
|
|
|
|
|
2025-07-10 08:01:11 +07:00
|
|
|
const ref = useRef(null)
|
|
|
|
|
const isInView = useInView(ref, { once: true, amount: 0.2 })
|
|
|
|
|
|
|
|
|
|
const containerVariants: Variants = {
|
|
|
|
|
hidden: { opacity: 0 },
|
|
|
|
|
visible: {
|
|
|
|
|
opacity: 1,
|
|
|
|
|
transition: {
|
|
|
|
|
staggerChildren: 0.1,
|
|
|
|
|
delayChildren: 0.3,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const childVariants: Variants = {
|
|
|
|
|
hidden: { opacity: 0, y: 20 },
|
|
|
|
|
visible: {
|
|
|
|
|
opacity: 1,
|
|
|
|
|
y: 0,
|
|
|
|
|
transition: {
|
|
|
|
|
duration: 0.4,
|
|
|
|
|
ease: [0.25, 0.46, 0.45, 0.94],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<main>
|
|
|
|
|
<BannerSection />
|
|
|
|
|
|
|
|
|
|
<section className="w-full px-8 md:px-[60px] md:pb-[60px] lg:px-20">
|
|
|
|
|
<motion.div
|
2026-08-05 19:28:02 +07:00
|
|
|
className="max-w-7xl mx-auto bg-white rounded-lg py-6 md:rounded-3xl"
|
2025-07-10 08:01:11 +07:00
|
|
|
initial={{ opacity: 0 }}
|
|
|
|
|
animate={{ opacity: 1 }}
|
|
|
|
|
transition={{ duration: 0.4 }}
|
|
|
|
|
>
|
|
|
|
|
<div className="mb-10 md:px-6 lg:mb-[60px]">
|
|
|
|
|
<motion.h1
|
|
|
|
|
className="text-center text-[15px] text-primary-500 font-semibold mb-8 md:text-2xl md:mb-10 lg:text-3xl lg:mb-[60px]"
|
|
|
|
|
initial={{ opacity: 0, y: 20 }}
|
|
|
|
|
animate={{ opacity: 1, y: 0 }}
|
|
|
|
|
transition={{ duration: 0.4, delay: 0.3 }}
|
|
|
|
|
>
|
|
|
|
|
Choose Your Topics
|
|
|
|
|
</motion.h1>
|
|
|
|
|
|
|
|
|
|
<Topics />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<motion.div ref={ref} className="md:px-6">
|
|
|
|
|
<motion.div
|
|
|
|
|
className="relative mb-6 md:px-12 lg:px-0 lg:w-[480px] lg:mx-auto"
|
|
|
|
|
initial={{ opacity: 0 }}
|
|
|
|
|
animate={{ opacity: 1 }}
|
|
|
|
|
transition={{ duration: 0.4 }}
|
|
|
|
|
>
|
|
|
|
|
<Input
|
|
|
|
|
placeholder="Cari berdasarkan nama, posisi/peran"
|
|
|
|
|
className="relative min-w-full w-full"
|
2026-08-04 17:55:59 +07:00
|
|
|
value={search}
|
|
|
|
|
onChange={(e) => setSearch(e.target.value)}
|
2025-07-10 08:01:11 +07:00
|
|
|
/>
|
|
|
|
|
<SearchOutlined className="absolute right-2.5 top-1/2 -translate-y-1/2 text-primary-500 size-2.5 cursor-text md:me-12 lg:me-0" />
|
|
|
|
|
</motion.div>
|
|
|
|
|
|
|
|
|
|
<motion.div
|
|
|
|
|
className="grid gap-2 mb-10 md:grid-cols-2 md:gap-6 lg:grid-cols-4"
|
|
|
|
|
variants={containerVariants}
|
|
|
|
|
initial="hidden"
|
|
|
|
|
animate={isInView ? 'visible' : 'hidden'}
|
|
|
|
|
>
|
2026-08-04 17:55:59 +07:00
|
|
|
<Show
|
|
|
|
|
condition={!isLoading}
|
|
|
|
|
fallback={
|
|
|
|
|
<motion.p variants={childVariants} className="text-center text-neutral-500 col-span-full py-10">
|
|
|
|
|
Memuat data mentor...
|
|
|
|
|
</motion.p>
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
<Show
|
|
|
|
|
condition={pageMentors.length > 0}
|
|
|
|
|
fallback={
|
|
|
|
|
<motion.p variants={childVariants} className="text-center text-neutral-500 col-span-full py-10">
|
|
|
|
|
Tidak ada mentor ditemukan
|
|
|
|
|
</motion.p>
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
<For data={pageMentors}>
|
|
|
|
|
{(mentor, index) => (
|
|
|
|
|
<motion.div key={mentor.id ?? index} variants={childVariants}>
|
|
|
|
|
<MentorCard mentor={mentor} />
|
|
|
|
|
</motion.div>
|
|
|
|
|
)}
|
|
|
|
|
</For>
|
|
|
|
|
</Show>
|
|
|
|
|
</Show>
|
2025-07-10 08:01:11 +07:00
|
|
|
</motion.div>
|
|
|
|
|
|
|
|
|
|
<Pagination table={table} />
|
|
|
|
|
</motion.div>
|
|
|
|
|
</motion.div>
|
|
|
|
|
</section>
|
|
|
|
|
</main>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default Components
|