diff --git a/src/base/components/common/chart/IncomeAreaChart.tsx b/src/base/components/common/chart/IncomeAreaChart.tsx index f5c19ae..2c1cf95 100644 --- a/src/base/components/common/chart/IncomeAreaChart.tsx +++ b/src/base/components/common/chart/IncomeAreaChart.tsx @@ -6,6 +6,7 @@ import { useTheme } from '@mui/material/styles'; // third-party import ReactApexChart, { Props as ChartProps } from 'react-apexcharts'; import useConfig from '@/base/hooks/useConfig'; +import useAnalytics from './useAnalytics'; // project import // import useConfig from 'hooks/useConfig'; @@ -45,6 +46,9 @@ const IncomeAreaChart = ({ slot }: Props) => { const line = theme.palette.divider; const [options, setOptions] = useState(areaChartOptions); + const {cusAnaWeek, kitchenAnaWeek} = useAnalytics(slot as ("month"| "week")); + console.log("cusAnaWeek=>", cusAnaWeek); + console.log("kitchenAnaWeek=>", kitchenAnaWeek); useEffect(() => { setOptions((prevState) => ({ @@ -105,19 +109,31 @@ const IncomeAreaChart = ({ slot }: Props) => { data: [0, 43, 14, 56, 24, 105, 68] } ]); - - useEffect(() => { + useEffect(()=>{ setSeries([ - { - name: 'Kitchen', - data: slot === 'month' ? [76, 85, 101, 98, 87, 105, 91, 114, 94, 86, 115, 35] : [31, 40, 28, 51, 42, 109, 100] - }, - { - name: 'Customer', - data: slot === 'month' ? [110, 60, 150, 35, 60, 36, 26, 45, 65, 52, 53, 41] : [11, 32, 45, 32, 34, 52, 41] - } - ]); - }, [slot]); + { + name: 'Kitchen', + data: kitchenAnaWeek + }, + { + name: 'Customer', + data: cusAnaWeek + } + ]); + },[cusAnaWeek, kitchenAnaWeek]) + // useEffect(() => { + // setSeries([ + // { + // name: 'Kitchen', + // data: slot === 'month' ? [76, 85, 101, 98, 87, 105, 91, 114, 94, 86, 115, 35] : [31, 40, 28, 51, 42, 109, 100] + // }, + // { + // name: 'Customer', + // data: slot === 'month' ? [110, 60, 150, 35, 60, 36, 26, 45, 65, 52, 53, 41] : [11, 32, 45, 32, 34, 52, 41] + // } + // ]); + // }, [slot]); + return ; }; diff --git a/src/base/components/common/chart/OrdersTable.tsx b/src/base/components/common/chart/OrdersTable.tsx index 67bd3af..bfd55a1 100644 --- a/src/base/components/common/chart/OrdersTable.tsx +++ b/src/base/components/common/chart/OrdersTable.tsx @@ -148,11 +148,11 @@ const OrderStatusRender = ({ status }: Props) => { color = 'success'; title = 'Paid'; break; - case "CANCEL": + case "CANCELED": color = 'error'; title = 'Rejected'; break; - case "COMPLETE": + case "COMPLETED": color = 'primary'; title = 'Complete'; break; diff --git a/src/base/components/common/chart/useAnalytics.tsx b/src/base/components/common/chart/useAnalytics.tsx new file mode 100644 index 0000000..4f492d0 --- /dev/null +++ b/src/base/components/common/chart/useAnalytics.tsx @@ -0,0 +1,105 @@ +import axiosClient from '@/base/service/axiosClient'; +import { useEffect, useState } from 'react' + + + +const dates = ['2023-10-30', '2023-10-31', '2023-11-01','2023-11-02', '2023-11-03', '2023-11-04', '2023-11-05'] +const months = [ + { + from: '2023-01-01T00:00:00.000', + to: '2023-01-31T23:59:59.000' + }, + { + from: '2023-02-01T00:00:00.000', + to: '2023-02-28T23:59:59.000' + }, + { + from: '2023-03-01T00:00:00.000', + to: '2023-03-31T23:59:59.000' + }, + { + from: '2023-04-01T00:00:00.000', + to: '2023-04-30T23:59:59.000' + }, + { + from: '2023-05-01T00:00:00.000', + to: '2023-05-31T23:59:59.000' + }, + { + from: '2023-06-01T00:00:00.000', + to: '2023-06-30T23:59:59.000' + }, + { + from: '2023-07-01T00:00:00.000', + to: '2023-07-31T23:59:59.000' + }, + { + from: '2023-08-01T00:00:00.000', + to: '2023-08-31T23:59:59.000' + }, + { + from: '2023-09-01T00:00:00.000', + to: '2023-09-30T23:59:59.000' + }, + { + from: '2023-10-01T00:00:00.000', + to: '2023-10-31T23:59:59.000' + }, + { + from: '2023-11-01T00:00:00.000', + to: '2023-11-30T23:59:59.000' + }, + { + from: '2023-12-01T00:00:00.000', + to: '2023-12-31T23:59:59.000' + } +]; +const useAnalytics = (type : "week" | "month") => { + const [kitchenAnaWeek, setKitchen] = useState([]); + const [cusAnaWeek, setCus] = useState([]); + const fetchKitchenWeek = async () => { + const rawRes = await Promise.all(months.map(m => axiosClient.get("/kitchen", {params: { + FromDate : m.from, + ToDate : m.to + }}).then(d=>d.data).then(d => d?.totalCount))) + setKitchen(rawRes) + } + const fetchCustomerWeek = async () => { + const rawRes = await Promise.all(months.map(m => axiosClient.get("/customer", {params: { + FromDate : m.from, + ToDate : m.to + }}).then(d=>d.data).then(d => d?.totalCount))) + setCus(rawRes); + } + + const fetchKitchenMonth = async () => { + const rawRes = await Promise.all(dates.map(d => axiosClient.get("/kitchen", {params: { + FromDate : d+"T00:00:00.000", + ToDate : d+"T23:59:59.000" + }}).then(d=>d.data).then(d => d?.totalCount))) + setKitchen(rawRes) + } + const fetchCustomerMonth = async () => { + const rawRes = await Promise.all(dates.map(d => axiosClient.get("/customer", {params: { + FromDate : d+"T00:00:00.000", + ToDate : d+"T23:59:59.000" + }}).then(d=>d.data).then(d => d?.totalCount))) + setCus(rawRes); + } + useEffect(()=>{ + if(type =="month"){ + fetchKitchenWeek(); + fetchCustomerWeek(); + }else { + fetchKitchenMonth(); + fetchCustomerMonth(); + } + },[type]) + return ({ + kitchenAnaWeek, + cusAnaWeek + } + ) +} + +export default useAnalytics \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx index aae245e..abe2f8e 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -9,7 +9,7 @@ import 'react-toastify/dist/ReactToastify.css'; * Fix regeneratorRuntime is not defined */ import "regenerator-runtime"; -// import "./utils/errorTracking/sentry"; +import "./utils/errorTracking/sentry"; import { LocalizationProvider } from "@mui/x-date-pickers"; import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; import { QueryClientProvider } from "react-query"; @@ -21,7 +21,6 @@ import { FirebaseProvider } from "./base/store/context/FirebaseContext.tsx"; ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( - diff --git a/src/modules/area/hooks/useAreaTable.tsx b/src/modules/area/hooks/useAreaTable.tsx index d855a3a..fc797dc 100644 --- a/src/modules/area/hooks/useAreaTable.tsx +++ b/src/modules/area/hooks/useAreaTable.tsx @@ -73,13 +73,17 @@ const useAreaTable = (props: Props) => { }, meta: { align: "left" - } + }, + enableSorting:false, + }), columnHelper.accessor("name", { header: "Area Name", meta: { align: "left" - } + }, + enableSorting:false, + }), columnHelper.accessor("noOfKitchens", { header: "Number of kitchens", @@ -91,7 +95,8 @@ const useAreaTable = (props: Props) => { ), meta: { align: "right" - } + }, + enableSorting:false, }), columnHelper.accessor("createdDate", { header: "CreatedDate", @@ -100,7 +105,9 @@ const useAreaTable = (props: Props) => { }, meta: { align: "left" - } + }, + enableSorting:false, + }), // eslint-disable-next-line @typescript-eslint/no-explicit-any columnHelper.accessor("action", { diff --git a/src/modules/customer/pages/list/list.tsx b/src/modules/customer/pages/list/list.tsx index 2ba9cd2..e2270ab 100644 --- a/src/modules/customer/pages/list/list.tsx +++ b/src/modules/customer/pages/list/list.tsx @@ -62,7 +62,8 @@ const CustomerList = () => { refreshCustomerData, setSortState, deleteCustomer: {mutateAsync : mutateDelete}, - totalRows + totalRows, + setKeyword } = useCustomerData(); useEffect(()=>{ refreshCustomerData() @@ -109,7 +110,7 @@ const CustomerList = () => { ), enableSorting: false, }), - columnHelper.accessor("id", { + columnHelper.accessor("no", { header: () => { return "#"; }, @@ -146,7 +147,9 @@ const CustomerList = () => { }, meta: { align : "left" - } + }, + enableSorting: false, + }), // columnHelper.accessor("user.avatarUrl", { // header: "Avatar", @@ -176,7 +179,8 @@ const CustomerList = () => { ), meta: { align : "left" - } + }, + enableSorting: false, }), columnHelper.accessor("orderQuantity", { header: "Order", @@ -185,7 +189,8 @@ const CustomerList = () => { ), meta: { align : "right" - } + }, + enableSorting: false, }), columnHelper.accessor("spentMoney", { header: "Spent", @@ -200,7 +205,9 @@ const CustomerList = () => { ), meta: { align : "right" - } + }, + enableSorting: false, + }), columnHelper.accessor("status", { header: "Status", @@ -340,7 +347,7 @@ const CustomerList = () => { } - onSearchKeywordChange={(q) => console.log(q)} + onSearchKeywordChange={(q) => setKeyword(q)} onSortByChange={(sort) => setSortState(sort)} filter={{ isShow: true, diff --git a/src/modules/customer/service/customer.api.ts b/src/modules/customer/service/customer.api.ts index b56d9ee..22216f4 100644 --- a/src/modules/customer/service/customer.api.ts +++ b/src/modules/customer/service/customer.api.ts @@ -14,13 +14,22 @@ interface CustomerGetParams { keyword?: string; // filter : } +const customerKeyMap = { + no : "No", + email :"Email", + status : "Status" +} const CustomerApi = { getCustomers: (params: CustomerGetParams) => { const endpoint = "/customer"; return axiosClient.get>(endpoint, { params: { PageNumber: params.paging.pageIndex + 1 ?? 1, - PageSize: params.paging?.pageSize ?? 10 + PageSize: params.paging?.pageSize ?? 10, + KeySearch : params?.keyword??"", + ...((!!params?.sort ?? false) && (!!params?.sort?.[0] ?? false) ? { + OrderBy: `${customerKeyMap[params?.sort?.[0]?.id??"no"]??"No"}:${params?.sort?.[0]?.desc? "desc" : "asc"}`, + } :{}) }, }); diff --git a/src/modules/kitchen/components/card/MealCard.tsx b/src/modules/kitchen/components/card/MealCard.tsx index 326153d..e1fab4f 100644 --- a/src/modules/kitchen/components/card/MealCard.tsx +++ b/src/modules/kitchen/components/card/MealCard.tsx @@ -1,19 +1,21 @@ import { Meal } from "@/types/@mk/entity/meal"; -import { imageUrl } from "@/utils/@mk/helper"; +import { checkDueStatus, imageUrl } from "@/utils/@mk/helper"; import { DeleteTwoTone, EditOutlined } from "@ant-design/icons"; import { - CardContent, - CardMedia, - Divider, - Grid, - Stack, - ToggleButton, - ToggleButtonGroup, - Typography, - useTheme, + Box, CardContent, + CardMedia, Chip, Divider, + Grid, + Stack, + ToggleButton, + ToggleButtonGroup, + Typography, + useTheme } from "@mui/material"; import MainCard from "@ui/MainCard"; +import { Image } from "@ui/common/image"; import moment from "moment"; +import { useMemo } from "react"; +import NumberFormat from "react-number-format"; import "swiper/css"; import "swiper/css/navigation"; import "swiper/css/pagination"; @@ -27,6 +29,19 @@ type Props = { const MealCard = ({ meal, onEdit, onDelete }: Props) => { const theme = useTheme(); + const StatusChip = useMemo(()=>{ + switch (checkDueStatus(meal?.serviceFrom, meal?.serviceTo)) { + case 0: + return + + + case 1: + return + + default: + return + } + },[meal]) return ( { // maxHeight: "24rem" }}> - + + {meal?.name} - {/* - {meal.description} - */} + {StatusChip} + + + { > Price - - {meal?.price}đ + + { lg={3} > - Serve to + Serve Quantity {meal?.serviceQuantity} diff --git a/src/modules/kitchen/hook/useKitchenData.ts b/src/modules/kitchen/hook/useKitchenData.ts index 73102b4..ffdbaac 100644 --- a/src/modules/kitchen/hook/useKitchenData.ts +++ b/src/modules/kitchen/hook/useKitchenData.ts @@ -1,8 +1,8 @@ -import { Kitchen, KitchenAdmin } from "@/types/@mk/entity/kitchen"; +import { Kitchen } from "@/types/@mk/entity/kitchen"; import { PaginationState, SortingState } from "@tanstack/react-table"; import { useEffect, useState } from "react"; import { useMutation, useQuery } from "react-query"; -import KitchenApi from "../service/kitchen.api"; +import KitchenApi, { UpdateKitchenRequest } from "../service/kitchen.api"; import UserApi from "../service/user.api"; const useKitchenData = () => { @@ -62,7 +62,7 @@ const useKitchenData = () => { } ); - const updateKitchenFunction = async (kitchen: KitchenAdmin) => { + const updateKitchenFunction = async (kitchen:UpdateKitchenRequest) => { const response = await KitchenApi.updateKitchen(kitchen); return response?.data; diff --git a/src/modules/kitchen/hook/useKitchenForm.tsx b/src/modules/kitchen/hook/useKitchenForm.tsx index d9a7cf7..e4e810d 100644 --- a/src/modules/kitchen/hook/useKitchenForm.tsx +++ b/src/modules/kitchen/hook/useKitchenForm.tsx @@ -8,6 +8,7 @@ import { KitchenStatus } from "@/types/@mk/enum/kitchenStatus"; import { useNavigate } from "react-router-dom"; import { toast } from "react-toastify"; import useKitchenData from "./useKitchenData"; +import axios from "axios"; export interface KitchenForm extends ManipulateCustomerForm { name?: string; @@ -16,11 +17,14 @@ export interface KitchenForm extends ManipulateCustomerForm { userId?: string; user: User; isCreateUser?: boolean; + address?: string; } const useKitchenForm = () => { const nav = useNavigate(); - const { createKitchenFunction } = useKitchenData(); + const { createKitchenFunction, updateKitchen:{ + mutateAsync: updateKitchenFunc + } } = useKitchenData(); const { putObject } = useAwsS3(); const { createKitchenOwner: { mutateAsync: createKitchenOwnerAsync }, @@ -69,7 +73,21 @@ const useKitchenForm = () => { nav("/kitchen"); } }; - return { createKitchenHandler }; + const updateKitchenHandler = async (formValues: KitchenForm,id: string) => { + const { position, name } = formValues; + const addressEndpoint = `https://revgeocode.search.hereapi.com/v1/revgeocode?at=${position?.lat}%2C${position?.lng}&lang=en-US&apiKey=7h1jyg35V5JfNIgPA8m1XEN39K9giRbtrfNj8nJ5kd4` + const address = (await axios.get(addressEndpoint)).data; + let addressString = "Unknown"; + if(address){ + addressString = address?.items?.[0]?.title??"Unknown" + } + const result= await updateKitchenFunc({name, location:position, address:addressString, id}) + if(result){ + toast.success("Update kitchen successfully "); + nav("/kitchen"); + } + } + return { createKitchenHandler, updateKitchenHandler }; }; export default useKitchenForm; diff --git a/src/modules/kitchen/pages/KitchenCreatePage.tsx b/src/modules/kitchen/pages/KitchenCreatePage.tsx index aed6cf5..a36a210 100644 --- a/src/modules/kitchen/pages/KitchenCreatePage.tsx +++ b/src/modules/kitchen/pages/KitchenCreatePage.tsx @@ -22,7 +22,7 @@ import { FormProvider, useForm } from "react-hook-form"; import { useNavigate } from "react-router-dom"; import useKitchenForm, { KitchenForm } from "../hook/useKitchenForm"; -interface AreaOptions extends AreaAdmin { +export interface AreaOptions extends AreaAdmin { label: string; } diff --git a/src/modules/kitchen/pages/KitchenEditPage.tsx b/src/modules/kitchen/pages/KitchenEditPage.tsx new file mode 100644 index 0000000..afb9988 --- /dev/null +++ b/src/modules/kitchen/pages/KitchenEditPage.tsx @@ -0,0 +1,169 @@ + + +import useAreaData from "@/modules/area/hooks/useAreaData"; +import { + Autocomplete, + Box, + Button, + Grid, + InputLabel, + TextField +} from "@mui/material"; +import { Stack } from "@mui/system"; +import MainCard from "@ui/MainCard"; +import HereMapSelect from "@ui/common/map/HereMapSelect"; +import React, { useEffect, useState } from "react"; +import { FormProvider, useForm } from "react-hook-form"; +import { useNavigate, useParams } from "react-router-dom"; +import useKitchenData from "../hook/useKitchenData"; +import useKitchenForm, { KitchenForm } from "../hook/useKitchenForm"; +import { AreaOptions } from "./KitchenCreatePage"; + +const KitchenEditPage = () => { + const nav = useNavigate(); + const {id} = useParams(); + const {setId, kitchenDetail} = useKitchenData(); + const [areaOptions, setAreaOptions] = useState([]); + const [areaKeyword, setAreaKeyword] = useState(""); + const [selectedArea, setSelectedArea] = useState(null); + const { updateKitchenHandler } = useKitchenForm(); + const [position, setPosition] = useState<{ lat: number; lng: number }>(); + + const methods = useForm({ + mode: "all", + // resolver: yupResolver(CustomerSchema), + }); + const { setValue } = methods; + const { areaData } = useAreaData(); + + const handleAutoAreaInputChange = ( + event: React.SyntheticEvent, + value: string + ) => { + setAreaKeyword(value); + }; + useEffect(()=>{ + if(id){ + setId(id); + } + },[id]) + useEffect(()=>{ + if(kitchenDetail?.data && kitchenDetail?.data?.location ){ + setPosition({ + lat: kitchenDetail?.data?.location?.lat, + lng: kitchenDetail?.data?.location?.lng + }) + setAreaKeyword(kitchenDetail?.data?.area?.name) + setValue("areaId", kitchenDetail?.data?.areaId); + setValue("name", kitchenDetail?.data?.name); + + + } + },[kitchenDetail]) + useEffect(() => { + // fetchNewArea(areaKeyword); + //TODO remove + // setAreaOptions([]); + }, [areaKeyword]); + + + useEffect(() => { + if (areaData) { + setAreaOptions( + areaData.map((area) => ({ + ...area, + label: area.name, + })) ?? [] + ); + } + }, [areaData]); + + useEffect(() => { + if (selectedArea) { + setValue("areaId", selectedArea.id); + } + }, [selectedArea, setValue]); + useEffect(() => { + if (position) { + setValue("position", position); + } + }, [position, setValue]); + return ( + + { + updateKitchenHandler(formValues,id ); + })}> + + + + + + + + Kitchen Name + + + + + + Location + + setPosition(pos)} + area={selectedArea} + /> + + + + Area + + { + setSelectedArea(areaSelected); + }} + onInputChange={handleAutoAreaInputChange} + filterOptions={(x) => + x.filter((x) => x.name.includes(areaKeyword)) + } + renderInput={(params) => ( + + )} + /> + + + + + + + + + + + + + ); +}; + +export default KitchenEditPage; diff --git a/src/modules/kitchen/pages/KitchenListPage.tsx b/src/modules/kitchen/pages/KitchenListPage.tsx index d9fdae2..00ff8e9 100644 --- a/src/modules/kitchen/pages/KitchenListPage.tsx +++ b/src/modules/kitchen/pages/KitchenListPage.tsx @@ -18,14 +18,15 @@ const KitchenListPage = () => { const [deleteConfirmation, setDeleteConfirmation] = useState(false); const [selectionRows, setSelectionRows] = useState(); const [deleteId, setDeleteId] = useState(); + const nav = useNavigate(); + const { columnsDef } = useKitchenTable({ - handleEditClick: () => console.log("TODO: implement"), + handleEditClick: (kitchen) => nav(`/kitchen/${kitchen?.id}/edit`), handleDeleteClick: (id) => { setDeleteConfirmation(true); setDeleteId(id); }, }); - const nav = useNavigate(); const { setPagination, setSortState, diff --git a/src/modules/kitchen/pages/KitchenProfile.tsx b/src/modules/kitchen/pages/KitchenProfile.tsx index 4654b55..7aabae7 100644 --- a/src/modules/kitchen/pages/KitchenProfile.tsx +++ b/src/modules/kitchen/pages/KitchenProfile.tsx @@ -12,12 +12,13 @@ import { useMediaQuery, } from "@mui/material"; import { Theme } from "@mui/material/styles"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; // third-party import { AimOutlined, MailOutlined, PhoneOutlined } from "@ant-design/icons"; import Avatar from "@ui/@extended/Avatar"; import MainCard from "@ui/MainCard"; +import axios from "axios"; import NumberFormat from "react-number-format"; import { useParams } from "react-router-dom"; import "swiper/css"; @@ -31,6 +32,7 @@ const KitchenProfile = () => { const matchDownMD = useMediaQuery((theme: Theme) => theme.breakpoints.down("md") ); + const [fb, setFb] = useState([]); const { id } = useParams(); const { kitchenDetail, @@ -48,8 +50,15 @@ const KitchenProfile = () => { useEffect(() => { if (id) { setId(id); + fetchFeeback(id).then(data=>setFb(data)) } }, [id, setId]); + console.log(fb); + + const fetchFeeback = async(id: string)=> { + const res = await axios.get('https://momkitchen.wyvernpserver.tech/api/feedback/kitchen/'+id) + return res.data?.data; + } return ( @@ -67,7 +76,7 @@ const KitchenProfile = () => { /> - {kitchenDetail?.data?.owner?.ownerAvatarUrl ? ( + {!isLoadingDetail ? ( { )} - {kitchenDetail?.data?.name ? ( + {!isLoadingDetail ? ( {kitchenDetail?.data?.name} @@ -168,7 +177,7 @@ const KitchenProfile = () => { - {ownerDetail?.email ? ( + {!isLoadingDetail ? ( {ownerDetail?.email} @@ -186,7 +195,7 @@ const KitchenProfile = () => { - {ownerDetail?.phone ? ( + {!isLoadingDetail ? ( {ownerDetail?.phone} @@ -204,7 +213,7 @@ const KitchenProfile = () => { - {kitchenDetail?.data?.area?.name ? ( + {!isLoadingDetail ? ( {kitchenDetail?.data?.area?.name} @@ -252,7 +261,7 @@ const KitchenProfile = () => { Fullname - {ownerDetail?.fullName ? ( + {!isLoadingDetail ? ( {ownerDetail?.fullName} ) : ( { - DoB + Area {!isLoadingDetail ? ( - Mr. Deepen Handgun + {kitchenDetail?.data?.area?.name} ) : ( { Country {!isLoadingDetail ? ( - {kitchenDetail?.data?.area?.name} + Viet nam ) : ( { direction="horizontal" onSlideChange={() => console.log("slide change")} onSwiper={(swiper) => console.log(swiper)}> - - - - - - - - - - - - - + + {fb && fb.length>0 ? fb?.map(f => ( )) + : <>This kitchen does not have feebacks yet + } + + {/* */} diff --git a/src/modules/kitchen/service/kitchen.api.ts b/src/modules/kitchen/service/kitchen.api.ts index f8632c9..e29f256 100644 --- a/src/modules/kitchen/service/kitchen.api.ts +++ b/src/modules/kitchen/service/kitchen.api.ts @@ -16,6 +16,15 @@ interface KitchenGetParams { keyword?: string; // filter : } +export interface UpdateKitchenRequest { + "name": string, + "location": { + "lat": number, + "lng": number + }, + address?: string, + id?: string +} const KitchenApi = { getKitchens: async (params: KitchenGetParams):Promise> => { const endpoint = "/kitchen"; @@ -81,8 +90,8 @@ const KitchenApi = { }, }); }, - updateKitchen: (kitchen: KitchenAdmin) => { - const endpoint = "/kitchen"; + updateKitchen: (kitchen: UpdateKitchenRequest) => { + const endpoint = "/kitchen/"+kitchen?.id; return axiosClient.put(endpoint, kitchen); }, deleteKitchen: (id: string) => { @@ -125,7 +134,6 @@ const KitchenApi = { const endpoint = "/kitchen"; const addressEndpoint = `https://revgeocode.search.hereapi.com/v1/revgeocode?at=${kitchen.location?.lat}%2C${kitchen.location?.lng}&lang=en-US&apiKey=7h1jyg35V5JfNIgPA8m1XEN39K9giRbtrfNj8nJ5kd4` const address = (await axios.get(addressEndpoint)).data; - console.log("ADDRESS", address); let addressString = "Unknown"; if(address){ addressString = address?.items?.[0]?.title??"Unknown" diff --git a/src/modules/order/hook/useOrderData.ts b/src/modules/order/hook/useOrderData.ts index 01fb9b1..cc33c8e 100644 --- a/src/modules/order/hook/useOrderData.ts +++ b/src/modules/order/hook/useOrderData.ts @@ -1,8 +1,10 @@ import { OrderAdmin } from "@/types/@mk/entity/order"; import { FilterState } from "@/types/common/pagination/FilterState"; import { PaginationState, SortingState } from "@tanstack/react-table"; +import moment from "moment"; import { useState } from "react"; import { useMutation, useQuery } from "react-query"; +import { toast } from "react-toastify"; import OrderApi from "../service/order.api"; const useOrderData = (enable?: boolean) => { @@ -92,7 +94,20 @@ const useOrderData = (enable?: boolean) => { const deleteOrder = useMutation(deleteOrderFunction, { // You can specify onSuccess and onError callbacks here }); - + const batchExportFunction = async ()=>{ + const ApiEndpoint = `http://momkitchen.wyvernpserver.tech/api/v1/order?PageNumber=1&PageSize=50${keyword?"&KeySearch="+keyword:""}${!!filter?.to?.value ?? false? "&ToDate="+ moment(filter?.to?.value as string).add(30, "hours").add(59, "minutes").utc().toISOString() :""}${!!filter?.from?.value ?? false? "&FromDate="+ moment(filter?.from?.value as string).add(7, "hours").utc().toISOString() :""}${!!filter?.tab?.value ?? false? "&OrderStatus="+ filter?.tab?.value :""}`; + const EmailSender = "phonglethanh2@gmail.com"; + await OrderApi.exportOrder({ + ApiEndpoint, + EmailSender + }) + console.log({ + ApiEndpoint, + EmailSender + }); + + toast.success("The exporting is being processed! You will receive email after it will have finished") + } return { orderData, setSortState, @@ -106,6 +121,7 @@ const useOrderData = (enable?: boolean) => { totalRows, orderTotalRows:totalRows, refreshOrderData, + batchExportFunction }; }; diff --git a/src/modules/order/hook/useOrderTable.tsx b/src/modules/order/hook/useOrderTable.tsx index 4230072..7425d9b 100644 --- a/src/modules/order/hook/useOrderTable.tsx +++ b/src/modules/order/hook/useOrderTable.tsx @@ -56,7 +56,7 @@ const useOrderTable = (props: Props) => { align: "left", }, }), - columnHelper.accessor("id", { + columnHelper.accessor("no", { header: "#", cell: ({ row }) => { return ( @@ -96,6 +96,8 @@ const useOrderTable = (props: Props) => { ); }, meta: { align: "left" }, + enableSorting: false, + }), columnHelper.accessor("meal", { header: "Meal", @@ -131,6 +133,7 @@ const useOrderTable = (props: Props) => { {renderValue()} ), + enableSorting: false, }), columnHelper.accessor("totalPrice", { header: "totalPrice ", @@ -207,7 +210,7 @@ const useOrderTable = (props: Props) => { case OrderStatus.UNPAID: return ( { case OrderStatus.PAID: return ( { case OrderStatus.PENDING: return ( { default: return ( { deleteOrder: { mutateAsync: deleteOrderFunc }, // updateOrder, totalRows, + batchExportFunction } = useOrderData(); const { columnsDef } = useOrderTable({ handleEditClick: () => { @@ -126,7 +127,7 @@ const OrderListPage = () => { variant="shadow" startIcon={} onClick={()=>{ - //TODO batch export kafka + batchExportFunction(); }}> {t("export_order")} diff --git a/src/modules/order/service/order.api.ts b/src/modules/order/service/order.api.ts index b3386bd..2ded84e 100644 --- a/src/modules/order/service/order.api.ts +++ b/src/modules/order/service/order.api.ts @@ -11,6 +11,12 @@ interface OrderGetParams { keyword?: string; filter?: FilterState; } +const orderKeyMap = { + no : "No", + totalPrice: "TotalPrice", + createdDate: "CreatedDate", + status: "Status" +} const OrderApi = { getOrders: async (params: OrderGetParams) => { const endpoint = "/order"; @@ -23,21 +29,21 @@ const OrderApi = { KeySearch: params?.keyword, ...(!!params?.filter?.to?.value ?? false ? { - ToDate: moment(params?.filter?.to?.value as string).toISOString(), + ToDate: moment(params?.filter?.to?.value as string).add(30, "hours").add(59, "minutes").toISOString(), } : {}), ...(!!params?.filter?.from?.value ?? false ? { FromDate: moment( params?.filter?.from?.value as string - ).toISOString(), + ).add(7, "hours").toISOString(), } : {}), ...(!!params?.filter?.tab?.value ?? false ? { OrderStatus : params?.filter?.tab?.value }:{}), ...((!!params?.sort ?? false) && (!!params?.sort?.[0] ?? false) ? { - OrderBy: `${params?.sort?.[0]?.id}:${params?.sort?.[0]?.desc? "desc" : "asc"}`, + OrderBy: `${orderKeyMap[params?.sort?.[0]?.id??"no"]??"CreatedDate"}:${params?.sort?.[0]?.desc? "desc" : "asc"}`, } :{}) }, @@ -64,5 +70,17 @@ const OrderApi = { params: { id }, }); }, + exportOrder: async ({ApiEndpoint,EmailSender}:{ + ApiEndpoint: string + EmailSender: string + })=>{ + const endpoint = "/order/xlsx"; + return await axiosClient.get(endpoint, { + params : { + ApiEndpoint, + EmailSender + } + }) + } }; export default OrderApi; diff --git a/src/route/Route.tsx b/src/route/Route.tsx index 4345339..d36d466 100644 --- a/src/route/Route.tsx +++ b/src/route/Route.tsx @@ -24,6 +24,7 @@ import KitchenProfileTray from "@/modules/kitchen/pages/KitchenProfileTray"; import KitchenProfileDish from "@/modules/kitchen/pages/KitchenProfileDish"; import OrderDetailPage from "@/modules/order/pages/detail/OrderDetailPage"; import KitchenProfileMeal from "@/modules/kitchen/pages/KitchenProfileMeal"; +import KitchenEditPage from "@/modules/kitchen/pages/KitchenEditPage"; const routes: RouteObject[] = [ { path: "/", @@ -94,6 +95,9 @@ const routes: RouteObject[] = [ { path: "meals", element: + },{ + path:"edit", + element: } ] } diff --git a/src/types/@mk/enum/orderStatus.ts b/src/types/@mk/enum/orderStatus.ts index fe494f1..60fd3f2 100644 --- a/src/types/@mk/enum/orderStatus.ts +++ b/src/types/@mk/enum/orderStatus.ts @@ -1,7 +1,7 @@ export enum OrderStatus { PENDING = "PENDING", - COMPLETE = "COMPLETE", + COMPLETE = "COMPLETED", PAID = "PAID", UNPAID = "UNPAID", - CANCEL = "CANCEL" + CANCEL = "CANCELED" } \ No newline at end of file diff --git a/src/utils/@mk/helper.ts b/src/utils/@mk/helper.ts index e691941..7ccc32f 100644 --- a/src/utils/@mk/helper.ts +++ b/src/utils/@mk/helper.ts @@ -1,3 +1,4 @@ +import moment from "moment"; export const imageUrl = (rawUrl: string) => { if(!rawUrl.includes("http")){ @@ -8,4 +9,20 @@ export const imageUrl = (rawUrl: string) => { }else{ return rawUrl; } -} \ No newline at end of file +} +export function checkDueStatus(startDateStr: string, endDateStr: string): -1 | 0 | 1 { + const currentDateTime = moment(); + const startDateTime = moment(startDateStr, 'DD/MM/YYYY HH:mm'); + const endDateTime = moment(endDateStr, 'DD/MM/YYYY HH:mm'); + + if (currentDateTime.isBefore(startDateTime)) { + // Upcoming + return -1; + } else if (currentDateTime.isBetween(startDateTime, endDateTime, undefined, '[]')) { + // During + return 0; + } else { + // Overdue + return 1; + } + } \ No newline at end of file diff --git a/src/utils/errorTracking/sentry/index.ts b/src/utils/errorTracking/sentry/index.ts index 72954b6..a431835 100644 --- a/src/utils/errorTracking/sentry/index.ts +++ b/src/utils/errorTracking/sentry/index.ts @@ -2,6 +2,7 @@ import * as Sentry from "@sentry/react"; Sentry.init({ dsn: import.meta.env.VITE_SENTRY_DSN, + integrations: [ new Sentry.BrowserTracing(), new Sentry.Replay(),