aboutsummaryrefslogtreecommitdiff
path: root/pages
diff options
context:
space:
mode:
Diffstat (limited to 'pages')
-rw-r--r--pages/404.js25
-rw-r--r--pages/_app.js48
-rw-r--r--pages/_document.js24
-rw-r--r--pages/about.js59
-rw-r--r--pages/anime/[...id].js623
-rw-r--r--pages/anime/watch/[...info].js323
-rw-r--r--pages/api/auth/[...nextauth].js76
-rw-r--r--pages/contact.js17
-rw-r--r--pages/dmca.js103
-rw-r--r--pages/index.js405
-rw-r--r--pages/manga/chapter/[chapter].js423
-rw-r--r--pages/manga/detail/id.js318
-rw-r--r--pages/search.js430
-rw-r--r--pages/staff.js431
-rw-r--r--pages/test.js241
15 files changed, 3546 insertions, 0 deletions
diff --git a/pages/404.js b/pages/404.js
new file mode 100644
index 0000000..8b91a69
--- /dev/null
+++ b/pages/404.js
@@ -0,0 +1,25 @@
+import Head from "next/head";
+import Footer from "../components/footer";
+import Navbar from "../components/navbar";
+
+export default function Custom404() {
+ return (
+ <>
+ <Head>
+ <title>Not Found</title>
+ <meta name="about" content="About this web" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <link rel="icon" href="/c.svg" />
+ </Head>
+ <Navbar className="dark:bg-black" />
+ <div className="flex h-[800px] w-screen items-center justify-center gap-10 bg-slate-50 text-2xl dark:bg-[#121212]">
+ <div className="flex items-center gap-8 font-karla font-semibold">
+ <h1>404</h1>
+ <div className="h-[45px] w-[2px] bg-black dark:bg-white" />
+ <h1>Nothing to see here.</h1>
+ </div>
+ </div>
+ <Footer />
+ </>
+ );
+}
diff --git a/pages/_app.js b/pages/_app.js
new file mode 100644
index 0000000..869f66e
--- /dev/null
+++ b/pages/_app.js
@@ -0,0 +1,48 @@
+import { ThemeProvider } from "next-themes";
+import "../styles/globals.css";
+import { useRouter } from "next/router";
+import { AnimatePresence, motion as m } from "framer-motion";
+import NextNProgress from "nextjs-progressbar";
+import { SessionProvider } from "next-auth/react";
+
+export default function App({
+ Component,
+ pageProps: { session, ...pageProps },
+}) {
+ const router = useRouter();
+
+ return (
+ <SessionProvider session={session}>
+ <ThemeProvider attribute="class">
+ <AnimatePresence mode="wait">
+ <m.div
+ key={`route-${router.route}`}
+ transition={{ duration: 0.5 }}
+ initial="initialState"
+ animate="animateState"
+ exit="exitState"
+ variants={{
+ initialState: {
+ opacity: 0,
+ },
+ animateState: {
+ opacity: 1,
+ },
+ exitState: {},
+ }}
+ className="z-50 w-screen"
+ >
+ <NextNProgress
+ color="#FF7E2C"
+ startPosition={0.3}
+ stopDelayMs={200}
+ height={3}
+ showOnShallow={true}
+ />
+ <Component {...pageProps} />
+ </m.div>
+ </AnimatePresence>
+ </ThemeProvider>
+ </SessionProvider>
+ );
+}
diff --git a/pages/_document.js b/pages/_document.js
new file mode 100644
index 0000000..effc121
--- /dev/null
+++ b/pages/_document.js
@@ -0,0 +1,24 @@
+import { Html, Head, Main, NextScript } from "next/document";
+
+export default function Document() {
+ return (
+ <Html lang="en">
+ <Head>
+ <link rel="manifest" href="/manifest.json" />
+ <link rel="apple-touch-icon" href="/icon.png"></link>
+ <meta name="theme-color" content="#141519" />
+ <link
+ rel="stylesheet"
+ href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css"
+ integrity="sha512-1PKOgIY59xJ8Co8+NE6FZ+LOAZKjy+KY8iq0G4B3CyeY6wYHN3yt9PW0XpSriVlkMXe40PTKnXrLnZ9+fkDaog=="
+ crossOrigin="anonymous"
+ />
+ <link rel="icon" href="/c.svg" />
+ </Head>
+ <body>
+ <Main />
+ <NextScript />
+ </body>
+ </Html>
+ );
+}
diff --git a/pages/about.js b/pages/about.js
new file mode 100644
index 0000000..24f04f3
--- /dev/null
+++ b/pages/about.js
@@ -0,0 +1,59 @@
+import Head from "next/head";
+import Layout from "../components/layout";
+import UnderConstruction from "../components/underConst";
+
+export default function About() {
+ const clientId = process.env.ANILIST_CLIENT_ID;
+
+ return (
+ <>
+ <Head>
+ <title>Moopa - About</title>
+ <meta name="about" content="About this web" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <link rel="icon" href="/c.svg" />
+ </Head>
+ <Layout>
+ <div className="mb-[6rem] bg-[#121212] text-white flex min-h-screen w-screen flex-col justify-center gap-8 px-6 pt-nav lg:items-center lg:gap-14">
+ <h1 className="place-items-start font-karla text-[3rem] font-bold">
+ Hi !
+ </h1>
+ <div className="flex flex-col gap-3 font-roboto text-xl text-[#cdcdcd] lg:mx-52 lg:gap-10 lg:text-2xl">
+ <div>
+ <p className="inline-block font-extrabold text-[#ffffff]">
+ Welcome to our website!
+ </p>{" "}
+ Moopa is a platform where you can watch and stream anime or read
+ manga for free, without any ads or VPNs. Our mission is to provide
+ a convenient and enjoyable experience for anime and manga
+ enthusiasts all around the world.
+ </div>
+ <p>
+ At our site, you will find a vast collection of anime and manga
+ titles from different genres, including action, adventure, comedy,
+ romance, and more. We take pride in our fast and reliable servers,
+ which ensure smooth streaming and reading for all our users.
+ </p>
+ <p>
+ It is important to note that we do not store any files on our
+ servers. Instead, we only link to media hosted on third-party
+ services. This is to ensure that we comply with copyright laws and
+ respect the intellectual property rights of content creators.
+ </p>
+ <p>
+ We are committed to providing a safe and secure environment for
+ our users. Our website is regularly monitored to prevent any
+ malicious activities, and we take proactive measures to ensure the
+ safety of our community.
+ </p>
+ <p>
+ Thank you for choosing our website as your go-to platform for
+ anime and manga. We hope you enjoy your stay here, and feel free
+ to contact us if you have any feedback or suggestions.
+ </p>
+ </div>
+ </div>
+ </Layout>
+ </>
+ );
+}
diff --git a/pages/anime/[...id].js b/pages/anime/[...id].js
new file mode 100644
index 0000000..d42a394
--- /dev/null
+++ b/pages/anime/[...id].js
@@ -0,0 +1,623 @@
+import React, { useEffect, useState } from "react";
+import { AnimatePresence, motion as m } from "framer-motion";
+import { META } from "@consumet/extensions";
+
+import Link from "next/link";
+import Layout from "../../components/layout";
+import Head from "next/head";
+
+import { closestMatch } from "closest-match";
+import Content from "../../components/hero/content";
+import Image from "next/image";
+
+export default function Himitsu({
+ info,
+ slicedDesc,
+ color,
+ episodeList,
+ episode1,
+ judul,
+ subIndo,
+ epIndo,
+}) {
+ const [isLoading, setIsloading] = useState(false);
+ const [showText, setShowtext] = useState(false);
+ const [title, setTitle] = useState(info.title.english || info.title.romaji);
+ const [load, setLoad] = useState(true);
+ const [Lang, setLang] = useState(true);
+ const [showAll, setShowAll] = useState(false);
+
+ const [lastPlayed, setLastPlayed] = useState(null);
+ const episode = episodeList;
+ const epi1 = episode1;
+
+ const maxItems = 3;
+
+ function handleEnLang() {
+ setLang(true);
+ }
+
+ function handleIdLang() {
+ setLang(false);
+ }
+
+ // const { ref } = useParallax({ speed: 10 });
+
+ useEffect(() => {
+ const playedStr = JSON.parse(localStorage.getItem("lastPlayed"));
+ setLastPlayed(
+ playedStr?.filter((item) => item.title === info.title.romaji)[0]?.data
+ );
+ function getBrightness(color) {
+ const rgb = color.match(/\d+/g);
+ return (299 * rgb[0] + 587 * rgb[1] + 114 * rgb[2]) / 1000;
+ }
+
+ // set the text color based on the background color
+ function setTextColor(element) {
+ const backgroundColor = getComputedStyle(element).backgroundColor;
+ const brightness = getBrightness(backgroundColor);
+ if (brightness < 128) {
+ element.style.color = "#fff"; // white
+ } else {
+ element.style.color = "#000"; // black
+ }
+ }
+
+ const elements = document.querySelectorAll(".dynamic-text");
+ elements.forEach((element) => {
+ setTextColor(element);
+ });
+ }, [color]);
+
+ const handleStore = (props) => {
+ let existingData = JSON.parse(localStorage.getItem("recentWatch"));
+ if (!Array.isArray(existingData)) {
+ existingData = [];
+ }
+ const index = existingData.findIndex((item) => item.title === props.title);
+ if (index !== -1) {
+ existingData.splice(index, 1);
+ }
+ const updatedData = [props, ...existingData];
+ localStorage.setItem("recentWatch", JSON.stringify(updatedData));
+ };
+
+ if (!info) {
+ return;
+ }
+
+ let episodeIndo = null;
+ if (epIndo < 17) {
+ episodeIndo = episode.slice(0, epIndo);
+ } else {
+ episodeIndo = episode;
+ }
+
+ // console.log({ NEXT: subIndo });
+
+ // console.log(episodeIndo);
+
+ // console.log(lastPlayed);
+
+ function handleLoad() {
+ setLoad(false);
+ }
+ return (
+ <>
+ <Head>
+ <title>{info.title?.english || info.title.romaji}</title>
+ <meta name="detail" content="Detail about the Anime" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <link rel="icon" href="/c.svg" />
+ </Head>
+
+ <Layout navTop="text-white bg-[#121212] md:pt-0 md:px-0 bg-slate bg-opacity-40">
+ <div className="text static flex w-screen flex-col justify-center pt-nav pb-10">
+ <div className="pointer-events-none absolute top-0 left-0">
+ <div className="brightness-90 bg-gradient-to-t from-[#121212] to-transparent">
+ <img
+ // ref={ref}
+ src={info.cover || info.image}
+ className="h-[300px] w-screen object-cover brightness-75 mix-blend-darken"
+ />
+ <div className="z-10 h-full drop-shadow-2xl bg-[#121212]" />
+ </div>
+ </div>
+ {isLoading ? (
+ <p>Loading cuy sabar...</p>
+ ) : info ? (
+ <div className="flex flex-col items-center gap-10">
+ <div className="flex w-screen flex-col gap-10 md:w-[70%]">
+ <div className="z-40 flex flex-col gap-10 px-5 pt-[8rem] md:flex-row lg:mt-[5rem] lg:px-0">
+ <div className="flex gap-10 md:h-[250px] md:w-52">
+ <div className="flex h-[200px] w-52 bg-[#dadada50] md:h-[250px] md:w-full">
+ {info.image && (
+ <>
+ <div
+ style={{
+ backgroundImage: `url(${info.image})`,
+ height: "100%",
+ width: "100%",
+ backgroundSize: "cover",
+ backgroundPosition: "center",
+ }}
+ // src={info.image}
+ className="h-[200px] w-[200px] md:h-[250px] bg-white shadow-md"
+ />
+ </>
+ )}
+ </div>
+
+ {/* MOBILE */}
+ <div className="flex w-full flex-col gap-5 lg:hidden ">
+ <h1 className="shrink-0 text-2xl font-semibold">
+ {judul}
+ </h1>
+ <div className="flex w-[90%] flex-col gap-1">
+ <div className="flex gap-2">
+ <h1>Rate:</h1>
+ <p className="font-bold">{info.rating}%</p>
+ </div>
+
+ <div className="flex w-[200px] gap-2">
+ <h1>Format:</h1>
+ <p>{info.type}</p>
+ </div>
+
+ <div className="flex gap-2">
+ <h1>Status:</h1>
+ <p>{info.status}</p>
+ </div>
+ </div>
+ <div className="flex">
+ {epi1 && epi1[0] ? (
+ <Link
+ href={`/anime/watch/${epi1[0].id}/${info.id}`}
+ onClick={() =>
+ handleStore({
+ title:
+ info.title?.english ||
+ info.title.romaji ||
+ info.title.native,
+ description: info.description,
+ image: info.image,
+ id: info.id,
+ })
+ }
+ >
+ <h1 className="flex cursor-pointer items-center gap-2 rounded-[20px] bg-[#ff9537] px-4 py-2 font-bold text-[#ffffff]">
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ width="13"
+ height="12"
+ fill="none"
+ viewBox="0 0 250 289"
+ >
+ <path
+ fill="#fff"
+ d="M249.734 144.5l-249 143.761V.741l249 143.759z"
+ ></path>
+ </svg>{" "}
+ WATCH
+ </h1>
+ </Link>
+ ) : (
+ <h1 className="pointer-events-none flex items-center gap-2 rounded-[20px] bg-[#ff94378f] px-4 py-2 font-bold text-[#ffffffa5]">
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ width="13"
+ height="12"
+ className="fill-[#ffffff8d]"
+ viewBox="0 0 250 289"
+ >
+ <path d="M249.734 144.5l-249 143.761V.741l249 143.759z"></path>
+ </svg>{" "}
+ WATCH
+ </h1>
+ )}
+ </div>
+ </div>
+ </div>
+
+ {/* PC */}
+ <div className="w-full flex-col gap-5 md:flex">
+ <div className="hidden flex-col gap-5 lg:flex">
+ <h1 className="text-4xl font-bold">
+ {info.title?.english ||
+ info.title.romaji ||
+ info.title.native}
+ </h1>
+ <div className="flex gap-6">
+ <div
+ className={`dynamic-text rounded-md px-2 font-karla font-bold`}
+ style={color}
+ >
+ {episode && episode.length} Episodes
+ </div>
+ <div
+ className={`dynamic-text rounded-md px-2 font-karla font-bold`}
+ style={color}
+ >
+ {info.releaseDate}
+ </div>
+ <div
+ className={`dynamic-text rounded-md px-2 font-karla font-bold`}
+ style={color}
+ >
+ {info.rating}%
+ </div>
+ <div
+ className={`dynamic-text rounded-md px-2 font-karla font-bold`}
+ style={color}
+ >
+ {info.type}
+ </div>
+ <div
+ className={`dynamic-text rounded-md px-2 font-karla font-bold`}
+ style={color}
+ >
+ {info.status}
+ </div>
+ <div
+ className={`dynamic-text rounded-md px-2 font-karla font-bold`}
+ style={color}
+ >
+ Sub | {subIndo === null ? "EN" : "EN/ID"}
+ </div>
+ </div>
+ </div>
+ <div
+ className={`hidden h-[140px] transition-all duration-300 overflow-y-hidden scrollbar-thin scrollbar-thumb-[#1b1c21] scrollbar-thumb-rounded-md hover:overflow-y-scroll hover:scrollbar-thumb-[#2e2f37] lg:block`}
+ >
+ <p
+ dangerouslySetInnerHTML={{ __html: info.description }}
+ className="mr-5"
+ />
+ </div>
+ <div className="lg:hidden">
+ <div
+ dangerouslySetInnerHTML={{
+ __html: showText ? info.description : slicedDesc,
+ }}
+ ></div>
+ <button
+ onClick={() => setShowtext(!showText)}
+ className="font-rama font-bold text-white"
+ >
+ {showText ? " Show Less" : " Show More"}
+ </button>
+ </div>
+ </div>
+ </div>
+
+ <div className="">
+ <div className="flex gap-5 items-center">
+ <div className="p-3 lg:p-0 text-3xl font-bold">
+ Relations
+ </div>
+ {info.relations.length > maxItems && (
+ <div
+ className="cursor-pointer"
+ onClick={() => setShowAll(!showAll)}
+ >
+ {showAll ? "show less" : "show more"}
+ </div>
+ )}
+ </div>
+ <div
+ className={`w-screen lg:w-full grid lg:grid-cols-3 justify-items-center gap-7 lg:pt-7 px-3 lg:px-4 pt-10 rounded-xl`}
+ >
+ {info.relations &&
+ info.relations
+ .slice(0, showAll ? info.relations.length : maxItems)
+ .map((relation, index) => {
+ return (
+ <Link
+ key={relation.id}
+ href={
+ relation.type === "TV" ||
+ relation.type === "OVA" ||
+ relation.type === "MOVIE" ||
+ relation.type === "SPECIAL" ||
+ relation.type === "ONA"
+ ? `/anime/${relation.id}`
+ : `/manga/detail/id?aniId=${
+ relation.id
+ }&aniTitle=${encodeURIComponent(
+ info.title?.english ||
+ info.title.romaji ||
+ info.title.native
+ )}`
+ }
+ className={`hover:scale-[1.02] scale-100 transition-transform duration-200 ease-out w-full ${
+ relation.type === "MUSIC"
+ ? "pointer-events-none"
+ : ""
+ }`}
+ >
+ <div
+ key={relation.id}
+ initial={{ opacity: 0, y: 50 }}
+ animate={{ opacity: 1, y: 0 }}
+ exit={{
+ opacity: 0,
+ y: -50,
+ transition: { duration: 0.5 },
+ }}
+ transition={{
+ duration: 0.8,
+ delay: index * 0.1,
+ }}
+ className="w-full shrink h-[126px] bg-secondary flex rounded-md"
+ >
+ <div className="min-w-[20%] bg-image rounded-l-md shrink-0">
+ <img
+ src={relation.image}
+ alt={relation.id}
+ className="object-cover h-full w-full shrink-0 rounded-l-md"
+ />
+ </div>
+ <div className="min-w-[80%] h-full grid px-3 items-center">
+ <div className="text-action font-outfit font-bold">
+ {relation.relationType}
+ </div>
+ <div className="font-outfit font-thin italic line-clamp-2">
+ {relation.title.romaji}
+ </div>
+ <div className={``}>{relation.type}</div>
+ </div>
+ </div>
+ </Link>
+ );
+ })}
+ </div>
+ </div>
+
+ <div className="z-20 flex flex-col gap-10 p-3 lg:p-0">
+ <div className="flex items-center gap-10">
+ <h1 className="text-3xl font-bold">Episodes</h1>
+ <div className="flex items-center rounded-md">
+ <button
+ onClick={handleEnLang}
+ className={
+ Lang
+ ? `w-16 p-2 rounded-l-md bg-[#212121]`
+ : `w-16 p-2 rounded-l-md bg-[#171717] text-[#404040]`
+ }
+ >
+ EN
+ </button>
+ <div className="w-[1px] bg-white h-4" />
+ <button
+ onClick={handleIdLang}
+ className={
+ subIndo === null
+ ? `w-16 p-2 rounded-r-md bg-[#171717] text-[#404040] pointer-events-none`
+ : Lang
+ ? `w-16 p-2 rounded-r-md bg-[#171717] text-[#404040]`
+ : `w-16 p-2 rounded-r-md bg-[#212121]`
+ }
+ >
+ ID
+ </button>
+ </div>
+ </div>
+ <div className="flex h-[640px] flex-col gap-5 overflow-y-hidden scrollbar-thin scrollbar-thumb-[#1b1c21] scrollbar-thumb-rounded-full hover:overflow-y-scroll hover:scrollbar-thumb-[#2e2f37]">
+ {episode && Lang ? (
+ episode.map((episode, index) => {
+ const item = lastPlayed?.find(
+ (item) => item.id === episode.id
+ );
+ // console.log(item);
+ return (
+ <div key={index} className="flex flex-col gap-3">
+ <Link
+ onClick={() =>
+ handleStore({
+ title:
+ info.title?.english ||
+ info.title.romaji ||
+ info.title.native,
+ description: info.description,
+ image: info.image,
+ id: info.id,
+ })
+ }
+ href={`/anime/watch/${episode.id}/${info.id}/${
+ item ? `${item.time}` : ""
+ }`}
+ className={`text-start text-xl ${
+ item ? "text-[#414141]" : "text-white"
+ }`}
+ >
+ <p>Episode {episode.number}</p>
+ {episode.title && (
+ <p
+ className={`text-[14px] ${
+ item ? "text-[#414141]" : "text-[#b1b1b1]"
+ } italic`}
+ >
+ "{episode.title}"
+ </p>
+ )}
+ </Link>
+ <div className="h-[1px] bg-white" />
+ </div>
+ );
+ })
+ ) : subIndo === null ? (
+ <p>No Episodes Available</p>
+ ) : (
+ <>
+ <div className="flex h-[640px] flex-col gap-5 overflow-y-hidden scrollbar-thin scrollbar-thumb-[#1b1c21] scrollbar-thumb-rounded-full hover:overflow-y-scroll hover:scrollbar-thumb-[#2e2f37]">
+ {episodeIndo.map((episode, index) => {
+ return (
+ <div key={index} className="flex flex-col gap-3">
+ <Link
+ onClick={() =>
+ handleStore({
+ title:
+ info.title?.english ||
+ info.title.romaji ||
+ info.title.native,
+ description: info.description,
+ image: info.image,
+ id: info.id,
+ })
+ }
+ href={`/anime/watch?title=${encodeURIComponent(
+ info.title?.romaji || info.title?.english
+ )}&id=${subIndo}&idInt=${info.id}&epi=${
+ episode.number
+ }&epiTitle=${encodeURIComponent(
+ episode.title
+ )}&te=${epIndo}&sub=id`}
+ className="text-start text-xl"
+ >
+ <p>Episode {episode.number}</p>
+ <p className="text-[14px] text-[#b1b1b1] italic">
+ "{episode.title}" (Sub Indonesia)
+ </p>
+ </Link>
+ <div className="h-[1px] bg-white" />
+ </div>
+ );
+ })}
+ </div>
+ </>
+ )}
+ </div>
+ </div>
+ </div>
+ <div className="w-screen md:w-[80%]">
+ <Content
+ ids="recommendAnime"
+ section="Recommendations"
+ data={info.recommendations}
+ />
+ </div>
+ </div>
+ ) : (
+ <div className="flex h-screen flex-col items-center justify-center gap-10 pb-52 ">
+ <h1 className="scale-150 font-roboto text-6xl text-red-400">
+ 404
+ </h1>
+ <p className="text-4xl font-semibold">{`> Woops.. I think we don't have that Anime :(`}</p>
+ <Link className="pt-10 text-2xl" href="/search">
+ Return to search
+ </Link>
+ </div>
+ )}
+ </div>
+ </Layout>
+ </>
+ );
+}
+
+export const getServerSideProps = async (context) => {
+ context.res.setHeader(
+ "Cache-Control",
+ "public, s-maxage=10, stale-while-revalidate=59"
+ );
+ const { id } = context.query;
+ if (!id) {
+ return {
+ notFound: true,
+ };
+ }
+
+ const provider = new META.Anilist();
+
+ const [info, episodes] = await Promise.all([
+ fetch(`https://api.moopa.my.id/meta/anilist/info/${id[0]}`).then((res) =>
+ res.json()
+ ),
+ provider.fetchEpisodesListById(id[0]),
+ ]);
+
+ let episodeList = episodes;
+ if (episodes.length === 0) {
+ const res = await fetch(
+ `https://api.moopa.my.id/anime/gogoanime/${
+ info.title.romaji || info.title.english
+ }`
+ );
+ const data = await res.json();
+ const match = closestMatch(
+ info.title.romaji,
+ data.results.map((item) => item.title)
+ );
+ const anime = data.results.filter((item) => item.title === match);
+ if (anime.length !== 0) {
+ const infos = await fetch(
+ `https://api.moopa.my.id/anime/gogoanime/info/${anime[0].id}`
+ ).then((res) => res.json());
+ episodeList = infos.episodes;
+ }
+ }
+
+ const ress = await fetch(
+ `https://ani-api-eight.vercel.app/kuramanime/search?query=${
+ info.title.romaji || info.title?.english
+ }`
+ );
+
+ const yes = await ress.json();
+
+ // Clannad Fixer
+ function convertToClannad(text) {
+ const regex = /(?<!\w)CLANNAD(?!\w)/g;
+ return text.replace(regex, "Clannad");
+ }
+
+ const fixedTitle = convertToClannad(info.title.romaji);
+
+ let epis = null;
+ let slug = null;
+
+ if (!yes.error) {
+ // let anime = yes.list.filter((item) => item.title.includes(fixedTitle));
+ let list = yes.list.map((item) => item.title);
+ const match = closestMatch(fixedTitle, list);
+
+ const anime = yes.list.filter((item) => item.title === match);
+
+ slug = anime[0]?.slug;
+ const inf = await fetch(
+ `https://ani-api-eight.vercel.app/kuramanime/anime/${slug}`
+ );
+
+ const dataInf = await inf.json();
+ epis = dataInf.episode;
+ }
+
+ const desc = info.description.slice(0, 150) + "...";
+ const color = { backgroundColor: `${info.color}` };
+ const epi1 = episodes.filter((epi) => epi.number === 1);
+ const title = info.title?.userPreferred || "No Title";
+
+ const MAX = 20;
+
+ const oriJ = info.title?.english || info.title.romaji || info.title.native;
+ const judul = oriJ.length > MAX ? `${oriJ.substring(0, MAX)}...` : oriJ;
+
+ return {
+ props: {
+ info: {
+ ...info,
+ title: {
+ ...info.title,
+ userPreferred: title,
+ },
+ },
+ slicedDesc: desc,
+ color,
+ episodeList,
+ episode1: epi1,
+ judul,
+ subIndo: slug,
+ epIndo: epis,
+ },
+ };
+};
diff --git a/pages/anime/watch/[...info].js b/pages/anime/watch/[...info].js
new file mode 100644
index 0000000..73e04f5
--- /dev/null
+++ b/pages/anime/watch/[...info].js
@@ -0,0 +1,323 @@
+import Layout from "../../../components/layout";
+// import { data } from "../../../lib/testData";
+// import { aniData } from "../../../lib/infoData";
+import Image from "next/image";
+import VideoPlayer from "../../../components/videoPlayer";
+import Link from "next/link";
+import { closestMatch } from "closest-match";
+import Head from "next/head";
+import { useEffect, useState } from "react";
+import Modal from "../../../components/modal";
+
+export default function Info({ info }) {
+ const title = info.aniData.title.romaji || info.aniData.title.english;
+ const data = info.aniData;
+ const fallback = info.epiFallback;
+
+ const [open, setOpen] = useState(false);
+
+ const playingTitle = data.episodes
+ .filter((item) => item.id == info.id)
+ .map((item) => item.title);
+
+ return (
+ <>
+ <Head>
+ <title>
+ {fallback ? data.title.romaji || data.title.english : playingTitle}
+ </title>
+ </Head>
+ <Modal open={open} onClose={() => setOpen(false)}>
+ <div className="bg-[#202020] rounded-lg w-[268px] text-center">
+ <div className="p-5 grid gap-2 justify-center place-items-center">
+ <h1 className="text-md font-extrabold font-karla">
+ Save this Anime to Your List
+ </h1>
+ <h1 className="text-sm font-karla font-extralight w-[205px]">
+ Are you sure you want to save this anime to your list?
+ </h1>
+ <div className="flex gap-12 items-center pt-3 justify-between">
+ <button className="p-2 font-karla font-extrabold text-sm bg-[#93FF3E] w-[84px] rounded-[10px] text-black shadow">
+ YES
+ </button>
+ <button
+ onClick={() => setOpen(false)}
+ className="p-2 font-karla font-extrabold text-sm bg-white w-[84px] rounded-[10px] text-black shadow-lg"
+ >
+ NO
+ </button>
+ </div>
+ </div>
+ </div>
+ </Modal>
+ <div className="min-h-screen flex flex-col lg:gap-0 gap-5 lg:flex-row lg:py-10 lg:px-10 justify-start w-screen">
+ <div className="w-screen lg:w-[67%]">
+ <div className="h-auto aspect-video z-20">
+ <VideoPlayer
+ key={info.id}
+ data={info.epiData}
+ seek={info.seek}
+ titles={title}
+ id={info.id}
+ />
+ </div>
+ <div>
+ <div className="">
+ {data.episodes.length > 0 ? (
+ data.episodes
+ .filter((items) => items.id == info.id)
+ .map((item) => (
+ <div key={item.id} className="p-3 grid gap-2">
+ <div className="text-xl font-outfit font-semibold line-clamp-2">
+ <Link
+ href={`/anime/${data.id}`}
+ className="inline hover:underline"
+ >
+ {item.title}
+ </Link>
+ </div>
+ <h4 className="text-sm font-karla font-light">
+ Episode {item.number}
+ </h4>
+ </div>
+ ))
+ ) : (
+ <>
+ {fallback
+ .filter((item) => item.id == info.id)
+ .map((item) => (
+ <div key={item.id} className="p-3 grid gap-2">
+ <div className="text-xl font-outfit font-semibold line-clamp-2">
+ <Link
+ href={`/anime/${data.id}`}
+ className="inline hover:underline"
+ >
+ {title}
+ </Link>
+ </div>
+ <h4 className="text-sm font-karla font-light">
+ Episode {item.number}
+ </h4>
+ </div>
+ ))}
+ </>
+ )}
+ </div>
+ <div className="h-[1px] bg-[#3b3b3b]" />
+ <div>
+ <div className="px-4 pt-7 pb-4 h-full flex">
+ <div className="aspect-[9/13] h-[240px]">
+ <Image
+ src={data.image}
+ alt="Anime Cover"
+ width={1000}
+ height={1000}
+ className="object-cover aspect-[9/13] h-[240px] rounded-md"
+ />
+ </div>
+ <div className="grid w-full px-5 gap-3 h-[240px]">
+ <div className="grid grid-cols-2 gap-1 items-center">
+ <h2 className="text-sm font-light font-roboto text-[#878787]">
+ Studios
+ </h2>
+ <div className="row-start-2">{data.studios}</div>
+ <div className="grid col-start-2 place-content-end relative">
+ <div className="" onClick={() => setOpen(true)}>
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ fill="none"
+ viewBox="0 0 24 24"
+ strokeWidth={1.5}
+ stroke="currentColor"
+ className="w-8 h-8 hover:fill-white hover:cursor-pointer"
+ >
+ <path
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ d="M17.593 3.322c1.1.128 1.907 1.077 1.907 2.185V21L12 17.25 4.5 21V5.507c0-1.108.806-2.057 1.907-2.185a48.507 48.507 0 0111.186 0z"
+ />
+ </svg>
+ {/* <span className=" transition-all duration-300 absolute -top-12 bg-[#2c2c2c] font-karla p-2 rounded-l-lg rounded-tr-lg right-3 select-none">
+ Save to My List
+ </span> */}
+ </div>
+ </div>
+ </div>
+ <div className="grid gap-1 items-center">
+ <h2 className="text-sm font-light font-roboto text-[#878787]">
+ Status
+ </h2>
+ <div>{data.status}</div>
+ </div>
+ <div className="grid gap-1 items-center overflow-y-hidden">
+ <h2 className="text-sm font-light font-roboto text-[#878787]">
+ Titles
+ </h2>
+ <div className="grid grid-flow-dense grid-cols-2 gap-2 h-full w-full">
+ <div className="line-clamp-3">
+ {data.title.romaji || ""}
+ </div>
+ <div className="line-clamp-3">
+ {data.title.english || ""}
+ </div>
+ <div className="line-clamp-3">
+ {data.title.native || ""}
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div className="flex flex-wrap gap-3 px-4 pt-3 ">
+ {data.genres.map((item, index) => (
+ <div
+ key={index}
+ className="border border-action text-gray-100 py-1 px-2 rounded-md font-karla text-sm"
+ >
+ {item}
+ </div>
+ ))}
+ </div>
+ <div className={`bg-[#2a2a2a] rounded-md mt-3 mx-3`}>
+ <p
+ dangerouslySetInnerHTML={{ __html: data.description }}
+ className={`p-5 text-sm font-light font-roboto text-[#e4e4e4] `}
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+ <div className="flex flex-col w-screen lg:w-[33%] ">
+ <h1 className="text-xl font-karla pl-4 pb-5 font-semibold">
+ Episodes
+ </h1>
+ <div className="grid gap-5 lg:px-5 px-2 py-2 scrollbar-thin scrollbar-thumb-[#313131] scrollbar-thumb-rounded-full">
+ {data.episodes.length > 0
+ ? data.episodes.map((item) => {
+ return (
+ <Link
+ href={`/anime/watch/${item.id}/${data.id}`}
+ key={item.id}
+ className={`bg-secondary flex w-full h-[110px] rounded-lg scale-100 transition-all duration-300 ease-out ${
+ item.id == info.id
+ ? "pointer-events-none ring-1 ring-action"
+ : "cursor-pointer hover:scale-[1.02] ring-0 hover:ring-1 hover:shadow-lg ring-white"
+ }`}
+ >
+ <div className="w-[40%] h-full relative shrink-0">
+ <Image
+ src={item.image}
+ alt="image"
+ height={1000}
+ width={1000}
+ className={`object-cover rounded-lg h-[110px] shadow-[4px_0px_5px_0px_rgba(0,0,0,0.3)] ${
+ item.id == info.id
+ ? "brightness-[30%]"
+ : "brightness-75"
+ }`}
+ />
+ <span className="absolute bottom-2 left-2 font-karla font-light text-sm">
+ Episode {item.number}
+ </span>
+ {item.id == info.id && (
+ <div className="absolute top-11 left-[105px] scale-[1.5]">
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ viewBox="0 0 20 20"
+ fill="currentColor"
+ className="w-5 h-5"
+ >
+ <path d="M6.3 2.841A1.5 1.5 0 004 4.11V15.89a1.5 1.5 0 002.3 1.269l9.344-5.89a1.5 1.5 0 000-2.538L6.3 2.84z" />
+ </svg>
+ </div>
+ )}
+ </div>
+ <div
+ className={`w-[70%] h-full select-none p-4 flex flex-col gap-2 ${
+ item.id == info.id ? "text-[#7a7a7a]" : ""
+ }`}
+ >
+ <h1 className="font-karla font-bold italic line-clamp-1">
+ {item.title}
+ </h1>
+ <p className="line-clamp-2 text-xs italic font-outfit font-extralight">
+ {item.description}
+ </p>
+ </div>
+ </Link>
+ );
+ })
+ : fallback.map((item) => {
+ return (
+ <Link
+ href={`/anime/watch/${item.id}/${data.id}`}
+ key={item.id}
+ className={`bg-secondary flex-center w-full h-[50px] rounded-lg scale-100 transition-all duration-300 ease-out ${
+ item.id == info.id
+ ? "pointer-events-none ring-1 ring-action text-[#5d5d5d]"
+ : "cursor-pointer hover:scale-[1.02] ring-0 hover:ring-1 hover:shadow-lg ring-white"
+ }`}
+ >
+ Episode {item.number}
+ </Link>
+ );
+ })}
+ </div>
+ </div>
+ </div>
+ </>
+ );
+}
+
+export async function getServerSideProps(context) {
+ const { info } = context.query;
+ if (!info) {
+ return {
+ notFound: true,
+ };
+ }
+
+ const id = info[0];
+ const aniId = info[1];
+ const seek = info[2] || 0;
+ let epiFallback = null;
+
+ const res = await fetch(`https://api.moopa.my.id/meta/anilist/watch/${id}`);
+ const epiData = await res.json();
+
+ const res2 = await fetch(
+ `https://api.moopa.my.id/meta/anilist/info/${aniId}`
+ );
+ const aniData = await res2.json();
+
+ if (aniData.episodes.length === 0) {
+ const res = await fetch(
+ `https://api.moopa.my.id/anime/gogoanime/${
+ aniData.title.romaji || aniData.title.english
+ }`
+ );
+ const data = await res.json();
+ const match = closestMatch(
+ aniData.title.romaji,
+ data.results.map((item) => item.title)
+ );
+ const anime = data.results.filter((item) => item.title === match);
+ if (anime.length !== 0) {
+ const infos = await fetch(
+ `https://api.moopa.my.id/anime/gogoanime/info/${anime[0].id}`
+ ).then((res) => res.json());
+ epiFallback = infos.episodes;
+ }
+ }
+
+ return {
+ props: {
+ info: {
+ id,
+ seek,
+ epiData,
+ aniData,
+ epiFallback,
+ },
+ },
+ };
+}
diff --git a/pages/api/auth/[...nextauth].js b/pages/api/auth/[...nextauth].js
new file mode 100644
index 0000000..571bfdb
--- /dev/null
+++ b/pages/api/auth/[...nextauth].js
@@ -0,0 +1,76 @@
+import NextAuth from "next-auth";
+import { GET_CURRENT_USER } from "../../../queries";
+import { client } from "../../../lib/apolloClient";
+import crypto from "crypto";
+import clientPromise from "../../../lib/mongodb";
+import { MongoDBAdapter } from "@next-auth/mongodb-adapter";
+
+const jwtSecret = crypto.randomBytes(64).toString("hex");
+
+export const authOptions = {
+ // Configure one or more authentication providers
+ adapter: MongoDBAdapter(clientPromise),
+ providers: [
+ {
+ id: "AniListProvider",
+ name: "AniList",
+ type: "oauth",
+ token: "https://anilist.co/api/v2/oauth/token",
+ authorization: {
+ url: "https://anilist.co/api/v2/oauth/authorize",
+ params: { scope: "", response_type: "code" },
+ },
+ userinfo: {
+ url: process.env.GRAPHQL_ENDPOINT,
+ async request(context) {
+ const { data } = await client.query({
+ query: GET_CURRENT_USER,
+ context: {
+ headers: {
+ Authorization: "Bearer " + context.tokens.access_token,
+ },
+ },
+ });
+
+ return {
+ token: context.tokens.access_token,
+ name: data.Viewer.name,
+ sub: data.Viewer.id,
+ image: data.Viewer.avatar,
+ };
+ },
+ },
+ clientId: process.env.CLIENT_ID,
+ clientSecret: process.env.CLIENT_SECRET,
+ profile(profile) {
+ return {
+ token: profile.token,
+ id: profile.sub,
+ name: profile?.name,
+ image: profile.image,
+ };
+ },
+ },
+ ],
+ secret: jwtSecret,
+ session: {
+ //Sets the session to use JSON Web Token
+ strategy: "jwt",
+ cookie: {
+ // Set the cookie to be secure and HTTP-only
+ secure: true,
+ httpOnly: true,
+ },
+ },
+ callbacks: {
+ async jwt({ token, user }) {
+ return { ...token, ...user };
+ },
+ async session({ session, token, user }) {
+ session.user = token;
+ return session;
+ },
+ },
+};
+
+export default NextAuth(authOptions);
diff --git a/pages/contact.js b/pages/contact.js
new file mode 100644
index 0000000..7dbc6d1
--- /dev/null
+++ b/pages/contact.js
@@ -0,0 +1,17 @@
+import Layout from "../components/layout";
+
+const Contact = () => {
+ return (
+ <Layout className="">
+ <div className=" flex h-screen w-screen flex-col items-center justify-center font-karla font-bold">
+ <h1>Contact Us</h1>
+ <p>If you have any questions or comments, please email us at:</p>
+ <p>
+ <a href="mailto:[email protected]">[email protected]</a>
+ </p>
+ </div>
+ </Layout>
+ );
+};
+
+export default Contact;
diff --git a/pages/dmca.js b/pages/dmca.js
new file mode 100644
index 0000000..7244bf7
--- /dev/null
+++ b/pages/dmca.js
@@ -0,0 +1,103 @@
+import Head from "next/head";
+import Layout from "../components/layout";
+
+export default function DMCA() {
+ return (
+ <>
+ <Head>
+ <title>Moopa - DMCA</title>
+ <meta name="DMCA" content="DMCA" />
+ <meta property="og:title" content="DMCA" />
+ <meta
+ property="og:description"
+ content="Moopa.my.id is committed to respecting the intellectual
+ property rights of others and complying with the Digital
+ Millennium Copyright Act (DMCA)."
+ />
+ <meta
+ property="og:image"
+ content="https://cdn.discordapp.com/attachments/1068758633464201268/1081591948705546330/logo.png"
+ />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <link rel="icon" href="/c.svg" />
+ </Head>
+ <Layout>
+ <div className="min-h-screen flex w-screen justify-center items-center">
+ <div className="w-[75%] text-2xl gap-7 flex flex-col my-[10rem]">
+ <div className="flex">
+ <h1 className="text-4xl font-bold font-karla rounded-md bg-[#212121] p-3">
+ DMCA - Disclaimer
+ </h1>
+ </div>
+ <div className="flex flex-col gap-10">
+ <div className="flex flex-col gap-3 text-[#cdcdcd]">
+ <p>
+ Moopa.my.id is committed to respecting the intellectual
+ property rights of others and complying with the Digital
+ Millennium Copyright Act (DMCA). We take copyright
+ infringement seriously and will respond to notices of alleged
+ copyright infringement that comply with the DMCA and any other
+ applicable laws.
+ </p>
+ <p>
+ If you believe that any content on our website is infringing
+ upon your copyrights, please send us an email. Please allow up
+ to 2-5 business days for a response. Please note that emailing
+ your complaint to other parties such as our Internet Service
+ Provider, Hosting Provider, and other third parties will not
+ expedite your request and may result in a delayed response due
+ to the complaint not being filed properly.
+ </p>
+ </div>
+ <p className="text-white">
+ In order for us to process your complaint, please provide the
+ following information:
+ </p>
+ <div className="text-xl ml-5 text-[#cdcdcd]">
+ <ul className="flex flex-col gap-1">
+ <li>
+ · Your name, address, and telephone number. We reserve the
+ right to verify this information.
+ </li>
+ <li>
+ · Identification of the copyrighted work claimed to have
+ been infringed.
+ </li>
+ <li>
+ · The exact and complete URL link where the infringing
+ material is located.
+ </li>
+ <li>
+ · The exact and complete URL link where the infringing
+ material is located.
+ </li>
+ <li>
+ · The exact and complete URL link where the infringing
+ material is located.
+ </li>
+ <li>· Please write to us in English or Indonesian.</li>
+ </ul>
+ </div>
+ <p className="text-[#cdcdcd]">
+ Please note that anonymous or incomplete messages will not be
+ dealt with. Thank you for your understanding.
+ </p>
+ <h1 className="text-white font-karla">DISCLAIMER:</h1>
+ <p className="text-[#cdcdcd]">
+ None of the files listed on Moopa.my.id are hosted on our
+ servers. All links point to content hosted on third-party
+ websites. Moopa.my.id does not accept responsibility for content
+ hosted on third-party websites and has no involvement in the
+ downloading/uploading of movies. We only post links that are
+ available on the internet. If you believe that any content on
+ our website infringes upon your intellectual property rights and
+ you hold the copyright for that content, please report it to
+ [email protected] and the content will be immediately removed.
+ </p>
+ </div>
+ </div>
+ </div>
+ </Layout>
+ </>
+ );
+}
diff --git a/pages/index.js b/pages/index.js
new file mode 100644
index 0000000..7c26f0b
--- /dev/null
+++ b/pages/index.js
@@ -0,0 +1,405 @@
+import { MdChevronLeft, MdChevronRight } from "react-icons/md";
+import { aniListData } from "../lib/AniList";
+import React, { useState, useEffect } from "react";
+import ReactHtmlParser from "kt-react-html-parser";
+import Head from "next/head";
+import Link from "next/link";
+import Footer from "../components/footer";
+import Image from "next/image";
+import Content from "../components/hero/content";
+import { useRouter } from "next/router";
+
+export default function Home({ detail, populars, topDesc }) {
+ const [isVisible, setIsVisible] = useState(false);
+ const [recently, setRecently] = useState(null);
+ const popular = populars.data;
+ const data = detail.data[0];
+ const router = useRouter();
+
+ const handleShowClick = () => {
+ setIsVisible(true);
+ };
+
+ const handleHideClick = () => {
+ setIsVisible(false);
+ };
+
+ useEffect(() => {
+ function fetchData() {
+ const recent = JSON.parse(localStorage.getItem("recentWatch"));
+ if (recent) {
+ setRecently(recent);
+ }
+ }
+ fetchData();
+ }, []);
+
+ function handleRemove() {
+ localStorage.removeItem("recentWatch");
+ setRecently(null);
+ }
+
+ const slideLeft = () => {
+ var slider = document.getElementById("recentslider");
+ slider.scrollLeft = slider.scrollLeft - 500;
+ };
+ const slideRight = () => {
+ var slider = document.getElementById("recentslider");
+ slider.scrollLeft = slider.scrollLeft + 500;
+ };
+
+ const handleFormSubmission = (inputValue) => {
+ router.push(`/search?hasil=${encodeURIComponent(inputValue)}`);
+ };
+
+ const handleKeyDown = async (event) => {
+ if (event.key === "Enter") {
+ event.preventDefault();
+ const inputValue = event.target.value;
+ handleFormSubmission(inputValue);
+ }
+ };
+
+ return (
+ <>
+ <Head>
+ <title>Moopa</title>
+ <meta charSet="UTF-8"></meta>
+ <meta name="twitter:card" content="summary_large_image" />
+ <meta
+ name="twitter:title"
+ content="Moopa - Free Anime and Manga Streaming"
+ />
+ <meta
+ name="twitter:description"
+ content="Discover your new favorite anime or manga title! Moopa offers a vast library of high-quality content, accessible on multiple devices and without any interruptions. Start using Moopa today!"
+ />
+ <meta
+ name="twitter:image"
+ content="https://cdn.discordapp.com/attachments/1084446049986420786/1093300833422168094/image.png"
+ />
+ <link rel="icon" href="/c.svg" />
+ </Head>
+ <div className="z-50">
+ {!isVisible && (
+ <button
+ onClick={handleShowClick}
+ className="fixed bottom-[30px] right-[20px] z-[100] flex h-[51px] w-[50px] cursor-pointer items-center justify-center rounded-[8px] bg-[#101925] shadow-menu md:hidden"
+ id="bars"
+ >
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ className="h-[42px] w-[61.5px] text-[#8BA0B2] fill-orange-500"
+ viewBox="0 0 20 20"
+ fill="currentColor"
+ >
+ <path
+ fillRule="evenodd"
+ d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
+ clipRule="evenodd"
+ />
+ </svg>
+ </button>
+ )}
+
+ {/* Mobile Menu */}
+ <div>
+ {isVisible && (
+ <div className="fixed bottom-[25px] right-[15px] z-50 flex h-[66px] w-[255px] items-center justify-center gap-8 rounded-[10px] text-[11px] bg-[#101925] shadow-menu md:hidden">
+ <div className="flex gap-7">
+ <button className="group flex flex-col items-center">
+ <Link href="/" className="">
+ <svg
+ width="28"
+ height="24"
+ viewBox="0 0 28 24"
+ className=" group-hover:fill-cyan-700 fill-white"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <g clipPath="url(#clip0_224_286)">
+ <path d="M14.0937 -0.571411C14.0937 -0.571411 5.91783 6.54859 1.34879 10.4046C1.08049 10.6499 0.876953 11.0073 0.876953 11.4286C0.876953 12.1659 1.46774 12.7619 2.19863 12.7619H4.84199V22.0953C4.84199 22.8326 5.43278 23.4286 6.16367 23.4286H10.1287C10.8596 23.4286 11.4504 22.8313 11.4504 22.0953V16.7619H16.7371V22.0953C16.7371 22.8313 17.3279 23.4286 18.0588 23.4286H22.0238C22.7547 23.4286 23.3455 22.8326 23.3455 22.0953V12.7619H25.9888C26.7197 12.7619 27.3105 12.1659 27.3105 11.4286C27.3105 11.0073 27.107 10.6499 26.8043 10.4046C22.267 6.54859 14.0937 -0.571411 14.0937 -0.571411Z" />
+ </g>
+ <defs>
+ <clipPath id="clip0_224_286">
+ <rect
+ width="27"
+ height="24"
+ fill="white"
+ transform="translate(0.5)"
+ />
+ </clipPath>
+ </defs>
+ </svg>
+ </Link>
+ <Link
+ href="/"
+ className="font-karla font-bold text-[#8BA0B2] group-hover:text-cyan-700"
+ >
+ home
+ </Link>
+ </button>
+ <button className="group flex flex-col items-center">
+ <Link href="/about">
+ <svg
+ width="27"
+ height="25"
+ viewBox="0 0 27 25"
+ className=" group-hover:fill-cyan-700 fill-white"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <g clipPath="url(#clip0_224_292)">
+ <path d="M21.3402 0.5H5.65974C4.31427 0.500087 3.02394 0.996857 2.07261 1.88103C1.12127 2.7652 0.586852 3.96435 0.586914 5.21469V19.7853C0.586852 21.0356 1.12127 22.2348 2.07261 23.119C3.02394 24.0031 4.31427 24.4999 5.65974 24.5H21.3402C22.6856 24.4999 23.976 24.0031 24.9273 23.119C25.8786 22.2348 26.4131 21.0356 26.413 19.7853V5.21469C26.4131 3.96435 25.8786 2.7652 24.9273 1.88103C23.976 0.996857 22.6856 0.500087 21.3402 0.5ZM13.5 4.93182C13.8482 4.93182 14.1887 5.02779 14.4782 5.20759C14.7678 5.3874 14.9935 5.64297 15.1268 5.94197C15.2601 6.24098 15.2949 6.57 15.227 6.88742C15.159 7.20484 14.9913 7.49642 14.7451 7.72527C14.4988 7.95412 14.1851 8.10996 13.8435 8.1731C13.5019 8.23624 13.1479 8.20384 12.8261 8.07999C12.5043 7.95613 12.2293 7.7464 12.0358 7.4773C11.8424 7.2082 11.7391 6.89182 11.7391 6.56818C11.7391 6.13419 11.9246 5.71798 12.2548 5.4111C12.5851 5.10422 13.0329 4.93182 13.5 4.93182ZM15.9212 20.1364H11.2255C10.9142 20.1364 10.6156 20.0214 10.3954 19.8168C10.1753 19.6123 10.0516 19.3348 10.0516 19.0455C10.0516 18.7561 10.1753 18.4787 10.3954 18.2741C10.6156 18.0695 10.9142 17.9545 11.2255 17.9545H12.326V11.4091H11.2255C10.9142 11.4091 10.6156 11.2942 10.3954 11.0896C10.1753 10.885 10.0516 10.6075 10.0516 10.3182C10.0516 10.0289 10.1753 9.75138 10.3954 9.54679C10.6156 9.34221 10.9142 9.22727 11.2255 9.22727H14.6739V17.9545H15.9212C16.2325 17.9545 16.5311 18.0695 16.7512 18.2741C16.9714 18.4787 17.0951 18.7561 17.0951 19.0455C17.0951 19.3348 16.9714 19.6123 16.7512 19.8168C16.5311 20.0214 16.2325 20.1364 15.9212 20.1364Z" />
+ </g>
+ <defs>
+ <clipPath id="clip0_224_292">
+ <rect
+ width="27"
+ height="24"
+ fill="white"
+ transform="translate(0 0.5)"
+ />
+ </clipPath>
+ </defs>
+ </svg>
+ </Link>
+ <Link
+ href="/about"
+ className="font-karla font-bold text-[#8BA0B2] group-hover:text-cyan-700"
+ >
+ about
+ </Link>
+ </button>
+ <button className="group flex gap-[1.5px] flex-col items-center ">
+ <div>
+ <Link href="/search">
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ viewBox="0 0 24 24"
+ fill="currentColor"
+ className="group-hover:fill-cyan-700 fill-white w-6 h-6"
+ >
+ <path
+ fillRule="evenodd"
+ d="M10.5 3.75a6.75 6.75 0 100 13.5 6.75 6.75 0 000-13.5zM2.25 10.5a8.25 8.25 0 1114.59 5.28l4.69 4.69a.75.75 0 11-1.06 1.06l-4.69-4.69A8.25 8.25 0 012.25 10.5z"
+ clipRule="evenodd"
+ />
+ </svg>
+ </Link>
+ </div>
+ <Link
+ href="/search"
+ className="font-karla font-bold text-[#8BA0B2] group-hover:text-cyan-700"
+ >
+ search
+ </Link>
+ </button>
+ </div>
+ <button onClick={handleHideClick}>
+ <svg
+ width="20"
+ height="21"
+ className="fill-orange-500"
+ viewBox="0 0 20 21"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <rect
+ x="2.44043"
+ y="0.941467"
+ width="23.5842"
+ height="3.45134"
+ rx="1.72567"
+ transform="rotate(45 2.44043 0.941467)"
+ />
+ <rect
+ x="19.1172"
+ y="3.38196"
+ width="23.5842"
+ height="3.45134"
+ rx="1.72567"
+ transform="rotate(135 19.1172 3.38196)"
+ />
+ </svg>
+ </button>
+ </div>
+ )}
+ </div>
+ </div>
+ <div className="h-auto w-screen bg-[#141519] text-white">
+ <div className="flex items-center justify-center">
+ <div className="flex w-full items-center justify-between px-5 md:mx-[94px]">
+ <div className="flex items-center md:gap-16 md:pt-7">
+ <h1 className=" font-outfit text-[40px] font-bold text-[#FF7F57]">
+ moopa
+ </h1>
+ <ul className="hidden gap-10 pt-2 font-outfit text-[14px] md:flex">
+ <Link href="/search">
+ <li>AniList Index</li>
+ </Link>
+ <Link href="/test">
+ <li>Manga</li>
+ </Link>
+ <li>Anime</li>
+ </ul>
+ </div>
+ <div className="relative flex scale-75 items-center">
+ <div className="search-box ">
+ <input
+ className="search-text"
+ type="text"
+ placeholder="Search Anime"
+ onKeyDown={handleKeyDown}
+ />
+ <a href="#" className="search-btn">
+ <i className="fas fa-search"></i>
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ {/* PC / TABLET */}
+ <div className="mt-10 hidden justify-center lg:flex">
+ <div className="relative grid grid-rows-2 items-center md:flex md:h-[760px] md:w-[80%] md:justify-between">
+ <div className="row-start-2 flex h-full flex-col gap-7 md:w-[55%] md:justify-center">
+ <h1 className="w-[85%] font-outfit font-extrabold md:text-[45px] line-clamp-2">
+ {data.title.english || data.title.romaji || data.title.native}
+ </h1>
+ <div className="font-roboto font-light md:text-[24px] line-clamp-5">
+ {ReactHtmlParser(data.description)}
+ </div>
+
+ <div className="md:pt-5">
+ <Link
+ href={`/anime/${data.id}`}
+ legacyBehavior
+ className="flex"
+ >
+ <a className="rounded-sm p-3 font-karla font-light ring-1 ring-[#FF7F57]">
+ START WATCHING
+ </a>
+ </Link>
+ </div>
+ </div>
+ <div className="z-10 row-start-1 flex justify-center ">
+ <div className="relative md:h-[662px] md:w-[460px] md:scale-100">
+ <div className="absolute bg-gradient-to-t from-[#141519] to-transparent md:h-[662px] md:w-[460px]" />
+
+ <Image
+ draggable={false}
+ src={data.coverImage.extraLarge}
+ alt={data.title.english || data.title.romaji}
+ width={460}
+ height={662}
+ priority
+ className="rounded-tl-xl rounded-tr-xl object-cover bg-blend-overlay md:h-[662px] md:w-[460px] "
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+
+ {/* Mobile */}
+
+ <div className="mt-16 flex flex-col items-center">
+ <div className="w-screen flex-none lg:w-[87%]">
+ {recently && (
+ <div>
+ <div className="flex items-center gap-5 lg:gap-10">
+ <h1 className="px-5 font-outfit text-[20px] font-extrabold lg:text-[27px]">
+ Recently Watched
+ </h1>
+ <div
+ className="cursor-pointer font-outfit font-light text-[#8f8f8f]"
+ onClick={() => handleRemove()}
+ >
+ Clear all
+ </div>
+ </div>
+ <div className="relative z-10 flex items-center py-10 lg:gap-2">
+ <MdChevronLeft
+ onClick={slideLeft}
+ size={40}
+ className="mb-5 cursor-pointer opacity-50 hover:opacity-100"
+ />
+ <div
+ id="recentslider"
+ className="scroll flex h-full w-full items-center overflow-x-scroll scroll-smooth whitespace-nowrap overflow-y-hidden scrollbar-hide lg:gap-5"
+ >
+ {recently.map((anime, index) => {
+ const url = encodeURIComponent(anime.title);
+
+ return (
+ <Link
+ href={`/anime/${anime.id}`}
+ key={index}
+ className="shrink-0 "
+ >
+ <Image
+ src={anime.image}
+ alt={anime.title || "cover image"}
+ width={209}
+ height={300}
+ className="z-20 h-[230px] w-[168px] object-cover p-2 duration-300 ease-in-out hover:scale-105 lg:h-[290px] lg:w-[209px]"
+ />
+ </Link>
+ );
+ })}
+ </div>
+ <MdChevronRight
+ onClick={slideRight}
+ size={40}
+ className="mb-5 cursor-pointer opacity-50 hover:opacity-100"
+ />
+ </div>
+ </div>
+ )}
+
+ {detail && (
+ <Content
+ ids="trendingAnime"
+ section="Trending Now"
+ data={detail.data}
+ />
+ )}
+
+ {popular && (
+ <Content
+ ids="popularAnime"
+ section="Popular Anime"
+ data={popular}
+ />
+ )}
+ </div>
+ </div>
+ </div>
+ <Footer />
+ </>
+ );
+}
+
+export async function getServerSideProps({ req, res }) {
+ const trendingDetail = await aniListData({
+ sort: "TRENDING_DESC",
+ page: 1,
+ });
+ const popularDetail = await aniListData({
+ sort: "POPULARITY_DESC",
+ page: 1,
+ });
+ const genreDetail = await aniListData({ sort: "TYPE", page: 1 });
+ const newTrend = await trendingDetail.props;
+ const trends = newTrend.data[0];
+ const topDesc = trends.description.slice(0, 350) + "...";
+
+ return {
+ props: {
+ topDesc: topDesc,
+ genre: genreDetail.props,
+ detail: trendingDetail.props,
+ populars: popularDetail.props,
+ },
+ };
+}
diff --git a/pages/manga/chapter/[chapter].js b/pages/manga/chapter/[chapter].js
new file mode 100644
index 0000000..8d2a5a1
--- /dev/null
+++ b/pages/manga/chapter/[chapter].js
@@ -0,0 +1,423 @@
+import axios from "axios";
+import Head from "next/head";
+import { useEffect, useState } from "react";
+import Navbar from "../../../components/navbar";
+import Footer from "../../../components/footer";
+import ScrollTracker from "../../../components/scrollTracker";
+
+export default function Test({ title, id, aniId, data, provider }) {
+ const [isLoading, setIsLoading] = useState(true);
+ const [datas, setData] = useState(data);
+ const [currentChapter, setCurrentChapter] = useState(null);
+ const [nextChapter, setNextChapter] = useState(null);
+ const [prevChapter, setPrevChapter] = useState(null);
+
+ useEffect(() => {
+ function storedData() {
+ const nowChap = localStorage.getItem("currentChapterId");
+ const chapt = localStorage.getItem("chapters");
+ const chapters = JSON.parse(chapt);
+ const currentChapter = chapters.find((chapter) => chapter.id === nowChap);
+ const currentIndex = chapters.findIndex(
+ (chapter) => chapter.id === nowChap
+ );
+
+ const nextIndex = currentIndex + 1;
+
+ const nextChapter = chapters[nextIndex];
+ const prevIndex = currentIndex - 1;
+
+ setNextChapter(nextChapter);
+ setCurrentChapter(currentChapter);
+ setPrevChapter(chapters[prevIndex]);
+ setIsLoading(false);
+ }
+ storedData();
+ }, []);
+
+ function getNextChapter() {
+ window.scrollTo(0, 0);
+ setIsLoading(true);
+ const currentId = localStorage.getItem("currentChapterId");
+ const scrollData = JSON.parse(localStorage.getItem("watchedManga")) || [];
+ const scroll = localStorage.getItem("scrollPercentage");
+ if (scroll >= 5) {
+ const existingDataIndex = scrollData.findIndex(
+ (data) => data.id === currentId
+ );
+ if (existingDataIndex !== -1) {
+ // Update existing data
+ scrollData[existingDataIndex].timestamp = Date.now();
+ scrollData[existingDataIndex].percentage = parseFloat(
+ localStorage.getItem("scrollPercentage")
+ );
+ } else {
+ // Add new data
+ scrollData.push({
+ timestamp: Date.now(),
+ percentage: parseFloat(localStorage.getItem("scrollPercentage")),
+ id: currentId,
+ });
+ }
+
+ localStorage.setItem("watchedManga", JSON.stringify(scrollData));
+
+ const chapt = localStorage.getItem("chapters");
+ const chapters = JSON.parse(chapt);
+
+ const currentIndex = chapters.findIndex(
+ (chapter) => chapter.id === currentId
+ );
+
+ const nextIndex = currentIndex + 1;
+
+ const nextChapter = chapters[nextIndex];
+ const nexttChapter = chapters[nextIndex + 1];
+ const prevChapter = chapters[nextIndex - 1];
+
+ setNextChapter(nexttChapter);
+ setCurrentChapter(nextChapter);
+ setPrevChapter(prevChapter);
+
+ if (!nextChapter) {
+ return;
+ }
+
+ fetch(
+ `https://api.moopa.my.id/meta/anilist-manga/read?chapterId=${nextChapter.id}&provider=${provider}`
+ )
+ .then((response) => response.json())
+ .then((data) => {
+ if (provider === "mangakakalot") {
+ const datas = data.map((item) => ({
+ img: `https://api.consumet.org/utils/image-proxy?url=${item.img}&referer={Referer:'https://mangakakalot.com/'}`,
+ page: item.page,
+ title: item.title,
+ }));
+ // console.log(datas);
+ setData(datas);
+ } else {
+ setData(data);
+ }
+ })
+ .finally(() => {
+ setIsLoading(false);
+ });
+
+ // Update the current chapter id in local storage
+ localStorage.setItem("currentChapterId", nextChapter.id);
+ }
+
+ const chapt = localStorage.getItem("chapters");
+ const chapters = JSON.parse(chapt);
+
+ const currentIndex = chapters.findIndex(
+ (chapter) => chapter.id === currentId
+ );
+
+ const nextIndex = currentIndex + 1;
+
+ const nextChapter = chapters[nextIndex];
+ const nexttChapter = chapters[nextIndex + 1];
+ const prevChapter = chapters[nextIndex - 1];
+
+ setNextChapter(nexttChapter);
+ setCurrentChapter(nextChapter);
+ setPrevChapter(prevChapter);
+
+ if (!nextChapter) {
+ return;
+ }
+
+ fetch(
+ `https://api.moopa.my.id/meta/anilist-manga/read?chapterId=${nextChapter.id}&provider=${provider}`
+ )
+ .then((response) => response.json())
+ .then((data) => {
+ if (provider === "mangakakalot") {
+ const datas = data.map((item) => ({
+ img: `https://api.consumet.org/utils/image-proxy?url=${item.img}&referer={Referer:'https://mangakakalot.com/'}`,
+ page: item.page,
+ title: item.title,
+ }));
+ // console.log(datas);
+ setData(datas);
+ } else {
+ setData(data);
+ }
+ })
+ .finally(() => {
+ setIsLoading(false);
+ });
+
+ // Update the current chapter id in local storage
+ localStorage.setItem("currentChapterId", nextChapter.id);
+ }
+ // console.log(data);
+
+ function getPrevChapter() {
+ // Get the current id
+ window.scrollTo(0, 0);
+ setIsLoading(true);
+
+ const currentId = localStorage.getItem("currentChapterId");
+ const scrollData = JSON.parse(localStorage.getItem("watchedManga")) || [];
+ const scroll = localStorage.getItem("scrollPercentage");
+ if (scroll >= 5) {
+ const existingDataIndex = scrollData.findIndex(
+ (data) => data.id === currentId
+ );
+ if (existingDataIndex !== -1) {
+ // Update existing data
+ scrollData[existingDataIndex].timestamp = Date.now();
+ scrollData[existingDataIndex].percentage = parseFloat(
+ localStorage.getItem("scrollPercentage")
+ );
+ } else {
+ // Add new data
+ scrollData.push({
+ timestamp: Date.now(),
+ percentage: parseFloat(localStorage.getItem("scrollPercentage")),
+ id: currentId,
+ });
+ }
+
+ localStorage.setItem("watchedManga", JSON.stringify(scrollData));
+
+ const chapt = localStorage.getItem("chapters");
+ const chapters = JSON.parse(chapt);
+
+ const currentIndex = chapters.findIndex(
+ (chapter) => chapter.id === currentId
+ );
+
+ const nextIndex = currentIndex + 1;
+
+ const nextChapter = chapters[nextIndex];
+ const nexttChapter = chapters[nextIndex + 1];
+ const prevChapter = chapters[nextIndex - 1];
+
+ setNextChapter(nexttChapter);
+ setCurrentChapter(nextChapter);
+ setPrevChapter(prevChapter);
+
+ if (!nextChapter) {
+ return;
+ }
+
+ fetch(
+ `https://api.moopa.my.id/meta/anilist-manga/read?chapterId=${nextChapter.id}&provider=${provider}`
+ )
+ .then((response) => response.json())
+ .then((data) => {
+ if (provider === "mangakakalot") {
+ const datas = data.map((item) => ({
+ img: `https://api.consumet.org/utils/image-proxy?url=${item.img}&referer={Referer:'https://mangakakalot.com/'}`,
+ page: item.page,
+ title: item.title,
+ }));
+ // console.log(datas);
+ setData(datas);
+ } else {
+ setData(data);
+ }
+ })
+ .finally(() => {
+ setIsLoading(false);
+ });
+
+ // Update the current chapter id in local storage
+ localStorage.setItem("currentChapterId", nextChapter.id);
+ }
+
+ const chapt = localStorage.getItem("chapters");
+ const chapters = JSON.parse(chapt);
+
+ const currentIndex = chapters.findIndex(
+ (chapter) => chapter.id === currentId
+ );
+
+ const prevIndex = currentIndex - 1;
+
+ const prevChapter = chapters[prevIndex];
+ const nextChapter = chapters[prevIndex + 1];
+ const prevvChapter = chapters[prevIndex - 1];
+ setCurrentChapter(prevChapter);
+ setNextChapter(nextChapter);
+ setPrevChapter(prevvChapter);
+
+ fetch(
+ `https://api.moopa.my.id/meta/anilist-manga/read?chapterId=${prevChapter.id}&provider=${provider}`
+ )
+ .then((response) => response.json())
+ .then((data) => {
+ // console.log(data);
+ if (provider === "mangakakalot") {
+ const datas = data.map((item) => ({
+ img: `https://api.consumet.org/utils/image-proxy?url=${item.img}&referer={Referer:'https://mangakakalot.com/'}`,
+ page: item.page,
+ title: item.title,
+ }));
+ // console.log(datas);
+ setData(datas);
+ } else {
+ setData(data);
+ }
+ })
+ .finally(() => {
+ setIsLoading(false);
+ });
+
+ // Update the current chapter id in local storage
+ localStorage.setItem("currentChapterId", prevChapter.id);
+ }
+ // console.log({ PREV_CHAPTER: prevChapter });
+ // console.log({ CURRENT_CHAPTER: currentChapter });
+ // console.log({ NEXT_CHAPTER: nextChapter });
+
+ return (
+ <>
+ <Head>
+ <title>{title}</title>
+ <meta name="info" content="More detailed info about the Manga" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <link rel="icon" href="/c.svg" />
+ </Head>
+ <Navbar />
+ <ScrollTracker data={currentChapter} id={aniId} />
+
+ <div className="flex h-full min-h-screen w-screen flex-col items-center text-white">
+ <div className="lg:pt-nav pt-5 text-3xl font-semibold">
+ {isLoading ? (
+ <div />
+ ) : currentChapter && currentChapter.chapter ? (
+ <p>Chapter {currentChapter.chapter}</p>
+ ) : (
+ <p>{currentChapter.title}</p>
+ )}
+ </div>
+
+ {isLoading ? (
+ <p className="pt-nav">Loading...</p>
+ ) : (
+ <div className="pointer-events-none z-10 flex min-h-screen w-screen flex-col items-center pt-nav">
+ {datas.length > 0 &&
+ datas.map((item, index) => (
+ <img
+ draggable={false}
+ key={index}
+ src={item.img}
+ alt={`Page ${item.page}`}
+ />
+ ))}
+ </div>
+ )}
+ {isLoading ? (
+ <div />
+ ) : (
+ <>
+ <div className="sticky bottom-9 z-40 my-5 mr-[20rem] flex gap-6 lg:mr-0 lg:gap-10">
+ <div
+ className={`cursor-pointer rounded-md bg-[#121212] p-2 text-center text-xl hover:bg-[#2d303a] ${
+ prevChapter ? `` : `pointer-events-none`
+ }`}
+ onClick={getPrevChapter}
+ >
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ fill="none"
+ viewBox="0 0 24 24"
+ strokeWidth="1.5"
+ stroke={`${prevChapter ? `currentColor` : `#919191`}`}
+ className="h-6 w-6"
+ >
+ <path
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ d="M15.75 19.5L8.25 12l7.5-7.5"
+ />
+ </svg>
+ </div>
+ <div
+ className={`cursor-pointer ${
+ nextChapter ? `` : `pointer-events-none`
+ } rounded-md bg-[#121212] p-2 text-center text-xl hover:bg-[#2d303a]`}
+ onClick={getNextChapter}
+ >
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ viewBox="0 0 24 24"
+ fill="none"
+ strokeWidth="1.5"
+ stroke={`${nextChapter ? `currentColor` : `#919191`}`}
+ className={`h-6 w-6`}
+ >
+ <path
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ d="M8.25 4.5l7.5 7.5-7.5 7.5"
+ />
+ </svg>
+ </div>
+ </div>
+
+ <div className="z-30 w-screen bg-[#1e2023] px-5 py-[3px] text-xl lg:w-[30%] lg:bg-transparent lg:text-center">
+ {nextChapter ? (
+ nextChapter.chapter ? (
+ <p>Next Chapter {nextChapter.chapter}</p>
+ ) : (
+ <p>Next Chapter {nextChapter.title}</p>
+ )
+ ) : (
+ <p>End of Chapter</p>
+ )}
+ </div>
+ </>
+ )}
+ </div>
+ <Footer />
+ </>
+ );
+}
+
+export async function getServerSideProps(context) {
+ const { id, title, provider, aniId } = context.query;
+ const urls = [
+ `https://api.moopa.my.id/meta/anilist-manga/read?chapterId=${id}&provider=${provider}`,
+ ];
+ const results = await axios.get(urls);
+
+ if (provider === "mangakakalot") {
+ const data = results.data;
+ const datas = data.map((item) => ({
+ img: `https://api.consumet.org/utils/image-proxy?url=${item.img}&referer={Referer:'https://mangakakalot.com/'}`,
+ page: item.page,
+ title: item.title,
+ }));
+ return {
+ props: {
+ aniId,
+ id,
+ title,
+ data: datas,
+ provider,
+ },
+ };
+ }
+
+ if (!results.data) {
+ return {
+ notFound: true,
+ };
+ }
+
+ return {
+ props: {
+ aniId,
+ id,
+ title,
+ data: results.data,
+ provider,
+ },
+ };
+}
diff --git a/pages/manga/detail/id.js b/pages/manga/detail/id.js
new file mode 100644
index 0000000..cfc7076
--- /dev/null
+++ b/pages/manga/detail/id.js
@@ -0,0 +1,318 @@
+import axios from "axios";
+import Head from "next/head";
+import Image from "next/image";
+import Link from "next/link";
+import { useEffect, useState } from "react";
+import Layout from "../../../components/layout";
+
+// { `Don't touch this` }
+
+// import { useUser } from "@auth0/nextjs-auth0/client";
+// import {
+// arrayRemove,
+// arrayUnion,
+// collection,
+// doc,
+// getDoc,
+// getDocs,
+// setDoc,
+// updateDoc,
+// } from "firebase/firestore";
+// import db from "../../../lib/firebase";
+
+const options = [
+ "mangadex",
+ "mangahere",
+ "mangakakalot",
+ "mangapark",
+ "mangapill",
+ "mangareader",
+ "mangasee123",
+];
+
+export default function MangaDetail({ data, manga, aniId, provider }) {
+ const [selectOption, setSelectedOption] = useState(options[0]);
+ const [recentWatch, setRecentWatch] = useState();
+
+ // const { user, error, loading } = useUser();
+
+ // if (loading) return <div>Loading...</div>;
+ // if (error) return <div>{error.message}</div>;
+
+ const [load, setLoad] = useState(true);
+
+ useEffect(() => {
+ // async function firebase() {
+ // const colRef = collection(db, "user");
+
+ // const snapshots = await getDocs(colRef);
+
+ // const docs = snapshots.docs.map((doc) => {
+ // const data = doc.data();
+ // data.id = doc.id;
+ // return data;
+ // });
+
+ // // console.log(docs);
+ // }
+ function getRecent() {
+ const recentWatch = JSON.parse(localStorage.getItem("watchedManga"))?.map(
+ (data) => data.id
+ );
+ setRecentWatch(recentWatch);
+ }
+ getRecent();
+ // firebase();
+ }, []);
+
+ function handleLoad() {
+ setLoad(false);
+ }
+
+ const relation = data?.relations.filter(
+ (relation) => relation.malId !== null
+ );
+
+ const mangan = JSON.stringify(manga);
+
+ async function clickDeez(props) {
+ localStorage.setItem("chapters", mangan);
+ localStorage.setItem("currentChapterId", props);
+
+ // const colRef = collection(db, "user");
+ // const docRef = doc(colRef, user.sub);
+
+ // const docMangaRef = collection(docRef, "watchedManga");
+ // const watchedMangaRef = doc(docMangaRef, data.id);
+
+ // const watchedMangaSnap = await getDoc(watchedMangaRef);
+
+ // if (watchedMangaSnap.exists()) {
+ // // Update existing chapter
+ // await updateDoc(watchedMangaRef, {
+ // chapter: arrayUnion({
+ // id: props,
+ // scrollPercentage: 0,
+ // timeStamp: null,
+ // }),
+ // });
+ // // console.log("Chapter updated successfully!");
+ // } else {
+ // // Add new manga with chapter data
+ // await setDoc(watchedMangaRef, {
+ // id: data.id,
+ // image: data.image,
+ // rating: data.rating,
+ // title: data.title?.romaji || data.title?.english,
+ // chapter: [props],
+ // });
+ // // console.log("Manga added successfully with chapter data!");
+ // }
+ }
+
+ // console.log(data.id);
+ // console.log(mangan);
+ return (
+ <>
+ <Head>
+ <title>{data.title?.english || data.title.romaji}</title>
+ <meta name="info" content="More detailed info about the Manga" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <link rel="icon" href="/c.svg" />
+ </Head>
+ <div className="w-screen">
+ <Layout>
+ <div className="flex w-screen justify-center pt-[10rem] ">
+ {data ? (
+ <div className="flex w-[85%] flex-col gap-10 ">
+ <div className="flex gap-10">
+ <div className="relative h-[358px] w-[230px]">
+ <Image
+ src={data.image}
+ alt="CoverImage"
+ fill
+ className="object-cover"
+ />
+ </div>
+ <div className="flex w-[77%] flex-col gap-10">
+ <h1 className="font-karla text-3xl font-bold">
+ {data.title?.english}
+ </h1>
+ <p dangerouslySetInnerHTML={{ __html: data.description }} />
+ </div>
+ </div>
+ {relation?.length > 0 ? (
+ <div className="p-3 lg:p-0">
+ <h1 className="items-start py-5 text-2xl font-bold">
+ Relations
+ </h1>
+ <div
+ className={`grid grid-cols-1 justify-items-center py-5 px-5 lg:grid-cols-3 ${
+ load ? "h-[290px] overflow-y-clip" : ""
+ }`}
+ >
+ {relation &&
+ relation.map((relation, index) => {
+ return (
+ <div key={index} className="w-full gap-6 p-5 ">
+ <Link
+ href={
+ relation.type === "MANGA" ||
+ relation.type === "NOVEL"
+ ? `/manga/detail/id?aniId=${relation.id}`
+ : `/anime/${relation.id}`
+ }
+ className={`flex w-full justify-between rounded-md bg-[#282828] p-2 shadow-lg duration-300 ease-out hover:scale-105 ${
+ relation.type === "TV" ||
+ relation.type === "OVA" ||
+ relation.type === "MOVIE" ||
+ relation.type === "SPECIAL" ||
+ relation.type === "ONA" ||
+ relation.type === "MANGA" ||
+ relation.type === "TV_SHORT"
+ ? ``
+ : "pointer-events-none"
+ }`}
+ >
+ <div className="flex flex-col justify-between">
+ <div className="font-bold text-[#FF7F57]">
+ {relation.relationType}
+ </div>
+ <div className="text-lg font-bold text-white">
+ {relation.title.romaji ||
+ relation.title.english ||
+ relation.title.userPreferred}
+ </div>
+ <div className="flex">
+ <p className="dynamic-text rounded-lg p-1 font-outfit text-sm font-semibold">
+ {relation.type}
+ </p>
+ </div>
+ </div>
+ <div className="relative h-[200px] w-[140px] shrink-0">
+ <Image
+ fill
+ src={relation.image}
+ alt={`Cover Image for ${relation.title}`}
+ className=" bg-slate-400 object-cover"
+ />
+ </div>
+ </Link>
+ </div>
+ );
+ })}
+ </div>
+ {relation.length > 3 && (
+ <button
+ type="button"
+ className="w-full"
+ onClick={handleLoad}
+ >
+ {load ? "Load More" : ""}
+ </button>
+ )}
+ </div>
+ ) : (
+ <p>No Relations</p>
+ )}
+ <div className="flex flex-col gap-10">
+ <h1 className="text-3xl font-bold">Chapters</h1>
+ <div className="flex h-[640px] flex-col gap-10 overflow-y-scroll scrollbar-thin scrollbar-thumb-slate-800 scrollbar-thumb-rounded-full hover:scrollbar-thumb-slate-600">
+ {manga?.map((chapter, index) => {
+ return (
+ <div
+ key={index}
+ onClick={() => clickDeez(chapter.id)}
+ className={`${
+ recentWatch?.includes(chapter.id)
+ ? "text-gray-400"
+ : ""
+ }`}
+ >
+ <Link
+ // href="#"
+ href={`/manga/chapter/[chapter]`}
+ as={`/manga/chapter/read?id=${chapter.id}&title=${
+ data.title?.english || data.title?.romaji
+ }&provider=${provider}&aniId=${aniId}`}
+ >
+ {typeof chapter.title === "string" &&
+ !isNaN(Number(chapter.title)) ? (
+ <div>Chapter {Number(chapter.title)}</div>
+ ) : (
+ <div>
+ {chapter.chapter ? (
+ <p>Chapter {chapter.chapter}</p>
+ ) : (
+ <p>{chapter.title}</p>
+ )}
+ </div>
+ )}
+ </Link>
+ </div>
+ );
+ })}
+ </div>
+ </div>
+ </div>
+ ) : (
+ <p>Oops no data found :(</p>
+ )}
+ </div>
+ </Layout>
+ </div>
+ </>
+ );
+}
+
+export const getServerSideProps = async (context) => {
+ context.res.setHeader("Cache-Control", "public, max-age=3600");
+ const { aniId, aniTitle } = context.query;
+ const prv = "mangapill";
+
+ try {
+ const info = await axios.get(
+ `https://api.moopa.my.id/meta/anilist-manga/info/${aniId}?provider=${prv}`
+ );
+ const result = info.data;
+ const manga = result.chapters;
+
+ return {
+ props: {
+ data: result,
+ aniId: aniId,
+ provider: prv,
+ manga,
+ },
+ };
+ } catch (error) {
+ if (error.response && error.response.status === 404) {
+ try {
+ const prv = "mangakakalot";
+ const manga = await axios.get(
+ `https://api.moopa.my.id/meta/anilist-manga/info/${aniId}?provider=${prv}`
+ );
+ const results = manga.data;
+
+ return {
+ props: {
+ data: results,
+ aniId: aniId,
+ manga: results.chapters,
+ provider: prv,
+ },
+ };
+ } catch (error) {
+ console.error(error);
+ return {
+ notFound: true,
+ };
+ }
+ } else {
+ console.error(error);
+ return {
+ notFound: true,
+ };
+ }
+ }
+};
diff --git a/pages/search.js b/pages/search.js
new file mode 100644
index 0000000..87f9cdc
--- /dev/null
+++ b/pages/search.js
@@ -0,0 +1,430 @@
+import { useEffect, useRef, useState } from "react";
+import { AnimatePresence, motion as m } from "framer-motion";
+import Skeleton, { SkeletonTheme } from "react-loading-skeleton";
+import { useRouter } from "next/router";
+import "react-loading-skeleton/dist/skeleton.css";
+import Link from "next/link";
+import Navbar from "../components/navbar";
+import Head from "next/head";
+import Footer from "../components/footer";
+
+import { useAniList } from "../lib/useAnilist";
+
+const genre = [
+ "Action",
+ "Adventure",
+ "Cars",
+ "Comedy",
+ "Drama",
+ "Fantasy",
+ "Horror",
+ "Mahou Shoujo",
+ "Mecha",
+ "Music",
+ "Mystery",
+ "Psychological",
+ "Romance",
+ "Sci-Fi",
+ "Slice of Life",
+ "Sports",
+ "Supernatural",
+ "Thriller",
+];
+
+const types = ["ANIME", "MANGA"];
+
+const sorts = [
+ "POPULARITY_DESC",
+ "POPULARITY",
+ "TRENDING_DESC",
+ "TRENDING",
+ "UPDATED_AT_DESC",
+ "UPDATED_AT",
+ "START_DATE_DESC",
+ "START_DATE",
+ "END_DATE_DESC",
+ "END_DATE",
+ "FAVOURITES_DESC",
+ "FAVOURITES",
+ "SCORE_DESC",
+ "SCORE",
+ "TITLE_ROMAJI_DESC",
+ "TITLE_ROMAJI",
+ "TITLE_ENGLISH_DESC",
+ "TITLE_ENGLISH",
+ "TITLE_NATIVE_DESC",
+ "TITLE_NATIVE",
+ "EPISODES_DESC",
+ "EPISODES",
+ "ID",
+ "ID_DESC",
+];
+
+export default function Card() {
+ const router = useRouter();
+ // const { genres } = router.query;
+ // console.log(genres);
+
+ const { aniAdvanceSearch } = useAniList();
+
+ const [data, setData] = useState();
+ const [loading, setLoading] = useState(true);
+ // const [selectedGenre, setSelectedGenre] = useState(null);
+ // const [selectedType, setSelectedType] = useState(type[0]);
+ // const [selectedSort, setSelectedSort] = useState(null);
+
+ const { hasil } = router.query;
+
+ const [search, setQuery] = useState(hasil || null);
+ const [type, setSelectedType] = useState("ANIME");
+ const [seasonYear, setSeasonYear] = useState();
+ const [season, setSeason] = useState();
+ const [genres, setSelectedGenre] = useState();
+ const [perPage, setPerPage] = useState(25);
+ const [sort, setSelectedSort] = useState(["POPULARITY_DESC"]);
+
+ const [isVisible, setIsVisible] = useState(false);
+
+ // const [query, setQuery] = useState(hasil || null);
+ const inputRef = useRef(null);
+
+ async function advance() {
+ setLoading(true);
+ const data = await aniAdvanceSearch(
+ search,
+ type,
+ seasonYear,
+ season,
+ genres,
+ perPage,
+ sort
+ );
+ setData(data);
+ setLoading(false);
+ }
+
+ useEffect(() => {
+ advance();
+ }, [search, type, seasonYear, season, genres, perPage, sort]);
+
+ console.log(data);
+
+ // useEffect(() => {
+ // async function fetchData() {
+ // setLoading(true);
+ // try {
+ // const res = await fetch(
+ // `https://api.moopa.my.id/meta/anilist/advanced-search?${
+ // query ? `query=${query}&` : ""
+ // }${selectedGenre ? `genres=["${selectedGenre}"]&` : ""}${
+ // selectedType ? `type=${selectedType}&` : ""
+ // }${selectedSort ? `sort=["${selectedSort}"]` : ""}`
+ // );
+ // const data = await res.json();
+ // setData(data);
+ // setLoading(false);
+ // } catch (e) {
+ // console.error(e);
+ // }
+ // }
+ // fetchData();
+ // }, [query, selectedGenre, selectedType, selectedSort]);
+
+ const handleKeyDown = async (event) => {
+ if (event.key === "Enter") {
+ event.preventDefault();
+ const inputValue = event.target.value;
+ setQuery(inputValue);
+ }
+ };
+
+ function trash() {
+ setQuery(null);
+ inputRef.current.value = "";
+ setSelectedGenre(null);
+ setSelectedSort(["POPULARITY_DESC"]);
+ }
+
+ function handleVisible() {
+ setIsVisible(!isVisible);
+ }
+
+ return (
+ <>
+ <Head>
+ <title>Moopa - search</title>
+ <link rel="icon" href="/c.svg" />
+ </Head>
+ <Navbar />
+ <div className="min-h-screen m-10 text-white items-center gap-5 lg:gap-0 flex flex-col">
+ <div className="w-screen px-10 lg:w-[80%] lg:h-[10rem] flex text-center lg:items-end lg:pb-16 justify-center lg:gap-10 gap-3 font-karla font-light">
+ <div className="text-start">
+ <h1 className="font-bold lg:pb-5 pb-3 hidden lg:block text-md pl-1 font-outfit">
+ TITLE
+ </h1>
+ <input
+ className="lg:w-[297px] lg:h-[46px] h-[35px] w-[230px] xs:w-[280px] bg-[#26272B] rounded-[10px] font-karla font-light text-[#ffffff89] text-center"
+ placeholder="search here..."
+ type="text"
+ onKeyDown={handleKeyDown}
+ ref={inputRef}
+ />
+ </div>
+
+ {/* TYPE */}
+ <div className="hidden lg:block text-start">
+ <h1 className="font-bold pb-5 text-md pl-1 font-outfit">TYPE</h1>
+ <select
+ className="w-[297px] h-[46px] bg-[#26272B] rounded-[10px] justify-between pl-[7.5rem] pr-5 flex items-center"
+ value={type}
+ onChange={(e) => setSelectedType(e.target.value)}
+ >
+ {types.map((option) => (
+ <option key={option} value={option}>
+ {option}
+ </option>
+ ))}
+ </select>
+ </div>
+
+ {/* SORT */}
+ <div className="hidden lg:block text-start">
+ <h1 className="font-bold pb-5 text-md pl-1 font-outfit">SORT</h1>
+ <select
+ className="w-[297px] h-[46px] bg-[#26272B] rounded-[10px] flex items-center text-center"
+ onChange={(e) => setSelectedSort(e.target.value)}
+ >
+ <option value="">Sort By</option>
+ {sorts.map((option) => (
+ <option key={option} value={option}>
+ {option}
+ </option>
+ ))}
+ </select>
+ </div>
+
+ {/* OPTIONS */}
+ <div className="flex lg:gap-7 text-center gap-3 items-end">
+ <div
+ className="lg:w-[73px] w-[50px] lg:h-[46px] h-[35px] bg-[#26272B] rounded-[10px] justify-center flex items-center cursor-pointer hover:bg-[#2F3136] transition-all duration-300"
+ onClick={handleVisible}
+ >
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ fill="none"
+ viewBox="0 0 24 24"
+ strokeWidth={1.5}
+ stroke="currentColor"
+ className="w-6 h-6"
+ >
+ <path
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ d="M10.5 6h9.75M10.5 6a1.5 1.5 0 11-3 0m3 0a1.5 1.5 0 10-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-9.75 0h9.75"
+ />
+ </svg>
+ </div>
+
+ {/* TRASH ICON */}
+ <div
+ className="lg:w-[73px] w-[50px] lg:h-[46px] h-[35px] bg-[#26272B] rounded-[10px] justify-center flex items-center cursor-pointer hover:bg-[#2F3136] transition-all duration-300"
+ onClick={trash}
+ >
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ fill="none"
+ viewBox="0 0 24 24"
+ strokeWidth={1.5}
+ stroke="currentColor"
+ className="w-6 h-6"
+ >
+ <path
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"
+ />
+ </svg>
+ </div>
+ </div>
+ </div>
+ <div className="w-screen lg:w-[64%] flex lg:justify-end lg:pl-0">
+ <AnimatePresence>
+ {isVisible && (
+ <m.div
+ key="imagine"
+ initial={{ opacity: 0, y: -10 }}
+ animate={{ opacity: 1, y: 0 }}
+ exit={{ opacity: 0, y: -10 }}
+ className="lg:pb-16"
+ >
+ <div className="text-start items-center lg:items-start flex w-screen lg:w-auto px-8 lg:px-0 flex-row justify-between lg:flex-col pb-5 ">
+ <h1 className="font-bold lg:pb-5 text-md pl-1 font-outfit">
+ GENRE
+ </h1>
+ <select
+ className="w-[195px] lg:w-[297px] lg:h-[46px] h-[35px] bg-[#26272B] rounded-[10px] flex items-center text-center cursor-pointer hover:bg-[#2F3136] transition-all duration-300"
+ onChange={(e) => setSelectedGenre(e.target.value)}
+ >
+ <option value="">Select a Genre</option>
+ {genre.map((option) => (
+ <option key={option} value={option}>
+ {option}
+ </option>
+ ))}
+ </select>
+ </div>
+ <div className="lg:hidden text-start items-center lg:items-start flex w-screen lg:w-auto px-8 lg:px-0 flex-row justify-between lg:flex-col pb-5 ">
+ <h1 className="font-bold lg:pb-5 text-md pl-1 font-outfit">
+ TYPE
+ </h1>
+ <select
+ className="w-[195px] h-[35px] bg-[#26272B] rounded-[10px] flex items-center text-center cursor-pointer hover:bg-[#2F3136] transition-all duration-300"
+ value={type}
+ onChange={(e) => setSelectedType(e.target.value)}
+ >
+ {types.map((option) => (
+ <option key={option} value={option}>
+ {option}
+ </option>
+ ))}
+ </select>
+ </div>
+
+ <div className="lg:hidden text-start items-center lg:items-start flex w-screen lg:w-auto px-8 lg:px-0 flex-row justify-between lg:flex-col ">
+ <h1 className="font-bold lg:pb-5 text-md pl-1 font-outfit">
+ SORT
+ </h1>
+ <select
+ className="w-[195px] h-[35px] bg-[#26272B] rounded-[10px] flex items-center text-center cursor-pointer hover:bg-[#2F3136] transition-all duration-300"
+ onChange={(e) => setSelectedSort(e.target.value)}
+ >
+ <option value="">Sort By</option>
+ {sort.map((option) => (
+ <option key={option} value={option}>
+ {option}
+ </option>
+ ))}
+ </select>
+ </div>
+ </m.div>
+ )}
+ </AnimatePresence>
+ </div>
+ <AnimatePresence>
+ <div
+ key="card-keys"
+ className="grid pt-3 lg:grid-cols-5 justify-items-center grid-cols-3 w-screen px-2 lg:w-auto lg:gap-10 gap-2 lg:gap-y-24 gap-y-12 overflow-hidden"
+ >
+ {loading ? (
+ <>
+ <SkeletonTheme baseColor="#3B3C41" highlightColor="#4D4E52">
+ <div
+ className="flex flex-col w-[115px] xs:w-[140px] lg:w-[228px] gap-5"
+ style={{ scale: 0.98 }}
+ >
+ <Skeleton className="lg:h-[313px] xs:h-[215px] h-[175px]" />
+ <Skeleton width={110} height={30} />
+ </div>
+ <div
+ className="flex flex-col w-[115px] xs:w-[140px] lg:w-[228px] gap-5"
+ style={{ scale: 0.98 }}
+ >
+ <Skeleton className="lg:h-[313px] xs:h-[215px] h-[175px]" />
+ <Skeleton width={110} height={30} />
+ </div>
+ <div
+ className="flex flex-col w-[115px] xs:w-[140px] lg:w-[228px] gap-5"
+ style={{ scale: 0.98 }}
+ >
+ <Skeleton className="lg:h-[313px] xs:h-[215px] h-[175px]" />
+ <Skeleton width={110} height={30} />
+ </div>
+ <div
+ className="flex flex-col w-[115px] xs:w-[140px] lg:w-[228px] gap-5"
+ style={{ scale: 0.98 }}
+ >
+ <Skeleton className="lg:h-[313px] xs:h-[215px] h-[175px]" />
+ <Skeleton width={110} height={30} />
+ </div>
+ <div
+ className="flex flex-col w-[115px] xs:w-[140px] lg:w-[228px] gap-5"
+ style={{ scale: 0.98 }}
+ >
+ <Skeleton className="lg:h-[313px] xs:h-[215px] h-[175px]" />
+ <Skeleton width={110} height={30} />
+ </div>
+ <div
+ className="flex flex-col w-[115px] xs:w-[140px] lg:w-[228px] gap-5"
+ style={{ scale: 0.98 }}
+ >
+ <Skeleton className="lg:h-[313px] xs:h-[215px] h-[175px]" />
+ <Skeleton width={110} height={30} />
+ </div>
+ <div
+ className="flex flex-col w-[115px] xs:w-[140px] lg:w-[228px] gap-5"
+ style={{ scale: 0.98 }}
+ >
+ <Skeleton className="lg:h-[313px] xs:h-[215px] h-[175px]" />
+ <Skeleton width={110} height={30} />
+ </div>
+ </SkeletonTheme>
+ </>
+ ) : data && data.media.length === 0 ? (
+ <div className="w-screen text-[#ff7f57] lg:col-start-3 col-start-2 items-center flex justify-center text-center font-bold font-karla lg:text-2xl">
+ Oops!<br></br> Nothing's Found...
+ </div>
+ ) : (
+ data.media.map((anime) => {
+ return (
+ <m.div
+ initial={{ scale: 0.9 }}
+ animate={{ scale: 1, transition: { duration: 0.35 } }}
+ className="w-[115px] xs:w-[140px] lg:w-[228px]"
+ key={anime.id}
+ >
+ <Link
+ href={
+ anime.format === "MANGA" || anime.format === "NOVEL"
+ ? `/manga/detail/id?aniId=${anime.id}&aniTitle=${anime.title.userPreferred}`
+ : `/anime/${anime.id}`
+ }
+ className=""
+ >
+ <div
+ // className=" bg-[#3B3C41] h-[313px] hover:ring-4 ring-[#ff8a57] transition-all cursor-pointer duration-100 ease-in-out rounded-[10px]"
+ className=" bg-[#3B3C41] lg:h-[313px] xs:h-[215px] h-[175px] hover:scale-105 scale-100 transition-all cursor-pointer duration-200 ease-out rounded-[10px]"
+ style={{
+ backgroundImage: `url(${anime.coverImage.extraLarge})`,
+ backgroundSize: "cover",
+ backgroundPosition: "center",
+ }}
+ />
+ </Link>
+ <Link href={`/anime/${anime.id}`}>
+ <h1 className="font-outfit font-bold lg:text-[20px] pt-4 title-overflow">
+ {anime.title.userPreferred}
+ </h1>
+ </Link>
+ <h2 className="font-outfit lg:text-[15px] text-[11px] font-light pt-2 text-[#8B8B8B]">
+ {anime.format || <p>-</p>} &#183;{" "}
+ {anime.status || <p>-</p>} &#183; {anime.episodes || 0}{" "}
+ Episodes
+ </h2>
+ </m.div>
+ );
+ })
+ )}
+ </div>
+ </AnimatePresence>
+ </div>
+ <Footer />
+ </>
+ );
+}
+
+{
+ /* <div className=" w-[228px]">
+ <div className="bg-[#3B3C41] h-[313px] rounded-[10px]" />
+ <h1 className="font-outfit font-extrabold text-[24px]">Anime Title</h1>
+ </div> */
+}
diff --git a/pages/staff.js b/pages/staff.js
new file mode 100644
index 0000000..a38d3e8
--- /dev/null
+++ b/pages/staff.js
@@ -0,0 +1,431 @@
+import Head from "next/head";
+import { useState } from "react";
+import Footer from "../components/footer";
+import Navbar from "../components/navbar";
+import Twitter from "../components/media/twitter";
+import Instagram from "../components/media/instagram";
+import Discord from "../components/media/discord";
+import AniList from "../components/media/aniList";
+
+export default function Staff() {
+ const dev = {
+ name: "Factiven",
+ desc: "“Bawah gw gay”",
+ coverImage:
+ "https://cdn.discordapp.com/attachments/986579286397964290/1058308027075276890/1160925.jpg",
+ iClass: "bg-center rounded-t-lg",
+ socials: {
+ twitter: "https://twitter.com/Factivens",
+ ig: "https://www.instagram.com/dvnabny/",
+ discord: "Factiven#9110",
+ aniList: "https://anilist.co/user/DevanAbinaya/",
+ },
+ };
+
+ const [copied, setCopied] = useState(false);
+
+ const handleClickDev = () => {
+ if (navigator.clipboard) {
+ navigator.clipboard.writeText(dev.socials.discord).then(() => {
+ setCopied(true);
+ setTimeout(() => {
+ setCopied(false);
+ }, 3000);
+ });
+ } else {
+ // Fallback action if navigator.clipboard is not supported
+ const textarea = document.createElement("textarea");
+ textarea.value = dev.socials.discord;
+ document.body.appendChild(textarea);
+ textarea.select();
+ document.execCommand("copy");
+ document.body.removeChild(textarea);
+ setCopied(true);
+ setTimeout(() => {
+ setCopied(false);
+ }, 3000);
+ }
+ };
+
+ const DevCard = () => {
+ return (
+ <div className="scale-95 antialiased xl:mx-28 xl:scale-100">
+ <div
+ className={`h-[116px] shrink bg-[#dadada50] bg-cover xl:w-[38rem] ${dev.iClass}`}
+ style={{
+ backgroundImage: `url(${
+ dev.coverImage ||
+ `https://cdn.discordapp.com/attachments/986579286397964290/1058415946945003611/gray_pfp.png`
+ })`,
+ }}
+ ></div>
+ <div className="flex h-auto flex-col gap-8 rounded-b-[10px] bg-white px-[30px] pb-[30px] pt-5 shadow-md dark:bg-[#181818] xl:w-[38rem] xl:px-[40px] xl:pb-[40px]">
+ <div>
+ <div className="flex items-center justify-between">
+ <h1 className="font-karla text-[30px] font-bold">{dev.name}</h1>
+ <div className="flex gap-5">
+ <div className="tooltip relative inline-block">
+ <svg
+ width="33"
+ height="32"
+ viewBox="0 0 33 32"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <g clip-path="url(#clip0_292_346)">
+ <path
+ d="M22.5508 22V26L32.7005 16L22.5508 6V10L28.6406 16L22.5508 22Z"
+ className="fill-black dark:fill-white"
+ />
+ <path
+ d="M10.3704 22V26L0.220703 16L10.3704 6V10L4.2806 16L10.3704 22Z"
+ className="fill-black dark:fill-white"
+ />
+ <path
+ d="M10.3711 20H14.431L22.5508 12H18.4909L10.3711 20Z"
+ className="fill-black dark:fill-white"
+ />
+ </g>
+ <defs>
+ <clipPath id="clip0_292_346">
+ <path
+ d="M0.220703 10C0.220703 4.47715 4.69786 0 10.2207 0H22.6999C28.2227 0 32.6999 4.47715 32.6999 10V22C32.6999 27.5228 28.2227 32 22.6999 32H10.2207C4.69785 32 0.220703 27.5228 0.220703 22V10Z"
+ className="fill-white dark:fill-black"
+ />
+ </clipPath>
+ </defs>
+ </svg>
+ <span className="tooltiptext absolute bottom-10 -right-12 w-max rounded-md bg-[#18191c] py-2 px-4 text-center text-sm font-semibold text-slate-200 transition-all duration-300 ease-out">
+ Lead Developer
+ </span>
+ </div>
+ <div className="tooltip relative inline-block">
+ <svg
+ width="28"
+ height="26"
+ viewBox="0 0 28 26"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M26.524 3.63947C23.2599 8.42076 13.4283 18.0917 13.4283 18.0917L13.4244 18.0879C13.4013 18.1128 13.3804 18.1388 13.3557 18.1632C12.7377 18.7709 11.8993 19.0168 11.0939 18.9231C11.3529 19.9913 11.0796 21.164 10.2533 21.9987C10.2533 21.9987 6.11896 27.0357 1.53711 23.8621C1.53711 23.8621 4.00781 23.1801 4.22278 21.216C4.22278 21.216 3.89291 19.2368 5.70663 17.4027C6.51975 16.5804 7.65724 16.2982 8.70017 16.5382C8.6166 15.756 8.86675 14.9457 9.47591 14.3461C9.5001 14.3217 9.52704 14.3011 9.55233 14.2778L9.54848 14.274C9.54848 14.274 19.3741 4.59768 24.2342 1.38614C24.8774 0.932763 25.7757 0.985847 26.353 1.55406C26.9319 2.12335 26.9852 3.00681 26.524 3.63947ZM9.488 18.1383C9.08007 17.7363 8.55118 17.5072 7.98546 17.5072C7.41754 17.5072 6.88095 17.7326 6.47687 18.1415C5.08318 19.5509 5.29209 21.1662 5.29759 21.2166C5.17774 22.3107 4.54824 23.2429 3.89181 23.8036C4.12382 23.8453 4.35857 23.8664 4.59827 23.8664C7.20972 23.8664 9.40993 21.3422 9.43027 21.3184C9.43082 21.3184 10.9361 19.5656 9.488 18.1383ZM9.78378 16.2543C9.78378 16.6855 9.94267 17.0918 10.2533 17.3967C10.5639 17.7022 10.9944 17.8793 11.4331 17.8793C11.8718 17.8793 12.2682 17.7028 12.569 17.4059L13.4233 16.5555C13.4667 16.5116 13.5261 16.4521 13.574 16.4038L11.2665 14.1305C11.216 14.1787 11.1538 14.2377 11.1087 14.2816L10.2527 15.1141C9.94267 15.4186 9.78378 15.8232 9.78378 16.2543ZM25.5767 2.3216C25.4398 2.18672 25.2798 2.16668 25.1957 2.16668C25.0792 2.16668 24.9681 2.20135 24.8466 2.28693C21.4281 4.54568 14.9511 10.6107 12.0362 13.3927L14.3228 15.6455C17.1486 12.7768 23.3193 6.39222 25.6306 3.00735C25.7867 2.79393 25.7642 2.50522 25.5767 2.3216Z"
+ className="fill-black dark:fill-white"
+ />
+ </svg>
+ <span className="tooltiptext absolute bottom-10 -right-11 w-max rounded-md bg-[#18191c] py-2 px-4 text-center text-sm font-semibold text-slate-200 transition-all duration-300 ease-out">
+ Lead Designer
+ </span>
+ </div>
+ </div>
+ </div>
+ </div>
+ <p className="font-robot text-xl font-light italic">{dev.desc}</p>
+
+ {dev.socials && (
+ <div className="pt-3">
+ <div className="flex items-center gap-4">
+ <div className="flex scale-90 gap-5">
+ {dev.socials.twitter && (
+ <div>
+ <a href={dev.socials.twitter}>
+ <Twitter className="fill-[#585858] " />
+ </a>
+ </div>
+ )}
+ {dev.socials.ig && (
+ <div className="pt-[1px]">
+ <a href={dev.socials.ig}>
+ <Instagram className="fill-[#585858] " />
+ </a>
+ </div>
+ )}
+ {dev.socials.discord && (
+ <div className="relative inline-block scale-95 pl-[1px] pt-[2px]">
+ <a onClick={handleClickDev}>
+ <Discord className="fill-[#585858] " />
+ </a>
+ {copied && (
+ <span className="absolute bottom-12 w-max rounded-md bg-[#18191c] py-2 px-4 text-center text-sm font-semibold text-slate-200 transition-all duration-300 ease-out">
+ Discord Tag copied to clipboard
+ </span>
+ )}
+ </div>
+ )}
+ {dev.socials.aniList && (
+ <div className="pt-[2px] xl:pt-[5px] ">
+ <a href={dev.socials.aniList}>
+ <AniList />
+ </a>
+ </div>
+ )}
+ </div>
+ </div>
+ </div>
+ )}
+ </div>
+ </div>
+ );
+ };
+
+ const staff = [
+ {
+ id: 1,
+ name: "rapiips_",
+ desc: "Time is precious, waste it wisely",
+ coverImage:
+ "https://media.discordapp.net/attachments/1068758633464201268/1072001529889959946/anime-girl-morning-breakfast-4k-wallpaper-uhdpaper.com-850i.jpg?width=1246&height=701",
+ bgPosition: "50% 25%",
+ socials: {
+ ig: "https://www.instagram.com/rapiips_/",
+ discord: "Pipip#5048",
+ },
+ tags: {
+ moral: "Pendukung Moral",
+ },
+ },
+ {
+ id: 2,
+ name: "Isantuyskl",
+ desc: "Sedang mencari.",
+ coverImage:
+ "https://cdn.discordapp.com/attachments/986579286397964290/1058420833376280656/23264b905fcd7ed378a6b3c5d8f2a047_7598772829043055595.png",
+ bgPosition: "50% 15%",
+ socials: {
+ ig: "https://www.instagram.com/isantuyskl/",
+ discord: "X-San#5418",
+ aniList: "https://anilist.co/user/isantuyskl/",
+ },
+ tags: {
+ moral: "Pendukung Moral",
+ design: "Idea Proposer",
+ },
+ },
+ {
+ id: 3,
+ name: "kha//sak?",
+ desc: "if you don't like it, ignore it\nI know your future, you can't defeat me..",
+ socials: {
+ ig: "https://www.instagram.com/khasakh_1364/",
+ discord: "sakh#0835",
+ },
+ tags: {
+ design: "Idea Proposer",
+ },
+ },
+ ];
+
+ const StaffCard = () => {
+ const [copySuccess, setCopySuccess] = useState("");
+
+ // your function to copy here
+
+ const copyStaff = async (copyMe) => {
+ try {
+ await navigator.clipboard.writeText(copyMe);
+ setCopySuccess("Copied!");
+ } catch (err) {
+ setCopySuccess("Failed to copy!");
+ }
+ };
+
+ return (
+ <div className="flex scale-95 flex-col flex-wrap gap-20 antialiased xl:mx-28 xl:scale-100 xl:flex-row xl:gap-20">
+ {staff.map((post) => (
+ <div key={post.id}>
+ <div
+ className={`h-[116px] shrink rounded-t-lg bg-[#dadada50] bg-cover dark:brightness-95 xl:w-[38rem]`}
+ style={{
+ backgroundImage: `url(${post.coverImage || ``})`,
+ backgroundPosition: `${post.bgPosition}`,
+ }}
+ >
+ {/* <img src={post.coverImage || `https://cdn.discordapp.com/attachments/986579286397964290/1058415946945003611/gray_pfp.png`} alt="Profile Picture" className={`object-cover object-[center_top] h-[116px] w-full rounded-t-lg ${post.iClass}`}/> */}
+ </div>
+ <div className="flex h-auto flex-col gap-8 rounded-b-lg bg-white px-[30px] pb-[30px] pt-5 shadow-md dark:bg-[#181818] xl:w-[38rem] xl:px-[40px] xl:pb-[40px]">
+ <div>
+ <div className="flex items-center justify-between">
+ <h1 className="font-karla text-[30px] font-bold">
+ {post.name}
+ </h1>
+ {post.tags && (
+ <div className="flex gap-5">
+ {post.tags.dev && (
+ <div className="tooltip relative inline-block">
+ <svg
+ width="33"
+ height="32"
+ viewBox="0 0 33 32"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <g clip-path="url(#clip0_292_346)">
+ <path
+ d="M22.5508 22V26L32.7005 16L22.5508 6V10L28.6406 16L22.5508 22Z"
+ className="fill-black dark:fill-white"
+ />
+ <path
+ d="M10.3704 22V26L0.220703 16L10.3704 6V10L4.2806 16L10.3704 22Z"
+ className="fill-black dark:fill-white"
+ />
+ <path
+ d="M10.3711 20H14.431L22.5508 12H18.4909L10.3711 20Z"
+ className="fill-black dark:fill-white"
+ />
+ </g>
+ <defs>
+ <clipPath id="clip0_292_346">
+ <path
+ d="M0.220703 10C0.220703 4.47715 4.69786 0 10.2207 0H22.6999C28.2227 0 32.6999 4.47715 32.6999 10V22C32.6999 27.5228 28.2227 32 22.6999 32H10.2207C4.69785 32 0.220703 27.5228 0.220703 22V10Z"
+ className="fill-white dark:fill-black"
+ />
+ </clipPath>
+ </defs>
+ </svg>
+ <span className="tooltiptext absolute bottom-10 w-max rounded-md bg-[#18191c] py-2 px-4 text-center text-sm font-semibold text-slate-200 transition-all duration-300 ease-out">
+ {post.tags.dev}
+ </span>
+ </div>
+ )}
+
+ {post.tags.moral && (
+ <div className="tooltip relative inline-block">
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ width="30"
+ height="30"
+ fill="none"
+ viewBox="0 0 30 30"
+ >
+ <g clipPath="url(#clip0_306_199)">
+ <path
+ className="fill-black dark:fill-white"
+ d="M28.896 16.376a11.061 11.061 0 01-2.361-3.916 11.72 11.72 0 00-3.877-8.658A11.781 11.781 0 0013.633.823a11.795 11.795 0 00-8.289 4.64A11.733 11.733 0 003.192 14.7H1.085a.438.438 0 00-.403.603.436.436 0 00.403.27h4.22v7.258h-4.22a.438.438 0 00-.437.436.435.435 0 00.437.436h6.47c.422 1.761.645 3.565.664 5.376a.435.435 0 00.441.408.437.437 0 00.432-.418 25.572 25.572 0 00-.629-5.367h8.13a2.15 2.15 0 001.688-.832 2.138 2.138 0 00.379-1.84 2.006 2.006 0 001.158-1.298 1.998 1.998 0 00-.296-1.713 1.876 1.876 0 00.438-2.191 1.875 1.875 0 00-.483-.629 2.03 2.03 0 00-1.31-3.397h-4.87c.79-3.232-.505-7.62-4.251-6.134a.432.432 0 00-.206.465 9.48 9.48 0 01.257 3.13c-.558 1.886-3.045 3.755-3.714 5.437h-1.3a10.848 10.848 0 011.893-8.623 10.92 10.92 0 0116.092-1.63 10.873 10.873 0 013.593 8.066 10.412 10.412 0 002.57 4.424c.32.474.662.862-.011 1.282-.757.422-1.54.794-2.347 1.112a.437.437 0 00-.274.463c.184 1.368.2 2.753.047 4.125-.129.65-1.596 1.087-3.662 1.087a1.774 1.774 0 00-1.799 1.111 1.763 1.763 0 00-.12.74v2.22a.436.436 0 00.745.31.436.436 0 00.128-.31v-2.225a.92.92 0 01.64-.943.928.928 0 01.406-.036c5.27-.027 4.81-1.877 4.52-5.86 1.484-.66 4.16-1.495 2.392-3.636zm-22.721-1.24c.559-1.643 3.247-3.635 3.752-5.597a8.696 8.696 0 00-.154-3.198c1.556-.396 2.607.59 2.827 2.78a8.192 8.192 0 01-.285 2.991.436.436 0 00.419.56h5.432a1.077 1.077 0 01.976 1.085 1.03 1.03 0 01-.654 1.076.437.437 0 00-.31.744.438.438 0 00.31.127.882.882 0 01.787.913c.01.71-.698.845-.803 1.313.013.45.61.682.57 1.19a1.28 1.28 0 01-1.052 1.218.433.433 0 00-.31.247c-.147.344.236.641.182.996a1.28 1.28 0 01-1.27 1.249H6.175v-7.695zm-3.202 5.807a.436.436 0 01.737-.293c.081.077.13.181.136.293a.435.435 0 01-.726.277.435.435 0 01-.143-.277h-.004z"
+ ></path>
+ </g>
+ <defs>
+ <clipPath id="clip0_306_199">
+ <path
+ fill="#fff"
+ d="M0 0H29V29H0z"
+ transform="translate(.479 .5)"
+ ></path>
+ </clipPath>
+ </defs>
+ </svg>
+ <span className="tooltiptext absolute bottom-10 -right-14 w-max rounded-md bg-[#18191c] py-2 px-4 text-center text-sm font-semibold text-slate-200 transition-all duration-300 ease-out">
+ {post.tags.moral}
+ </span>
+ </div>
+ )}
+
+ {post.tags.design && (
+ <div className="tooltip relative inline-block">
+ <svg
+ width="28"
+ height="26"
+ viewBox="0 0 28 26"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M26.524 3.63947C23.2599 8.42076 13.4283 18.0917 13.4283 18.0917L13.4244 18.0879C13.4013 18.1128 13.3804 18.1388 13.3557 18.1632C12.7377 18.7709 11.8993 19.0168 11.0939 18.9231C11.3529 19.9913 11.0796 21.164 10.2533 21.9987C10.2533 21.9987 6.11896 27.0357 1.53711 23.8621C1.53711 23.8621 4.00781 23.1801 4.22278 21.216C4.22278 21.216 3.89291 19.2368 5.70663 17.4027C6.51975 16.5804 7.65724 16.2982 8.70017 16.5382C8.6166 15.756 8.86675 14.9457 9.47591 14.3461C9.5001 14.3217 9.52704 14.3011 9.55233 14.2778L9.54848 14.274C9.54848 14.274 19.3741 4.59768 24.2342 1.38614C24.8774 0.932763 25.7757 0.985847 26.353 1.55406C26.9319 2.12335 26.9852 3.00681 26.524 3.63947ZM9.488 18.1383C9.08007 17.7363 8.55118 17.5072 7.98546 17.5072C7.41754 17.5072 6.88095 17.7326 6.47687 18.1415C5.08318 19.5509 5.29209 21.1662 5.29759 21.2166C5.17774 22.3107 4.54824 23.2429 3.89181 23.8036C4.12382 23.8453 4.35857 23.8664 4.59827 23.8664C7.20972 23.8664 9.40993 21.3422 9.43027 21.3184C9.43082 21.3184 10.9361 19.5656 9.488 18.1383ZM9.78378 16.2543C9.78378 16.6855 9.94267 17.0918 10.2533 17.3967C10.5639 17.7022 10.9944 17.8793 11.4331 17.8793C11.8718 17.8793 12.2682 17.7028 12.569 17.4059L13.4233 16.5555C13.4667 16.5116 13.5261 16.4521 13.574 16.4038L11.2665 14.1305C11.216 14.1787 11.1538 14.2377 11.1087 14.2816L10.2527 15.1141C9.94267 15.4186 9.78378 15.8232 9.78378 16.2543ZM25.5767 2.3216C25.4398 2.18672 25.2798 2.16668 25.1957 2.16668C25.0792 2.16668 24.9681 2.20135 24.8466 2.28693C21.4281 4.54568 14.9511 10.6107 12.0362 13.3927L14.3228 15.6455C17.1486 12.7768 23.3193 6.39222 25.6306 3.00735C25.7867 2.79393 25.7642 2.50522 25.5767 2.3216Z"
+ className="fill-black dark:fill-white"
+ />
+ </svg>
+ <span className="tooltiptext absolute bottom-10 -right-11 w-max rounded-md bg-[#18191c] py-2 px-4 text-center text-sm font-semibold text-slate-200 transition-all duration-300 ease-out">
+ {post.tags.design}
+ </span>
+ </div>
+ )}
+ </div>
+ )}
+ </div>
+ </div>
+ <p className="font-robot text-xl font-light italic">
+ “{post.desc || "description not provided"}”
+ </p>
+ {post.socials && (
+ <div className="pt-3">
+ <div className="items center flex gap-4 ">
+ <div className="flex scale-90 gap-5 ">
+ {post.socials.twitter && (
+ <div>
+ <a href={post.socials.twitter}>
+ <Twitter className="fill-[#585858] " />
+ </a>
+ </div>
+ )}
+ {post.socials.ig && (
+ <div className="pt-[1px]">
+ <a href={post.socials.ig}>
+ <Instagram className="fill-[#585858] " />
+ </a>
+ </div>
+ )}
+ {post.socials.discord && (
+ <div className="tooltip relative inline-block scale-95 pl-[1px] pt-[2px]">
+ <a onClick={() => copyStaff(post.socials.discord)}>
+ <Discord className="fill-[#585858] " />
+ </a>
+ <span className="tooltiptext absolute bottom-12 w-max rounded-md bg-[#18191c] py-2 px-4 text-center text-sm font-semibold text-slate-200 transition-all duration-300 ease-out">
+ Tap to copy Discord Tag
+ </span>
+ </div>
+ )}
+ {post.socials.aniList && (
+ <div className="pt-[2px] xl:pt-[5px] ">
+ <a href={post.socials.aniList}>
+ <AniList />
+ </a>
+ </div>
+ )}
+ </div>
+ </div>
+ </div>
+ )}
+ </div>
+ </div>
+ ))}
+ </div>
+ );
+ };
+
+ return (
+ <>
+ <Head>
+ <title>Moopa - Staff</title>
+ <meta name="staff" content="Our beloved staff-san" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <link rel="icon" href="/c.svg" />
+ </Head>
+
+ <Navbar className="dark:bg-black" />
+
+ <section className="flex h-auto flex-col gap-10 bg-[#F9F9F9] p-5 pt-10 dark:bg-[#111111] xl:gap-16 xl:px-20 xl:py-10">
+ <div>
+ <h1 className="font-roboto text-[23px] font-bold italic dark:text-gray-200">
+ MAIN CHARACTER
+ </h1>
+ </div>
+
+ {/* MAIN CHAR */}
+ {DevCard()}
+
+ <div>
+ <h1 className="font-roboto text-[23px] font-bold italic dark:text-gray-200">
+ SIDE CHARACTER
+ </h1>
+ </div>
+
+ {/* SIDE CHAR */}
+ {StaffCard()}
+ </section>
+
+ <Footer />
+ </>
+ );
+}
diff --git a/pages/test.js b/pages/test.js
new file mode 100644
index 0000000..57db350
--- /dev/null
+++ b/pages/test.js
@@ -0,0 +1,241 @@
+import { signIn, signOut, useSession } from "next-auth/react";
+import { useEffect, useState } from "react";
+import { useQuery } from "@apollo/client";
+import { GET_MEDIA } from "../queries";
+import StackPlayer from "../components/test/player";
+import Modal from "../components/modal";
+
+import { AniData as data } from "../components/test/dataAni";
+import Image from "next/image";
+import { client } from "../lib/apolloClient";
+import Link from "next/link";
+import { useAniList } from "../lib/useAnilist";
+
+export default function AniTest() {
+ const { data: session, status } = useSession();
+ const { media, aniAdvanceSearch } = useAniList(session);
+ const [advanceSearch, setAdvanceSearch] = useState();
+
+ const [search, setSearch] = useState();
+ const [type, setType] = useState("ANIME");
+ const [seasonYear, setSeasonYear] = useState();
+ const [season, setSeason] = useState();
+ const [genres, setGenres] = useState();
+ const [perPage, setPerPage] = useState(25);
+ const [sort, setSort] = useState(["POPULARITY_DESC"]);
+
+ // async function handleUpdateMediaEntry(entryId, status, progress, score) {
+ // try {
+ // const updatedEntry = await updateMediaEntry(
+ // entryId,
+ // status,
+ // progress,
+ // score
+ // );
+ // console.log(updatedEntry);
+ // } catch (error) {
+ // console.error(error);
+ // }
+ // }
+
+ // const userId = session?.user?.id;
+ // const MediaList = ({ userId }) => {
+ // const { data, loading, error } = useQuery(GET_MEDIA, {
+ // variables: { page: 1, userId, type: "ANIME", status: "COMPLETED" },
+ // });
+
+ // if (loading) return <p>Loading...</p>;
+ // if (error) return <p>Error :(</p>;
+
+ // const { mediaList } = data.Page;
+ // console.log(mediaList);
+ // };
+
+ // const [open, setOpen] = useState(false);
+
+ async function advance() {
+ const data = await aniAdvanceSearch(
+ search,
+ type,
+ seasonYear,
+ season,
+ genres,
+ perPage,
+ sort
+ );
+ setAdvanceSearch(data);
+ }
+
+ useEffect(() => {
+ advance();
+ }, [search, type, seasonYear, season, genres, perPage, sort]);
+
+ if (status === "loading") {
+ return <div>Loading...</div>;
+ }
+
+ const astatus = "COMPLETED";
+
+ // const { data } = aniAdvanceSearch({
+ // search: "naruto",
+ // });
+
+ console.log(advanceSearch);
+
+ return (
+ // <div className="h-[720px] w-[1280px]">
+ // <StackPlayer />
+ // </div>
+ <>
+ {/* <button
+ className="bg-[#30c168] p-2 rounded-lg m-5 text-black font-semibold font-karla"
+ onClick={() => setOpen(true)}
+ >
+ Start Watching
+ </button>
+ <Modal open={open} onClose={() => setOpen(false)}>
+ <div className="bg-white rounded-md text-black">
+ <div className="">
+ <Image
+ src={data.episodes[0].image}
+ alt="iamge"
+ width={1000}
+ height={1000}
+ className="object-cover rounded-t-md w-[420px] h-[100px]"
+ />
+ </div>
+ <div>Episode 6</div>
+ <div>test</div>
+ </div>
+ </Modal> */}
+ {!session && (
+ <button onClick={() => signIn("AniListProvider")}>
+ Sign in with Anilist
+ </button>
+ )}
+ {session && (
+ <div>
+ <button onClick={() => signOut()}>
+ Sign out ({session.user?.name})
+ </button>
+ <img
+ src={session.user?.image.large}
+ className="w-[100px] h-[100px]"
+ />
+ </div>
+ )}
+ {advanceSearch?.media.map((item) => {
+ return <div key={item.id}>{item.title.userPreferred}</div>;
+ })}
+ {media?.length > 0 && (
+ <div className="flex-center flex-col gap-5">
+ {media.map((item, index) => {
+ return (
+ <div key={index} className="flex-center flex-col gap-5">
+ <h2 className="font-bold text-xl font-karla">{item.name}</h2>
+ <div className="grid grid-cols-4 gap-5">
+ {item.entries.map((items, index) => {
+ return (
+ <div key={index}>
+ <div className="bg-secondary flex h-[120px] w-[420px]">
+ <div className="w-[20%] shrink-0">
+ <Image
+ src={items.media.coverImage.large}
+ alt="image deez nuts"
+ height={1000}
+ width={1000}
+ className="object-cover h-[120px] shrink-0"
+ />
+ </div>
+ <div className="p-3">
+ <h1 className="text-semibold font-karla text-lg line-clamp-3">
+ {items.media.title.romaji}
+ </h1>
+ <h3 className="text-sm font-karla text-light">
+ Episodes {items.progress} - {items.media.episodes}
+ </h3>
+ {item.name === "Watching" && (
+ <button
+ onClick={() =>
+ handleUpdateMediaEntry(
+ items.media.id,
+ "COMPLETED",
+ items.media.episodes,
+ 8
+ )
+ }
+ >
+ Mark as Complete
+ </button>
+ )}
+ </div>
+ </div>
+ </div>
+ );
+ })}
+ </div>
+ </div>
+ );
+ })}
+ {/* <h2 className="font-bold text-xl font-karla">Paused</h2>
+ <div className="grid grid-cols-4 gap-5">
+ {media[1].entries.map((item, index) => {
+ return (
+ <div key={index} className="">
+ <div className="bg-secondary flex h-[120px] w-[420px]">
+ <div className="w-[20%] shrink-0">
+ <Image
+ src={item.media.coverImage.large}
+ alt="image deez nuts"
+ height={1000}
+ width={1000}
+ className="object-cover h-[120px]"
+ />
+ </div>
+ <div className="p-3">
+ <h1 className="text-semibold font-karla text-lg line-clamp-3">
+ {item.media.title.romaji}
+ </h1>
+ <h3 className="text-sm font-karla text-light">
+ Episodes {item.progress} - {item.media.episodes}
+ </h3>
+ </div>
+ </div>
+ </div>
+ );
+ })}
+ </div>
+ <h2 className="font-bold text-xl font-karla">Dropped</h2>
+ <div className="grid grid-cols-4 gap-5">
+ {media[2].entries.map((item, index) => {
+ return (
+ <div key={index} className="">
+ <div className="bg-secondary flex h-[120px] w-[420px]">
+ <div className="w-[20%] shrink-0">
+ <Image
+ src={item.media.coverImage.large}
+ alt="image deez nuts"
+ height={1000}
+ width={1000}
+ className="object-cover h-[120px]"
+ />
+ </div>
+ <div className="p-3">
+ <h1 className="text-semibold font-karla text-lg line-clamp-3">
+ {item.media.title.romaji}
+ </h1>
+ <h3 className="text-sm font-karla text-light">
+ Episodes {item.progress} - {item.media.episodes}
+ </h3>
+ </div>
+ </div>
+ </div>
+ );
+ })}
+ </div> */}
+ </div>
+ )}
+ <Link href="/">Home</Link>
+ </>
+ );
+}