| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- import { useNavigate } from "react-router-dom";
- import { productApi } from "../../apis/productApi";
- import { startLoading, stopLoading } from "../../features/loading/loadingSlice";
- import { useAppDispatch } from "../../hooks/useRedux";
- import { OrderHistory } from "../../services/product/type";
- import { useMutation } from "@tanstack/react-query";
- import React, { useState, useEffect } from "react";
- import {
- convertOrderStatusToColor,
- convertOrderStatusToText,
- formatCurrency,
- formatNumber,
- } from "../../logic/loigicUtils";
- import { useTranslation } from "react-i18next";
- const OrderHistoryView = () => {
- const [searchOrder, setSearchOrder] = useState("");
- const lastDay = new Date(new Date().setDate(new Date().getDate() - 30))
- .toISOString()
- .split("T")[0];
- const today = new Date().toISOString().split("T")[0];
- // status is one of: -1, 1, 2
- const [status, setStatus] = useState("-1");
- const [fromDate, setFromDate] = useState(lastDay);
- const [toDate, setToDate] = useState(today);
- const [orders, setOrders] = useState<OrderHistory[]>([]);
- const { t } = useTranslation();
- const dispatch = useAppDispatch();
- const navigate = useNavigate();
- useEffect(() => {
- getOrderMutation.mutate();
- }, []);
- const getOrderMutation = useMutation({
- mutationFn: async () => {
- dispatch(startLoading({}));
- const res = await productApi.getOrderHistory({
- searchOrder: searchOrder,
- fromDate,
- toDate,
- status,
- });
- return res;
- },
- onSuccess: (data) => {
- dispatch(stopLoading());
- console.log("Get order history response data:", data);
- if (data && data.errorCode === "0") {
- console.log("Get order history successful");
- setOrders(data.data || []);
- } else {
- console.error("Get order history failed, no token received");
- }
- },
- onError: (error: any) => {
- dispatch(stopLoading());
- console.error("Get order history error:", error.response.data);
- },
- });
- // Input style matching LoginView
- const inputClass =
- "w-full bg-slate-50 border-2 border-transparent focus:border-[#EE0434]/20 rounded-2xl py-3 px-5 focus:outline-none focus:bg-white transition-all text-slate-700 font-bold placeholder:text-slate-300 text-sm md:text-base h-[50px]";
- // const filteredOrders = orders.filter((order) => {
- // const matchesSearch = order.id.includes(searchOrder);
- // const matchesStatus = status === "-1" || order.status === status;
- // return matchesSearch && matchesStatus;
- // });
- return (
- <div className="bg-[#fcfdfe] min-h-screen pb-20">
- {/* Breadcrumb */}
- <div className="max-w-7xl mx-auto px-4 py-4 md:py-6 border-b border-slate-50">
- <nav className="flex items-center space-x-2 text-xs md:text-sm text-slate-500 font-medium">
- <button
- // onClick={() => onViewChange(ViewMode.HOME)}
- className="hover:text-[#EE0434] text-[18px]"
- >
- {t("home")}
- </button>
- <svg
- className="w-3 h-3"
- fill="none"
- stroke="currentColor"
- viewBox="0 0 24 24"
- >
- <path
- d="M9 5l7 7-7 7"
- strokeWidth={2.5}
- strokeLinecap="round"
- strokeLinejoin="round"
- />
- </svg>
- <span className="text-slate-900 font-bold text-[18px]">
- {t("transactionHistory")}
- </span>
- </nav>
- </div>
- <div className="max-w-5xl mx-auto px-4 py-8 md:py-12">
- {/* Filter Bar */}
- <div className="grid grid-cols-1 md:grid-cols-12 gap-4 mb-8">
- <div className="md:col-span-4 relative">
- <input
- type="text"
- placeholder="Enter order id"
- className={inputClass}
- value={searchOrder}
- onChange={(e) => {
- setSearchOrder(e.target.value);
- getOrderMutation.mutate();
- }}
- />
- <button className="absolute right-2 top-1/2 -translate-y-1/2 w-8 h-8 bg-[#EE0434] rounded-xl flex items-center justify-center text-white shadow-md hover:scale-105 transition-transform">
- <svg
- className="w-4 h-4"
- fill="none"
- stroke="currentColor"
- viewBox="0 0 24 24"
- >
- <path
- strokeLinecap="round"
- strokeLinejoin="round"
- strokeWidth={2.5}
- d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
- />
- </svg>
- </button>
- </div>
- <div className="md:col-span-3">
- <input
- type="date"
- placeholder="From date"
- onFocus={(e) => (e.target.type = "date")}
- onBlur={(e) => (e.target.type = "date")}
- className={inputClass}
- value={fromDate}
- onChange={(e) => {
- setFromDate(e.target.value);
- getOrderMutation.mutate();
- }}
- />
- </div>
- <div className="md:col-span-3">
- <input
- type="date"
- placeholder="To date"
- onFocus={(e) => (e.target.type = "date")}
- onBlur={(e) => (e.target.type = "date")}
- className={inputClass}
- value={toDate}
- onChange={(e) => {
- setToDate(e.target.value);
- getOrderMutation.mutate();
- }}
- />
- </div>
- <div className="md:col-span-2 relative">
- <select
- className={`${inputClass} appearance-none cursor-pointer`}
- value={status}
- onChange={(e) => {
- setStatus(e.target.value);
- getOrderMutation.mutate();
- }}
- >
- {/* 1: chờ thanh toán 2: đã thanh toán, chờ xuất esim 3: thanh toán
- thất bại 4: đã trả esim */}
- <option value="-1">Trạng thái</option>
- <option value="1">Chờ thanh toán</option>
- <option value="2">Đã thanh toán, chờ xuất eSIM</option>
- <option value="3">Thanh toán thất bại</option>
- <option value="4">Đã trả eSIM</option>
- </select>
- <svg
- className="absolute right-4 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400 pointer-events-none"
- fill="none"
- stroke="currentColor"
- viewBox="0 0 24 24"
- >
- <path
- strokeLinecap="round"
- strokeLinejoin="round"
- strokeWidth={2}
- d="M19 9l-7 7-7-7"
- />
- </svg>
- </div>
- </div>
- {/* Order List */}
- <div className="space-y-4">
- {orders.length === 0 ? (
- <div className="text-center py-20 opacity-50">
- <p className="text-xl font-bold text-slate-400">
- No orders found
- </p>
- </div>
- ) : (
- orders.map((order) => (
- <div
- key={order.id}
- onClick={() =>
- navigate(`/order-history-detail/${order.id}`, {
- state: { id: order.id, orderHistory: order },
- })
- }
- className="bg-white rounded-[20px] p-6 shadow-sm border border-slate-100 hover:shadow-md transition-shadow flex flex-col md:flex-row justify-between items-start md:items-center gap-6"
- >
- <div className="space-y-3">
- <span
- className={`inline-block px-3 py-1 rounded-lg text-xs font-black uppercase tracking-wider ${convertOrderStatusToColor(
- order.status,
- )}`}
- >
- {convertOrderStatusToText(order.status)}
- </span>
- <div>
- <p className="text-xl font-black text-slate-900 tracking-tight">
- {order.orderCode}
- </p>
- <p className="text-sm font-medium text-slate-400 mt-1">
- {order.createdDate}
- </p>
- </div>
- </div>
- <div className="flex items-center justify-between w-full md:w-auto gap-8">
- <div className="flex -space-x-2">
- {/* {order.flags.map((flag, idx) => (
- <div
- key={idx}
- className="w-8 h-8 rounded-full border-2 border-white shadow-sm overflow-hidden z-0"
- >
- <img
- src={`https://flagcdn.com/w80/${flag}.png`}
- alt={flag}
- className="w-full h-full object-cover"
- />
- </div>
- ))} */}
- </div>
- <div className="text-right">
- <p className="text-xl font-black text-slate-900">
- {formatCurrency(order.paymentMoney, order.curency)}
- </p>
- <p className="text-sm font-bold text-slate-400">
- {formatCurrency(order.totalMoney, order.curency)}
- </p>
- </div>
- </div>
- </div>
- ))
- )}
- </div>
- </div>
- </div>
- );
- };
- export default OrderHistoryView;
|