diff --git a/package-lock.json b/package-lock.json index 194c864..27ce720 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "react-virtualized-auto-sizer": "^1.0.24", "react-window": "^1.8.10", "sweetalert2": "^11.17.2", + "swr": "^2.3.2", "tailwind-merge": "^2.5.2", "zustand": "^4.5.5" }, @@ -2316,6 +2317,15 @@ "node": ">=0.4.0" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -4360,6 +4370,28 @@ "url": "https://github.com/sponsors/limonte" } }, + "node_modules/swr": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.2.tgz", + "integrity": "sha512-RosxFpiabojs75IwQ316DGoDRmOqtiAj0tg8wCcbEu4CiLZBs/a9QNtHV7TUfDXmmlgqij/NqzKq/eLelyv9xA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/swr/node_modules/use-sync-external-store": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", + "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/tailwind-merge": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.5.2.tgz", diff --git a/package.json b/package.json index 5769e77..482f68c 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "react-virtualized-auto-sizer": "^1.0.24", "react-window": "^1.8.10", "sweetalert2": "^11.17.2", + "swr": "^2.3.2", "tailwind-merge": "^2.5.2", "zustand": "^4.5.5" }, diff --git a/public/assets/images/nodata.svg b/public/assets/images/nodata.svg new file mode 100644 index 0000000..513cdfc --- /dev/null +++ b/public/assets/images/nodata.svg @@ -0,0 +1 @@ +winter_magic \ No newline at end of file diff --git a/src/pages/Ranking/Ranking.tsx b/src/pages/Ranking/Ranking.tsx index b997d92..b6193d7 100644 --- a/src/pages/Ranking/Ranking.tsx +++ b/src/pages/Ranking/Ranking.tsx @@ -1,77 +1,37 @@ import LayoutPage from "@/components/templates/LayoutPage"; import useNavbarStore from "@/stores/NavbarStore"; -import { useEffect, useState } from "react"; +import { ChangeEvent, useEffect, useState } from "react"; import { HiOutlineHome } from "react-icons/hi"; import { Link } from "react-router-dom"; import { motion } from "framer-motion"; - -const data = [ - { - id: 1, - name: "Asep", - score: 100, - time: "15-08-2021 12:40", - }, - { - id: 2, - name: "Budi", - score: 90, - time: "15-08-2021 12:40", - }, - { - id: 3, - name: "Cecep", - score: 80, - time: "15-08-2021 12:40", - }, - { - id: 4, - name: "Dedi", - score: 70, - time: "15-08-2021 12:40", - }, - { - id: 5, - name: "Euis", - score: 60, - time: "15-08-2021 12:40", - }, - { - id: 6, - name: "Fafa", - score: 50, - time: "15-08-2021 12:40", - }, - { - id: 7, - name: "Gaga", - score: 40, - time: "15-08-2021 12:40", - }, - { - id: 8, - name: "Haha", - score: 30, - time: "15-08-2021 12:40", - }, - { - id: 9, - name: "Ii", - score: 20, - time: "15-08-2021 12:40", - }, - { - id: 10, - name: "Jaja", - score: 10, - time: "15-08-2021 12:40", - }, -]; +import useSWR from "swr"; +import { fetcher } from "@/utils/fetcher"; +import { debounce } from "@/utils/debounce"; const Ranking = () => { const store = useNavbarStore(); const [tabSelected, setTabSelected] = useState(0); + const [page, setPage] = useState(1); + const [search, setSearch] = useState(""); + const size = 20; + + const { data, error, isLoading } = useSWR( + `https://ksuli-api.deno.dev/ranking?page=${page}&search=${search}&size=${size}&kategori_id=${ + tabSelected == 0 ? "rec_cuum78tqrj678tmbcjh0" : "rec_cuum7c5qrj60bgubcjog" + }`, + fetcher + ); + + const handleSearch = debounce((term) => { + setSearch(term); + }, 500); + + const handleChangeSearch = (e: ChangeEvent) => { + const { value } = e.target; + setPage(1); + handleSearch(value); + }; useEffect(() => { store.setNavSelected("kuis"); @@ -93,19 +53,25 @@ const Ranking = () => {
  • Ranking
  • -
    -
    +
    +
    - {data.map((item, index) => ( - + {isLoading && ( +
    +
    +

    Loading...

    +
    + )} + + {error && !isLoading && ( +
    -
    -

    {item.name}

    -

    {item.score} Poin

    -

    {item.time}

    -
    -
    + Sedang terjadi error, silakan coba lagi nanti. +

    +
    + )} + + {data && data.records.length === 0 && !isLoading && ( +
    + Error Image +

    + Data tidak ditemukan +

    +
    + )} + + {data && + data.records + .sort((a: any, b: any) => + tabSelected === 0 ? b.score - a.score : a.score - b.score + ) + .map((item: any, index: number) => ( + + +
    +

    {item.person_name}

    +
    +

    + {item.score} {tabSelected == 0 ? "Point" : "Detik"} +

    +
    + {index < 3 && ( +

    + ⭐ Top Score {index + 1} +

    + )} +
    + +

    + {new Date(item.xata.createdAt).toLocaleString()} +

    +
    +
    + {index + 1} +
    +
    + ))} + {data && ( +
    +
    - - ))} + + + + + +
    + )}
    diff --git a/src/utils/debounce.ts b/src/utils/debounce.ts new file mode 100644 index 0000000..0171a5f --- /dev/null +++ b/src/utils/debounce.ts @@ -0,0 +1,10 @@ +export const debounce = any>( + func: T, + delay: number +): ((...args: Parameters) => void) => { + let timeoutId: ReturnType; + return function (this: ThisParameterType, ...args: Parameters) { + clearTimeout(timeoutId); + timeoutId = setTimeout(() => func.apply(this, args), delay); + }; +}; \ No newline at end of file diff --git a/src/utils/fetcher.ts b/src/utils/fetcher.ts new file mode 100644 index 0000000..7380b08 --- /dev/null +++ b/src/utils/fetcher.ts @@ -0,0 +1,9 @@ +export const fetcher = (url: RequestInfo, options: RequestInit = {}) => { + const token = "KSULI_TOKEN_321"; // Replace with your actual token + const headers = { + ...options.headers, + Authorization: `Bearer ${token}`, + }; + + return fetch(url, { ...options, headers }).then((res) => res.json()); +};