tuyetnhi 1 год назад
Родитель
Сommit
3cff4119fd

+ 308 - 298
src/@types/index.d.ts

@@ -1,324 +1,334 @@
-declare module 'metarare' {
-    import { ECurrencyType, EItemType, EActivity, ESaleType, ESaleStatus, ENetworkType } from '@src/constants/Enums';
+declare module "metarare" {
+  import {
+    ECurrencyType,
+    EItemType,
+    EActivity,
+    ESaleType,
+    ESaleStatus,
+    ENetworkType,
+  } from "@src/constants/Enums";
 
-    export interface IHome {
-        collections: ICollection[];
-        curating_list: INFT[];
-        explore: INFT[];
-        hot_bids: INFT[];
-        special_bids: INFT[];
-        time_auctions: INFT[];
-    }
+  export interface IHome {
+    collections: ICollection[];
+    curating_list: INFT[];
+    explore: INFT[];
+    hot_bids: INFT[];
+    special_bids: INFT[];
+    time_auctions: INFT[];
+  }
 
-    export interface IActivity {
-        from_address: string;
-        from_user_profile: string;
-        from_user_name: string;
-        is_cancel: boolean;
-        log_relation_id: number;
-        log_type: EActivity;
-        price: string;
-        sale_uid: number;
-        to_address: string;
-        to_user_name: string;
-        to_user_profile: string;
-        token_content_url: string;
-        token_name: string;
-        tx: string;
-        created_at: string;
-        currency: ECurrencyType;
-        sale_id: number;
-        token_id: number;
-        from_user_is_artist: boolean;
-        to_user_is_artist: boolean;
-    }
+  export interface IActivity {
+    from_address: string;
+    from_user_profile: string;
+    from_user_name: string;
+    is_cancel: boolean;
+    log_relation_id: number;
+    log_type: EActivity;
+    price: string;
+    sale_uid: number;
+    to_address: string;
+    to_user_name: string;
+    to_user_profile: string;
+    token_content_url: string;
+    token_name: string;
+    tx: string;
+    created_at: string;
+    currency: ECurrencyType;
+    sale_id: number;
+    token_id: number;
+    from_user_is_artist: boolean;
+    to_user_is_artist: boolean;
+  }
 
-    export interface ICollection {
-        contract_address: string;
-        cover_image: string;
-        curating_number: number;
-        description: string;
-        highest_sale_price: number;
-        id: number;
-        is_official: boolean;
-        is_owner: boolean;
-        items: number;
-        market_cap: number;
-        name: string;
-        recent_trading_day: string;
-        status: ESaleStatus;
-        symbol: string;
-        thumbnail_image: string;
-        total_like_count: number;
-        total_volume: number;
-        user_name: string;
-        cover_image: string;
-    }
+  export interface ICollection {
+    contract_address: string;
+    cover_image: string;
+    curating_number: number;
+    description: string;
+    highest_sale_price: number;
+    id: number;
+    is_official: boolean;
+    is_owner: boolean;
+    items: number;
+    market_cap: number;
+    name: string;
+    recent_trading_day: string;
+    status: ESaleStatus;
+    symbol: string;
+    thumbnail_image: string;
+    total_like_count: number;
+    total_volume: number;
+    user_name: string;
+    cover_image: string;
+  }
 
-    export interface ICollectionBaseInfo {
-        id: number;
-        name: string;
-        description: string;
-        contract_address: string;
-        thumbnail_image: string;
-        cover_image: string;
-        is_official: boolean;
-        user_name: string;
-        items: number;
-        highest_sale_price: number;
-        market_cap: number;
-        total_volume: number;
-        is_owner: boolean;
-    }
+  export interface ICollectionBaseInfo {
+    id: number;
+    name: string;
+    description: string;
+    contract_address: string;
+    thumbnail_image: string;
+    cover_image: string;
+    is_official: boolean;
+    user_name: string;
+    items: number;
+    highest_sale_price: number;
+    market_cap: number;
+    total_volume: number;
+    is_owner: boolean;
+  }
 
-    export interface INFT {
-        collection_id: number;
-        collection_thumbnail_image: string;
-        content_title: string;
-        content_url: string;
-        current_price: number;
-        current_currency: ECurrencyType;
-        item_index: number;
-        lastest_currency: ECurrencyType;
-        lastest_price: number;
-        owner_name: string;
-        owner_thumbnail_image: string;
-        sale_type: ESaleType;
-        total_count: number;
-        user_id: number;
-        sale_id: number;
-        token_id: number;
-        likes: number;
-        is_like: boolean;
-        end_at: string;
-        highest_price: number;
-        start_price: number;
-        created_at: number;
-        collection_name: string;
-        fixed_price: number;
-        sale_status: ESaleStatus;
-    }
+  export interface INFT {
+    collection_id: number;
+    collection_thumbnail_image: string;
+    content_title: string;
+    content_url: string;
+    current_price: number;
+    current_currency: ECurrencyType;
+    item_index: number;
+    lastest_currency: ECurrencyType;
+    lastest_price: number;
+    owner_name: string;
+    owner_thumbnail_image: string;
+    sale_type: ESaleType;
+    total_count: number;
+    user_id: number;
+    sale_id: number;
+    token_id: number;
+    likes: number;
+    is_like: boolean;
+    end_at: string;
+    highest_price: number;
+    start_price: number;
+    created_at: number;
+    collection_name: string;
+    fixed_price: number;
+    sale_status: ESaleStatus;
+  }
 
-    export interface IUserInfo {
-        settings: {
-            network_commission: number;
-            service_commission: number;
-        };
-        simple_profile: {
-            address: string;
-            cover_image: string;
-            description: string;
-            email: string;
-            eth_balance: number;
-            is_artist: boolean;
-            mf_balance: number;
-            mr_balance: number;
-            name: string;
-            thumbnail_image: string;
-            twitter: string;
-            user_id: number;
-            available_eth_balance: number;
-            available_mf_balance: number;
-            available_mr_balance: number;
-        };
-    }
+  export interface IUserInfo {
+    settings: {
+      network_commission: number;
+      service_commission: number;
+    };
+    simple_profile: {
+      address: string;
+      cover_image: string;
+      description: string;
+      email: string;
+      eth_balance: number;
+      is_artist: boolean;
+      mf_balance: number;
+      mr_balance: number;
+      name: string;
+      thumbnail_image: string;
+      twitter: string;
+      user_id: number;
+      available_eth_balance: number;
+      available_mf_balance: number;
+      available_mr_balance: number;
+    };
+  }
 
-    export interface IProfile {
-        cover_image: string;
-        thumbnail_image: string;
-        is_artist: boolean;
-        name: string;
-        address: string;
-        description: string;
-        twitter: string;
-        id: number;
-        email: string;
-        is_owner: boolean;
-    }
+  export interface IProfile {
+    cover_image: string;
+    thumbnail_image: string;
+    is_artist: boolean;
+    name: string;
+    address: string;
+    description: string;
+    twitter: string;
+    id: number;
+    email: string;
+    is_owner: boolean;
+  }
 
-    export interface ITrait {
-        created_at: string;
-        deleted_at: string;
-        id: number;
-        key: string;
-        token_id: number;
-        updated_at: string;
-        value: string;
-    }
+  export interface ITrait {
+    created_at: string;
+    deleted_at: string;
+    id: number;
+    key: string;
+    token_id: number;
+    updated_at: string;
+    value: string;
+  }
 
-    export interface ISaleDetail {
-        id: number;
-        token_id: number;
-        token_type: EItemType;
-        content_url: string;
-        name: string;
-        description: string;
-        sale_type: ESaleType;
-        price: number;
-        highest_price: number;
-        my_bidding_price: number;
-        start_price: number;
-        start_at: string;
-        end_at: string;
-        royalties: number;
-        artist_profile_image: string;
-        artist_name: string;
-        like_count: number;
-        collection_thumbnail_image: string;
-        collection_name: string;
-        user_profile_image: string;
-        user_name: string;
-        network: string;
-        currency: ECurrencyType;
-        is_owner: boolean;
-        is_like: boolean;
-        total_count: number;
-        token_uid: string;
-        index: number;
-        status: ESaleStatus;
-        collection_is_official: boolean;
-        artist_is_official: boolean;
-        owner_is_official: boolean;
-    }
+  export interface ISaleDetail {
+    id: number;
+    token_id: number;
+    token_type: EItemType;
+    content_url: string;
+    name: string;
+    description: string;
+    sale_type: ESaleType;
+    price: number;
+    highest_price: number;
+    my_bidding_price: number;
+    start_price: number;
+    start_at: string;
+    end_at: string;
+    royalties: number;
+    artist_profile_image: string;
+    artist_name: string;
+    like_count: number;
+    collection_thumbnail_image: string;
+    collection_name: string;
+    user_profile_image: string;
+    user_name: string;
+    network: string;
+    currency: ECurrencyType;
+    is_owner: boolean;
+    is_like: boolean;
+    total_count: number;
+    token_uid: string;
+    index: number;
+    status: ESaleStatus;
+    collection_is_official: boolean;
+    artist_is_official: boolean;
+    owner_is_official: boolean;
+  }
 
-    export interface ISale {
-        sale: ISaleDetail;
-        traits: ITrait[];
-    }
+  export interface ISale {
+    sale: ISaleDetail;
+    traits: ITrait[];
+  }
 
-    export interface ITokenDetail {
-        id: number; // tokenId
-        type: EItemType; // token_type
-        content_url: string;
-        name: string;
-        description: string;
-        royalties: number;
-        artist_profile_image: string;
-        artist_name: string;
-        like_count: number;
-        collection_name: string;
-        collection_thumbnail_image: string;
-        collection_is_official: boolean;
-        user_profile_image: string;
-        user_name: string;
-        network: string;
-        is_owner: boolean;
-        is_like: boolean;
-        total_count: number;
-        index: number;
-        artist_is_official: boolean;
-        owner_is_official: boolean;
-        uid: string; // token uid
-    }
+  export interface ISaleRanking {
+    artist_id: number;
+    thumbnail_image: string;
+    cover_image: string;
+    name: string;
+    category: string;
+    amounts_sale: number;
+  }
 
-    export interface IToken {
-        token: ITokenDetail;
-        traits: ITrait[];
-    }
+  export interface ITokenDetail {
+    id: number; // tokenId
+    type: EItemType; // token_type
+    content_url: string;
+    name: string;
+    description: string;
+    royalties: number;
+    artist_profile_image: string;
+    artist_name: string;
+    like_count: number;
+    collection_name: string;
+    collection_thumbnail_image: string;
+    collection_is_official: boolean;
+    user_profile_image: string;
+    user_name: string;
+    network: string;
+    is_owner: boolean;
+    is_like: boolean;
+    total_count: number;
+    index: number;
+    artist_is_official: boolean;
+    owner_is_official: boolean;
+    uid: string; // token uid
+  }
 
-    export interface ITokenCollection {
-        collection_id: number;
-        is_official: boolean;
-        name: string;
-        thumbnail_image: string;
-    }
+  export interface IToken {
+    token: ITokenDetail;
+    traits: ITrait[];
+  }
 
-    export interface ITokenBaseInfo {
-        collection: ITokenCollection[];
-        commission: number;
-    }
+  export interface ITokenCollection {
+    collection_id: number;
+    is_official: boolean;
+    name: string;
+    thumbnail_image: string;
+  }
 
-    export interface ILog {
-        from_address: string;
-        from_user_profile: string;
-        from_user_name: string;
-        from_user_is_artist: boolean;
-        is_cancel: boolean;
-        log_relation_id: number;
-        log_type: string;
-        price: string;
-        sale_uid: string;
-        to_address: string;
-        to_user_name: string;
-        to_user_profile: string;
-        token_content_url: string;
-        token_name: string;
-        tx: string;
-        created_at: string;
-        is_official: boolean;
-        currency: ECurrencyType;
-    }
+  export interface ITokenBaseInfo {
+    collection: ITokenCollection[];
+    commission: number;
+  }
 
-    export interface IOwner {
-        index: number;
-        name: string;
-        thumbnail_image: string;
-        total_count: number;
-        updated_at: string;
-        token_id: number;
-        is_official: boolean;
-    }
+  export interface ILog {
+    from_address: string;
+    from_user_profile: string;
+    from_user_name: string;
+    from_user_is_artist: boolean;
+    is_cancel: boolean;
+    log_relation_id: number;
+    log_type: string;
+    price: string;
+    sale_uid: string;
+    to_address: string;
+    to_user_name: string;
+    to_user_profile: string;
+    token_content_url: string;
+    token_name: string;
+    tx: string;
+    created_at: string;
+    is_official: boolean;
+    currency: ECurrencyType;
+  }
 
-    export interface IResaleInfo {
-        token: {
-            collection_id: number;
-            collection_is_official: boolean;
-            collection_name: string;
-            collection_thumbnail_image: string;
-            commission: number;
-            content_url: string;
-            description: string;
-            name: string;
-            network: ENetworkType;
-            royalties: number;
-            token_id: number;
-            total_count: number;
-            type: EItemType;
-        };
-        traits: ITrait[];
-    }
+  export interface IOwner {
+    index: number;
+    name: string;
+    thumbnail_image: string;
+    total_count: number;
+    updated_at: string;
+    token_id: number;
+    is_official: boolean;
+  }
 
-    export interface IFilter {
-        category?: string;
-        collection_name?: string;
-        currency?: ECurrencyType | '';
-        currency_amount_end?: string;
-        currency_amount_start?: string;
-        limit?: number;
-        network?: string;
-        offset?: number;
-        sale_status?: string;
-        sale_type?: string;
-        token_name?: string;
-        authentication_number?: number;
-    }
+  export interface IResaleInfo {
+    token: {
+      collection_id: number;
+      collection_is_official: boolean;
+      collection_name: string;
+      collection_thumbnail_image: string;
+      commission: number;
+      content_url: string;
+      description: string;
+      name: string;
+      network: ENetworkType;
+      royalties: number;
+      token_id: number;
+      total_count: number;
+      type: EItemType;
+    };
+    traits: ITrait[];
+  }
 
-    export interface IBidHistory {
-        thumbnail_image: string;
-        name: string;
-        price: number;
-        is_cancel: boolean;
-        currency: ECurrencyType;
-        created_at: string;
-        is_official: boolean;
-    }
+  export interface IFilter {
+    category?: string;
+    collection_name?: string;
+    currency?: ECurrencyType | "";
+    currency_amount_end?: string;
+    currency_amount_start?: string;
+    limit?: number;
+    network?: string;
+    offset?: number;
+    sale_status?: string;
+    sale_type?: string;
+    token_name?: string;
+    authentication_number?: number;
+  }
 
-    export interface IArtist {
-        id: number;
-        name: string;
-        thumbnail_image: string;
-    }
+  export interface IBidHistory {
+    thumbnail_image: string;
+    name: string;
+    price: number;
+    is_cancel: boolean;
+    currency: ECurrencyType;
+    created_at: string;
+    is_official: boolean;
+  }
 
-    export interface ISalesRanking {
-        id: number;
-        rank: number;
-        name: string;
-        thumbnail_image: string;
-        total_revenue: number;
-    }
+  export interface IArtist {
+    ID: number;
+    UserID: number;
+    Team: string;
+    Category: string;
+    ThumbnailImage: string;
+  }
 }
 
-declare module '*.svg';
+declare module "*.svg";
 declare module '*.scss';
 declare module '*.module.scss' {
     const classes: { [key: string]: string };

+ 22 - 0
src/api/APIs.ts

@@ -66,6 +66,14 @@ const fetchExploreSearch = (filter: IFilter) => {
   return post({ url, body: filter });
 };
 
+const fetchExploreHint = (filter: IFilter) => {
+  const url = `${host}/v1/explore/hint`;
+
+  return post({ url, body: filter });
+};
+
+
+
 const fetchExploreKeyword = (collectionName: string) => {
   const url = `${host}/v1/explore/keyword`;
 
@@ -79,6 +87,11 @@ const fetchHome = () => {
   return get({ url });
 };
 
+const fetchHomeArtists = (filter: IFilter) => {
+  const url = `${host}/v1/home/artist`;
+
+  return get({ url, params: filter });
+};
 
 // sale
 const saleHighestPrice = ({ bid_price, currency, sale_id }: { bid_price: number; currency: ECurrencyType; sale_id: number }) => {
@@ -129,6 +142,12 @@ const fetchSale = (id: number) => {
   return get({ url });
 };
 
+const fetchSaleRanking = (filter: IFilter) => {
+  const url = `${host}/v1/sale/ranking`;
+
+  return get({ url, params: filter });
+}
+
 // token
 const fetchToken = (id: number) => {
   const url = `${host}/v1/token/${id}`;
@@ -267,7 +286,9 @@ const APIs = {
   fetchExploreHome,
   fetchExploreSearch,
   fetchExploreKeyword,
+  fetchExploreHint,
   fetchHome,
+  fetchHomeArtists,
   saleHighestPrice,
   saleBid,
   cancelBid,
@@ -275,6 +296,7 @@ const APIs = {
   resale,
   changeSaleStatus,
   fetchSale,
+  fetchSaleRanking,
   fetchBid,
   fetchToken,
   addToken,

+ 1 - 1
src/api/index.ts

@@ -8,7 +8,7 @@ const authorizationHeader = {
 export const get = async ({ url, params = {}, headers = {} }: { url: string; params?: object; headers?: object }) => {
   try {
     const result = await axios.get(url, {
-      ...params,
+      params,
       headers: {
         ...authorizationHeader,
         ...headers,

+ 25 - 23
src/components/common/ArtistCard.tsx

@@ -1,9 +1,10 @@
-import * as React from 'react';
-import styles from './ArtistCard.scss';
-import classNames from 'classnames/bind';
-import { Link } from 'react-router-dom';
-import Img from './Img';
-import { IArtist } from 'metarare';
+import * as React from "react";
+import styles from "./ArtistCard.scss";
+import classNames from "classnames/bind";
+import { Link } from "react-router-dom";
+import Img from "./Img";
+import { IArtist } from "metarare";
+import URLInfo from "@src/constants/URLInfo";
 
 const cx = classNames.bind(styles);
 
@@ -11,27 +12,28 @@ export interface IArtistCardProps extends IArtist {
   className: string;
 }
 
-export default function ArtistCard ({
-  id,
-  name,
-  thumbnail_image,
-  className
+export default function ArtistCard({
+  ID,
+  Team,
+  ThumbnailImage,
+  className,
 }: IArtistCardProps) {
   return (
-    <div className={cx("artist_card", className )}>
-      <div className={cx("card_wrap")}>
+    <div className={cx("artist_card", className)}>
+      <Link to={URLInfo.getProfileUserUrl(Team || "")}>
+        <div className={cx("card_wrap")}>
           <div className={cx("thumb_area")}>
-              <Img
-                src={thumbnail_image}
-                alt=""
-                width="260"
-                height="260"
-                className={cx("thumb")}
-              />
-           
+            <Img
+              src={ThumbnailImage}
+              alt=""
+              width="260"
+              height="260"
+              className={cx("thumb")}
+            />
           </div>
-          <div className={cx("info_area")}>{name}</div>
-      </div>
+          <div className={cx("info_area")}>{Team}</div>
+        </div>
+      </Link>
     </div>
   );
 }

+ 1 - 1
src/components/common/RankingTable.scss

@@ -37,7 +37,7 @@
         tbody {
             tr {
                 height: 60px;
-
+                cursor: pointer;
                 td {
                     padding: 0 10px;
                     font-size: 1em;

+ 19 - 8
src/components/common/RankingTable.tsx

@@ -1,23 +1,31 @@
-import { ISalesRanking } from "metarare";
+import { ISaleRanking } from "metarare";
 import * as React from "react";
 import styles from "./RankingTable.scss";
 import classNames from "classnames/bind";
 import currency from "currency.js";
+import { useHistory } from "react-router-dom";
+import URLInfo from "@src/constants/URLInfo";
 
 const cx = classNames.bind(styles);
 
 export interface IRankingTableProps{
-    data: ISalesRanking[];
+    data: ISaleRanking[];
 }
 
 export default function RankingTable({data}: IRankingTableProps) {
+  const history = useHistory()
   const header = ["", "Store", "", "Total Value"];
   const renderColor = (rank: number) => {
     if (rank === 1) return "#ffd700";
     if (rank === 2) return "#c0c0c0";
     if (rank === 3) return "#cd7f32";
-    return "#3d3d3d";
+    if(rank <= 10 ) return "#3d3d3d";
+    return "unset";
   };
+  
+  const navigate = (path: string) => {
+    history.push(path)
+  }
   return (
     <div className={cx("table_wrapper")}>
       <table>
@@ -30,16 +38,19 @@ export default function RankingTable({data}: IRankingTableProps) {
         </thead>
         <tbody>
           {data.map((item, index) => (
-            <tr key={"custom-table-ranking" + index}>
-              <td className={cx("rank")}>
+            <tr key={"custom-table-ranking" + index} onClick={() =>{
+              navigate(URLInfo.getProfileUserUrl(item.name || ""))
+            }}>
+              <td className={cx("rank")} >
                 <div style={{
-                    background: renderColor(item.rank)
-                }}>{item.rank}</div></td>
+                    background: renderColor(index + 1),
+                    color: index + 1 <= 10 ? '#fff' : '#000'
+                }}>{index + 1}</div></td>
               <td className={cx("thumbnail_image")}>
                 <img src={item.thumbnail_image} alt={item.name} />
               </td>
               <td className={cx("name")}>{item.name}</td>
-              <td className={cx("total_revenue")}>{currency(item.total_revenue).format({
+              <td className={cx("total_revenue")}>{currency(item.amounts_sale).format({
                     symbol: "$",
                     precision: 0,
               })}</td>

+ 33 - 28
src/components/common/SaleRankingCard.tsx

@@ -1,27 +1,30 @@
 import classNames from "classnames/bind";
-import { ISalesRanking } from "metarare";
+import { ISaleRanking } from "metarare";
 import * as React from "react";
 import Img from "./Img";
 import styles from "./SalesRankingCard.scss";
 import currency from "currency.js";
+import { Link } from "react-router-dom";
+import URLInfo from "@src/constants/URLInfo";
 
 const cx = classNames.bind(styles);
 
-export interface ISalesRankingCardProps extends ISalesRanking {
+export interface ISalesRankingCardProps extends ISaleRanking {
   className: string;
+  index: number;
 }
 
 export default function SalesRankingCard({
-  rank,
+  index,
   name,
   thumbnail_image,
   className,
-  total_revenue,
+  amounts_sale,
 }: ISalesRankingCardProps) {
   const renderColor = () => {
-    if (rank === 1) return "#ffd700";
-    if (rank === 2) return "#c0c0c0";
-    if (rank === 3) return "#cd7f32";
+    if (index + 1 === 1) return "#ffd700";
+    if (index + 1 === 2) return "#c0c0c0";
+    if (index + 1 === 3) return "#cd7f32";
     return "#3d3d3d";
   };
   return (
@@ -32,29 +35,31 @@ export default function SalesRankingCard({
           background: renderColor(),
         }}
       >
-        <span>{rank}</span>
-      </div>
-      <div className={cx("card_wrap")}>
-        <div className={cx("thumb_area")}>
-          <Img
-            src={thumbnail_image}
-            alt=""
-            width="260"
-            height="260"
-            className={cx("thumb")}
-          />
-        </div>
-        <div className={cx("info_area")}>
-          <div className={cx("total")}>
-            Total:{" "}
-            {currency(total_revenue).format({
-              symbol: "$",
-              precision: 0,
-            })}
+        <span>{index + 1}</span>
+      </div>{" "}
+      <Link to={URLInfo.getProfileUserUrl(name || "")}>
+        <div className={cx("card_wrap")}>
+          <div className={cx("thumb_area")}>
+            <Img
+              src={thumbnail_image}
+              alt=""
+              width="260"
+              height="260"
+              className={cx("thumb")}
+            />
+          </div>
+          <div className={cx("info_area")}>
+            <div className={cx("total")}>
+              Total:{" "}
+              {currency(amounts_sale).format({
+                symbol: "$",
+                precision: 0,
+              })}
+            </div>
+            <div className={cx("name")}>{name}</div>
           </div>
-          <div className={cx("name")}>{name}</div>
         </div>
-      </div>
+      </Link>
     </div>
   );
 }

+ 1 - 1
src/components/mobile/ArtistScrollList.tsx

@@ -20,7 +20,7 @@ const ArtistScrollList: React.FC<IOwnProps> = ({
       <Flicking align="prev">
         {list.map((item, index) => (
           <div
-            key={`${item.id}_${index}`}
+            key={`${item.ID}_${index}`}
             className={cx('nft_panel')}
           >
             <ArtistCard {...item} className='scroll' />

+ 11 - 11
src/components/mobile/Menu.tsx

@@ -37,7 +37,7 @@ const Menu: React.FC<IOwnProps> = ({ onClose }) => {
         <IconClose className={cx("ico_close")} />
       </button>
       <ul className={cx("menu_list")}>
-        <li
+        {/* <li
           className={cx("menu_item")}
           aria-selected={URLInfo.isExplorePage(location)}
         >
@@ -54,7 +54,7 @@ const Menu: React.FC<IOwnProps> = ({ onClose }) => {
               내 프로필
             </Link>
           </li>
-        )}
+        )} */}
         {/* <li
           className={cx("menu_item")}
           aria-selected={URLInfo.isActivityPage(location)}
@@ -63,14 +63,6 @@ const Menu: React.FC<IOwnProps> = ({ onClose }) => {
             활동
           </Link>
         </li> */}
-        <li
-          className={cx("menu_item")}
-          aria-selected={URLInfo.isGuidePage(location)}
-        >
-          <Link to={URLInfo.EXPLORE} className={cx("menu")}>
-            순위 100
-          </Link>
-        </li>
       </ul>
       <div className={cx("community_area")}>
         <strong className={cx("community_title")}>커뮤니티</strong>
@@ -106,7 +98,7 @@ const Menu: React.FC<IOwnProps> = ({ onClose }) => {
           <li className={cx("community_item")}>
             <Link to={URLInfo.GUIDE} className={cx("link_community")}>
               사용방법
-            </Link>         
+            </Link>
           </li>
           <li className={cx("community_sns")}>
             <a
@@ -145,6 +137,14 @@ const Menu: React.FC<IOwnProps> = ({ onClose }) => {
           </li>
         </ul>
       </div>
+      <li
+        className={cx("menu_item")}
+        aria-selected={URLInfo.isGuidePage(location)}
+      >
+        <Link to={URLInfo.EXPLORE} className={cx("menu")}>
+          Top 100
+        </Link>
+      </li>
       <div className={cx("btn_area")}>
         {isLogin && (
           <Link to={URLInfo.CREATE} className={cx("link", "link_make")}>

+ 4 - 4
src/components/mobile/SalesRankingScrollList.tsx

@@ -3,13 +3,13 @@ import classNames from "classnames/bind";
 
 import Flicking from "@egjs/react-flicking";
 import styles from "./NFTScrollList.scss";
-import { ISalesRanking, INFT } from "metarare";
+import { ISaleRanking, INFT } from "metarare";
 import SalesRankingCard from "../common/SaleRankingCard";
 
 const cx = classNames.bind(styles);
 
 interface IOwnProps {
-  list: ISalesRanking[];
+  list: ISaleRanking[];
 }
 
 const SalesRankingScrollList: React.FC<IOwnProps> = ({ list }) => {
@@ -17,8 +17,8 @@ const SalesRankingScrollList: React.FC<IOwnProps> = ({ list }) => {
     <div className={cx("nft_scroll")}>
       <Flicking align="prev">
         {list.map((item, index) => (
-          <div key={`${item.id}_${index}`} className={cx("nft_panel")}>
-            <SalesRankingCard {...item} className="scroll" />
+          <div key={`${item.artist_id}_${index}`} className={cx("nft_panel")}>
+            <SalesRankingCard {...item} className="scroll" index={index} />
           </div>
         ))}
       </Flicking>

+ 1 - 1
src/components/pc/Header.scss

@@ -113,7 +113,7 @@
         }
 
         .btn_community {
-            padding: 0 20px;
+            padding: 0 0 0 20px;
             color: #888;
             font-size: 12px;
             line-height: 36px;

+ 33 - 26
src/components/pc/Header.tsx

@@ -18,8 +18,9 @@ import useLogin from "@src/hooks/useLogin";
 import { useHistory } from "react-router";
 import { fetchUserInfo } from "@src/store/reducers/UserReducer";
 import { YoutubeIcon } from "../common/icon/youtube";
-import { useOnClickOutside} from "usehooks-ts";
+import { useDebounce, useOnClickOutside} from "usehooks-ts";
 import AutoCompleteSearch from "../common/AutoCompleteSearch";
+import { fetchExploreHint } from "@src/store/reducers/ExploreReducer";
 
 const cx = classNames.bind(styles);
 
@@ -38,10 +39,11 @@ const Header: React.FC = () => {
   const { keyword, setKeyword, onKeyPress, handleSearch } = useSearch({
     onSearch,
   });
+  const debouncedValue = useDebounce<string>(keyword, 1000)
   const [showCommunity, setShowCommunity] = useState(false);
   const [showProfile, setShowProfile] = useState<boolean>(false);
   const { handleClickLoginButton, loginText } = useLogin();
-
+  const { hint } = useSelector((state) => state.explore)
   useEffect(() => {
     if (showProfile) {
       dispatch(fetchUserInfo());
@@ -54,23 +56,23 @@ const Header: React.FC = () => {
     }
   };
 
-  const fakeComplete = [
-    "test1",
-    "test2",
-    "test3",
-    "test4",
-    "test5",
-    "test6",
-    "test7",
-    "test8",
-    "test9",
-    "test10",
-  ];
+  const dispatchHint = (token_name: string) => {
+    console.log('%cHeader.tsx line:59 token_name', 'color: #007acc;', token_name);
+    dispatch(
+      fetchExploreHint({
+        token_name,
+      })
+    );
+  };
 
   useOnClickOutside(searchRef, () => {
     setShowAutoComplete(false);
   });
 
+  useEffect(() => {
+      dispatchHint(keyword)
+  }, [debouncedValue]);
+
   return (
     <div className={cx("header_wrap")}>
       <div className={cx("header_group")}>
@@ -97,14 +99,18 @@ const Header: React.FC = () => {
               <span className={cx("blind")}>Search</span>
               <IconSearchGray className={cx("ico_search")} />
             </button>
-            <AutoCompleteSearch show={showAutoComplete} data={fakeComplete} onClick={(e) => {
-              setShowAutoComplete(false);
-              SelectTextComplete(e);
-              setKeyword(e);
-            }} />
+            <AutoCompleteSearch
+              show={showAutoComplete}
+              data={hint}
+              onClick={(e) => {
+                setShowAutoComplete(false);
+                SelectTextComplete(e);
+                setKeyword(e);
+              }}
+            />
           </div>
           <ul className={cx("menu_list")}>
-            <li className={cx("menu_item")}>
+            {/* <li className={cx("menu_item")}>
               <Link to={URLInfo.EXPLORE} className={cx("menu")}>
                 탐색하기
               </Link>
@@ -118,17 +124,13 @@ const Header: React.FC = () => {
                   내 프로필
                 </Link>
               </li>
-            )}
+            )} */}
             {/* <li className={cx("menu_item")}>
               <Link to={URLInfo.ACTIVITY} className={cx("menu")}>
                 활동
               </Link>
             </li> */}
-            <li className={cx("menu_item")}>
-              <Link to={URLInfo.EXPLORE} className={cx("menu")}>
-                순위 100
-              </Link>
-            </li>
+
             {/* <li className={cx("menu_item")}>
               <a
                 href="https://reactnavigation.org/"
@@ -223,6 +225,11 @@ const Header: React.FC = () => {
                 </div>
               )}
             </li>
+            <li className={cx("menu_item")}>
+              <Link to={URLInfo.EXPLORE} className={cx("menu")}>
+                Top 100
+              </Link>
+            </li>
           </ul>
           <div className={cx("btn_area")}>
             <Link to={URLInfo.ACTIVITY} className={cx("link_activity")}>

+ 4 - 3
src/components/pc/SalesRankingScrollList.tsx

@@ -4,13 +4,13 @@ import classNames from 'classnames/bind';
 import Flicking, { ViewportSlot } from '@egjs/react-flicking';
 import { Arrow } from '@egjs/flicking-plugins';
 import styles from './NFTScrollList.scss';
-import { ISalesRanking, INFT } from 'metarare';
+import { ISaleRanking, INFT } from 'metarare';
 import SalesRankingCard from '../common/SaleRankingCard';
 
 const cx = classNames.bind(styles);
 
 interface IOwnProps {
-  list: ISalesRanking[];
+  list: ISaleRanking[];
 };
 
 const PANEL_COUNT = 7;
@@ -19,7 +19,7 @@ const SalesRankingScrollList: React.FC<IOwnProps> = ({
   list
 }) => {
   const plugins = [new Arrow()];
-  const [panel, setPanel] = useState<ISalesRanking[][]>([]);
+  const [panel, setPanel] = useState<ISaleRanking[][]>([]);
 
   useEffect(() => {
     if (list?.length) {
@@ -41,6 +41,7 @@ const SalesRankingScrollList: React.FC<IOwnProps> = ({
               {list.map((item, index) => (
                 <SalesRankingCard
                   {...item}
+                  index={index}
                   key={index}
                   className="scroll"
                 />

+ 1 - 1
src/constants/NodeEnv.ts

@@ -3,4 +3,4 @@ export const isDev = process.env.REACT_APP_STAGE !== 'production';
 export const devHost = 'https://alpha.meta-rare.net';
 export const realHost = 'https://meta-rare.net';
 export const host = isDev ? devHost : realHost;
-export const LOCAL_ENDPOINT = "localhost:9000";
+export const LOCAL_ENDPOINT = "https://4eb0-118-70-233-192.ngrok-free.app";

+ 8 - 60
src/containers/common/ArtistsScrollList.tsx

@@ -1,73 +1,21 @@
-import { IArtist } from 'metarare';
-import * as React from 'react';
+import { IArtist } from "metarare";
+import * as React from "react";
 import PCArtistScrollList from "@src/components/pc/ArtistScrollList";
 import MobileArtistScrollList from "@src/components/mobile/ArtistScrollList";
 
-export interface IArtistsScrollListProps {}
+export interface IArtistsScrollListProps {
+  list: IArtist[];
+}
 
-export default function ArtistsScrollList(props: IArtistsScrollListProps) {
+export default function ArtistsScrollList({ list }: IArtistsScrollListProps) {
   return (
     <>
       <div className={"mo_wrapper"}>
-        <MobileArtistScrollList list={dataFake} />
+        <MobileArtistScrollList list={list} />
       </div>
       <div className={"pc_wrapper"}>
-        <PCArtistScrollList list={dataFake} />
+        <PCArtistScrollList list={list} />
       </div>
     </>
   );
 }
-
-
-const dataFake: IArtist[] = [
-    {
-        id: 1,
-        name: 'John Doe',
-        thumbnail_image: 'https://picsum.photos/id/20/300/300',
-    },
-    {
-        id: 2,
-        name: 'Kasper Henderson',
-        thumbnail_image: 'https://picsum.photos/id/21/300/300',
-    },
-    {
-        id: 3,
-        name: 'Cooper Lynn',
-        thumbnail_image: 'https://picsum.photos/id/22/300/300',
-    },
-    {
-        id: 4,
-        name: 'Iris Stein',
-        thumbnail_image: 'https://picsum.photos/id/23/300/300',
-    },
-    {
-        id: 5,
-        name: 'Leland Richards',
-        thumbnail_image: 'https://picsum.photos/id/24/300/300',
-    },
-    {
-        id: 6,
-        name: 'Octavia Norris',
-        thumbnail_image: 'https://picsum.photos/id/25/300/300',
-    },
-    {
-        id: 7,
-        name: 'Leland Richards',
-        thumbnail_image: 'https://picsum.photos/id/26/300/300',
-    },
-    {
-        id: 8,
-        name: 'Octavia Norris',
-        thumbnail_image: 'https://picsum.photos/id/27/300/300',
-    },
-    {
-        id: 9,
-        name: 'Leland Richards',
-        thumbnail_image: 'https://picsum.photos/id/28/300/300',
-    },
-    {
-        id: 10,
-        name: 'Octavia Norris',
-        thumbnail_image: 'https://picsum.photos/id/29/300/300',
-    }
-]

+ 7 - 78
src/containers/common/SalesRankingscrollList.tsx

@@ -1,92 +1,21 @@
 import MobileSalesRankingScrollList from "@src/components/mobile/SalesRankingScrollList";
 import PCSalesRankingScrollList from "@src/components/pc/SalesRankingScrollList";
-import { ISalesRanking } from "metarare";
+import { ISaleRanking } from "metarare";
 import * as React from "react";
 
-export interface ISalesRankingScrollListProps {}
+export interface ISalesRankingScrollListProps {
+  list: ISaleRanking[]
+}
 
-export default function SalesRankingScrollList(props: ISalesRankingScrollListProps) {
+export default function SalesRankingScrollList({list}: ISalesRankingScrollListProps) {
   return (
     <>
       <div className={"mo_wrapper"}>
-        <MobileSalesRankingScrollList list={dataRankingFake} />
+        <MobileSalesRankingScrollList list={list} />
       </div>
       <div className={"pc_wrapper"}>
-        <PCSalesRankingScrollList list={dataRankingFake} />
+        <PCSalesRankingScrollList list={list} />
       </div>
     </>
   );
 }
-
-export const dataRankingFake: ISalesRanking[] = [
-  {
-    id: 1,
-    rank: 1,
-    name: "John Doe",
-    thumbnail_image: "https://picsum.photos/id/10/300/300",
-    total_revenue: 100000,
-  },
-  {
-    id: 2,
-    rank: 2,
-    name: "Kasper Henderson Kasper Henderson Kasper Henderson",
-    thumbnail_image: "https://picsum.photos/id/11/300/300",
-    total_revenue: 90000,
-  },
-  {
-    id: 3,
-    rank: 3,
-    name: "Cooper Lynn",
-    thumbnail_image: "https://picsum.photos/id/12/300/300",
-    total_revenue: 80000,
-  },
-  {
-    id: 4,
-    rank: 4,
-    name: "Iris Stein",
-    thumbnail_image: "https://picsum.photos/id/13/300/300",
-    total_revenue: 70000,
-  },
-  {
-    id: 5,
-    rank: 5,
-    name: "Leland Richards",
-    thumbnail_image: "https://picsum.photos/id/14/300/300",
-    total_revenue: 60000,
-  },
-  {
-    id: 6,
-    rank: 6,
-    name: "Octavia Norris",
-    thumbnail_image: "https://picsum.photos/id/15/300/300",
-    total_revenue: 50000,
-  },
-  {
-    id: 7,
-    rank: 7,
-    name: "Leland Richards",
-    thumbnail_image: "https://picsum.photos/id/16/300/300",
-    total_revenue: 40000,
-  },
-  {
-    id: 8,
-    rank: 8,
-    name: "Octavia Norris",
-    thumbnail_image: "https://picsum.photos/id/17/300/300",
-    total_revenue: 30000,
-  },
-  {
-    id: 9,
-    rank: 9,
-    name: "Leland Richards",
-    thumbnail_image: "https://picsum.photos/id/18/300/300",
-    total_revenue: 20000,
-  },
-  {
-    id: 10,
-    rank: 10,
-    name: "Octavia Norris",
-    thumbnail_image: "https://picsum.photos/id/19/300/300",
-    total_revenue: 10000,
-  },
-];

+ 40 - 16
src/pages/Home.tsx

@@ -8,35 +8,54 @@ import useExploreFilter from '@src/hooks/useExploreFilter';
 import { useCallbackAwaitStateSync } from 'use-callback-await-state-sync';
 import { useDispatch, useSelector } from 'react-redux';
 import { fetchExploreHome, resetExploreHome } from '@src/store/reducers/ExploreReducer';
-import { fetchHome } from '@src/store/reducers/HomeReducer';
+import { fetchHome, fetchHomeArtists } from '@src/store/reducers/HomeReducer';
 import Loading from '@src/components/common/Loading';
 import useMore from '@src/hooks/useMore';
 import ArtistsScrollList from '@src/containers/common/ArtistsScrollList';
 import SalesRankingScrollList from '@src/containers/common/SalesRankingscrollList';
 import { Link } from 'react-router-dom';
 import URLInfo from '@src/constants/URLInfo';
+import { fetchSaleRanking } from '@src/store/reducers/SaleReducer';
 
 const cx = classNames.bind(styles);
 
 const Home: React.FC = () => {
   const dispatch = useDispatch();
-  const { data, isLoaded } = useSelector(store => store.home);
-  const { list, isExploreListLoaded } = useSelector(store => store.explore);
+  const { data, isLoaded } = useSelector((store) => store.home);
+  const { list, isExploreListLoaded } = useSelector((store) => store.explore);
+  const { dataRaking } = useSelector((store) => store.sale);
+  const { dataArtists } = useSelector((store) => store.home)
 
   const filterProps = useExploreFilter({ list });
   const dispatchExploreHome = (offset: number, limit: number) => {
-    dispatch(fetchExploreHome({
-      offset,
-      limit,
-      collection_name: filterProps.collection,
-      currency: (filterProps.maxPrice && filterProps.minPrice) ? filterProps.selectedCurrency : '',
-      currency_amount_end: filterProps.maxPrice,
-      currency_amount_start: filterProps.minPrice,
-      network: filterProps.networkType,
-      sale_type: filterProps.saleType
-    }));
+    dispatch(
+      fetchExploreHome({
+        offset,
+        limit,
+        collection_name: filterProps.collection,
+        currency:
+          filterProps.maxPrice && filterProps.minPrice
+            ? filterProps.selectedCurrency
+            : "",
+        currency_amount_end: filterProps.maxPrice,
+        currency_amount_start: filterProps.minPrice,
+        network: filterProps.networkType,
+        sale_type: filterProps.saleType,
+      })
+    );
   };
 
+  const dispatchSaleRanking = () => {
+    dispatch(fetchSaleRanking({
+      limit: 25
+    }))
+  } 
+
+  const dispatchHomeArtists = () => {
+    dispatch(fetchHomeArtists({
+      limit: 25
+    }))
+  }
   const { onClickMore, showMore, resetPagination, defaultDisplayCnt } = useMore({ list, dispatchFnc: dispatchExploreHome, setSelectedFilterType: filterProps.setSelectedFilterType });
 
   const filterHandler = useCallbackAwaitStateSync(() => {
@@ -45,6 +64,11 @@ const Home: React.FC = () => {
     dispatchExploreHome(0, defaultDisplayCnt);
   });
 
+  useEffect(() => {
+    dispatchSaleRanking();
+    dispatchHomeArtists();
+  }, []);
+
   useEffect(() => {
     dispatch(resetExploreHome());
     resetPagination();
@@ -61,7 +85,7 @@ const Home: React.FC = () => {
         <Loading />
       ) : (
         <>
-          {!!data?.curating_list.length && (
+          {!!data?.curating_list?.length && (
             <div className={cx("banner_group")}>
               <BannerScrollList list={data.curating_list} />
             </div>
@@ -96,7 +120,7 @@ const Home: React.FC = () => {
           </div> */}
           <div className={cx("home_group")}>
             <strong className={cx("home_title")}>Registered Artists 🧑‍🎨</strong>
-            <ArtistsScrollList />
+            <ArtistsScrollList list={dataArtists} />
           </div>
           <div className={cx("home_group")}>
             <strong className={cx("home_title")}>
@@ -105,7 +129,7 @@ const Home: React.FC = () => {
                 See more
               </Link>
             </strong>
-            <SalesRankingScrollList />
+            <SalesRankingScrollList list={dataRaking} />
           </div>
         </>
       )}

+ 15 - 2
src/pages/Ranking.tsx

@@ -2,7 +2,8 @@ import * as React from 'react';
 import classNames from 'classnames/bind';
 import styles from './Ranking.scss';
 import RankingTable from '@src/components/common/RankingTable';
-import { dataRankingFake } from '@src/containers/common/SalesRankingscrollList';
+import { useDispatch, useSelector } from 'react-redux';
+import { fetchSaleRanking } from '@src/store/reducers/SaleReducer';
 
 const cx = classNames.bind(styles);
 
@@ -10,10 +11,22 @@ export interface IRankingProps {
 }
 
 export default function Ranking (props: IRankingProps) {
+  const { dataRaking } = useSelector((store) => store.sale);
+  const dispatch = useDispatch();
+  const dispatchSaleRanking = () => {
+    dispatch(fetchSaleRanking({
+      limit: 100
+    }))
+  } 
+  
+  React.useEffect(() => {
+    dispatchSaleRanking()
+  }, [])
+
   return (
     <div className={cx('ranking')}>
         <div className={cx('title')}>랭킹</div>
-        <RankingTable data={dataRankingFake} />
+        <RankingTable data={dataRaking} />
     </div>
   );
 }

+ 6 - 1
src/pages/Sale.tsx

@@ -150,7 +150,12 @@ const Sale: React.FC = () => {
               </div>
               <div className={cx("info_group")}>
                 <div className={cx("header_box")}>
-                  <div className={cx("name")}>{data.collection_name}</div>
+                  <Link
+                    to={URLInfo.getProfileCollectionUrl(data.collection_name)}
+                    className={cx("name")}
+                  >
+                    {data.collection_name}
+                  </Link>
                   <div className={cx("group")}>
                     <div className={cx("views")}>견해: 0</div>
                     <div className={cx("like")}>

+ 25 - 0
src/store/reducers/ExploreReducer.ts

@@ -6,12 +6,14 @@ type ExploreState = {
   isExploreListLoaded: boolean;
   list: INFT[];
   collectionList: ICollection[];
+  hint: string[]
 }
 
 const exploreState: ExploreState = {
   isExploreListLoaded: true,
   list: [],
   collectionList: [],
+  hint: []
 };
 
 export const fetchExploreHome = createAsyncThunk<Promise<any>, IFilter>(
@@ -38,6 +40,19 @@ export const fetchExploreSearch = createAsyncThunk<Promise<any>, IFilter>(
   }
 );
 
+export const fetchExploreHint = createAsyncThunk<Promise<any>, IFilter>(
+  'explore/fetchExploreHint',
+  async (filter, thunkAPI) => {
+    try {
+      const response = await APIs.fetchExploreHint(filter);
+      return response;
+    } catch (err) {
+      return thunkAPI.rejectWithValue('Something went wrong.');
+    }
+  }
+);
+
+
 export const fetchExploreKeyword = createAsyncThunk<Promise<any>, string>(
   'explore/fetchExploreKeyword',
   async (collectionName, thunkAPI) => {
@@ -83,6 +98,16 @@ export const slice = createSlice({
       ];
       state.isExploreListLoaded = true;
     },
+    [fetchExploreHint.pending.type]: (state: ExploreState, action) => {
+      state.isExploreListLoaded = false;
+    },
+    [fetchExploreHint.fulfilled.type]: (state: ExploreState, action) => {
+      const { payload } = action;
+
+      state.hint = payload;
+      state.isExploreListLoaded = true;
+    },
+
     [fetchExploreKeyword.fulfilled.type]: (state: ExploreState, action) => {
       const { payload } = action;
 

+ 29 - 2
src/store/reducers/HomeReducer.ts

@@ -1,15 +1,17 @@
 import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
 import APIs from '@src/api/APIs';
-import { IHome } from 'metarare';
+import { IArtist, IFilter, IHome } from 'metarare';
 
 type HomeState = {
   isLoaded: boolean;
   data: IHome | null;
+  dataArtists: IArtist[]
 }
 
 const homeState: HomeState = {
   isLoaded: false,
-  data: null
+  data: null,
+  dataArtists: []
 };
 
 export const fetchHome = createAsyncThunk(
@@ -24,6 +26,19 @@ export const fetchHome = createAsyncThunk(
   }
 );
 
+export const fetchHomeArtists = createAsyncThunk<Promise<any>, IFilter>(
+  'home/fetchHomeArtists',
+  async (filter, thunkAPI) => {
+    try {
+      const response = await APIs.fetchHomeArtists(filter);
+      return response;
+    } catch (err) {
+      return thunkAPI.rejectWithValue('Something went wrong.');
+    }
+  }
+);
+
+
 export const slice = createSlice({
   name: 'home',
   initialState: homeState,
@@ -42,6 +57,18 @@ export const slice = createSlice({
     [fetchHome.rejected.type]: (state: HomeState, action) => {
       state.isLoaded = false;
     },
+    [fetchHomeArtists.pending.type]: (state: HomeState, action) => {
+      state.isLoaded = true;
+    },
+    [fetchHomeArtists.fulfilled.type]: (state: HomeState, action) => {
+      const { payload } = action;
+
+      state.dataArtists = payload;
+      state.isLoaded = false;
+    },
+    [fetchHomeArtists.rejected.type]: (state: HomeState, action) => {
+      state.isLoaded = false;
+    },
   },
 });
 

+ 28 - 1
src/store/reducers/SaleReducer.ts

@@ -1,16 +1,18 @@
 import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
 import APIs from '@src/api/APIs';
-import { IBidHistory, ISale } from 'metarare';
+import { IBidHistory, IFilter, ISale, ISaleRanking } from 'metarare';
 
 type SaleState = {
   isDataLoaded: boolean;
   data: ISale | null;
   bid: IBidHistory[];
+  dataRaking: ISaleRanking[]
 }
 
 const saleState: SaleState = {
   isDataLoaded: true,
   data: null,
+  dataRaking: [],
   bid: [],
 };
 
@@ -26,6 +28,19 @@ export const fetchSale = createAsyncThunk<Promise<any>, number>(
   }
 );
 
+export const fetchSaleRanking = createAsyncThunk<Promise<any>, IFilter>(
+  'sale/fetchSaleRanking',
+  async (filter, thunkAPI) => {
+    try {
+      const response = await APIs.fetchSaleRanking(filter);
+      return response;
+    } catch (err) {
+      return thunkAPI.rejectWithValue('Something went wrong.');
+    }
+  }
+);
+
+
 export const fetchBid = createAsyncThunk<Promise<any>, number>(
   'sale/fetchBid',
   async (id, thunkAPI) => {
@@ -56,6 +71,18 @@ export const slice = createSlice({
     [fetchSale.rejected.type]: (state: SaleState, action) => {
       state.isDataLoaded = true;
     },
+    [fetchSaleRanking.pending.type]: (state: SaleState, action) => {
+      state.isDataLoaded = false;
+    },
+    [fetchSaleRanking.fulfilled.type]: (state: SaleState, action) => {
+      const { payload } = action;
+
+      state.dataRaking = payload;
+      state.isDataLoaded = true;
+    },
+    [fetchSaleRanking.rejected.type]: (state: SaleState, action) => {
+      state.isDataLoaded = true;
+    },
     [fetchBid.fulfilled.type]: (state: SaleState, action) => {
       const { payload } = action;