Skip to content

Commit

Permalink
[Feat/#35] 관리자 페이지 (#43)
Browse files Browse the repository at this point in the history
* feat: admin 페이지 라우팅 설정

* feat: 활성화된 탭에 따라 보여지는 컴포넌트 나누기

* feat: 어드민 페이지 구조수정

* feat: 관리자페이지 작업 중

* feat: filter 박스 퍼블리싱

* feat: 어드민 주문조회 테이블 퍼블리싱

* feat: 배송 가능 날짜 페이지 퍼블리싱

* feat: 상품 조회 페이지 퍼블리싱

* feat: 배송날짜 페이지 useFetchDeliveryDate 연결하기

* feat: 상품조회 페이지 퍼블리싱(추가/삭제 버튼, 토글 스위치 추가)

* feat: 관리자페이지 퍼블리싱

* feat: 관리자페이지 API 연동중

* feat: 주문조회 페이지 API 연동

* feat: 상품 판매상태 변경 API 연동

* feat: 상품조회 페이지 API 연동

* feat: 배송가능날짜 API 연동

---------

Co-authored-by: Parkchaeyeon <ccyy1029@naver.com>
  • Loading branch information
gudusol and chaeneey authored Sep 26, 2024
1 parent 128b99a commit 173d232
Show file tree
Hide file tree
Showing 53 changed files with 1,793 additions and 12 deletions.
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@
"@emotion/react": "^11.13.3",
"@tanstack/react-query": "^5.56.2",
"axios": "^1.7.7",
"dayjs": "^1.11.13",
"jotai": "^2.9.3",
"react": "^18.3.1",
"react-calendar": "^5.0.0",
"react-datepicker": "^7.4.0",
"react-daum-postcode": "^3.1.3",
"react-dom": "^18.3.1",
"react-router-dom": "^6.26.1"
"react-router-dom": "^6.26.1",
"react-select": "^5.8.1",
"react-switch": "^7.0.0"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
Expand Down
8 changes: 8 additions & 0 deletions public/svg/ic_arrow_down.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions public/svg/ic_arrow_up.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Global, ThemeProvider } from "@emotion/react";
import { homeRoutes, orderInfoRoutes } from "@routes";
import { adminRoutes, homeRoutes, orderInfoRoutes } from "@routes";
import GlobalStyle from "@styles/global";
import theme from "@styles/theme";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { createBrowserRouter, RouterProvider } from "react-router-dom";

const allRoutes = [...homeRoutes, ...orderInfoRoutes];
const allRoutes = [...homeRoutes, ...orderInfoRoutes, ...adminRoutes];
const router = createBrowserRouter([...allRoutes]);

function App() {
Expand Down
Empty file removed src/apis/.keep
Empty file.
29 changes: 29 additions & 0 deletions src/apis/domains/admin/useDeleteProduct.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { del } from "@apis/api";
import { QUERY_KEY } from "@apis/queryKeys/queryKeys";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { ErrorResponse, MutateResponseType } from "@types";

const deleteProduct = async (
productIdList: number[]
): Promise<MutateResponseType> => {
try {
const response = await del<MutateResponseType>(`api/v1/product`, {
data: productIdList,
});
return response.data;
} catch (error) {
const errorResponse = error as ErrorResponse;
const errorData = errorResponse.response.data;
throw errorData;
}
};

export const useDeleteProduct = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (productIdList: number[]) => deleteProduct(productIdList),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [QUERY_KEY.PRODUCT_LIST_ALL] });
},
});
};
24 changes: 24 additions & 0 deletions src/apis/domains/admin/useFetchDeliveryDate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { get } from "@apis/api";
import { QUERY_KEY } from "@apis/queryKeys/queryKeys";
import { useQuery } from "@tanstack/react-query";
import { ApiResponseType, ErrorResponse } from "@types";

const getDeliveryDate = async (): Promise<number | null> => {
try {
const response = await get<ApiResponseType<number>>("api/v1/delivery/max");
console.log(response.data);
return response.data.data;
} catch (error) {
console.log(error);
const errorResponse = error as ErrorResponse;
const errorData = errorResponse.response.data;
throw errorData;
}
};

export const useFetchDeliveryDate = () => {
return useQuery({
queryKey: [QUERY_KEY.DELIVERY_DATE],
queryFn: () => getDeliveryDate(),
});
};
43 changes: 43 additions & 0 deletions src/apis/domains/admin/useFetchOrders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { get } from "@apis/api";
import { QUERY_KEY } from "@apis/queryKeys/queryKeys";
import { useQuery } from "@tanstack/react-query";
import { ApiResponseType, ErrorResponse, Order, OrderData } from "@types";

interface queryType {
orderReceivedDate: string;
deliveryDate: string;
productName: string;
deliveryStatus: string;
}

const buildQuery = (query: queryType): string => {
const queryString = Object.entries(query)
.filter(([, value]) => value && value.trim() !== "") // 빈 값 또는 공백 문자열 제거
.map(
([key, value]) =>
`${encodeURIComponent(key)}=${encodeURIComponent(value as string)}`
) // 쿼리 파라미터를 인코딩
.join("&");

return queryString ? `${queryString}` : "";
};

const getOrders = async (query: queryType): Promise<Order[] | null> => {
try {
const response = await get<ApiResponseType<OrderData>>(
`api/v1/order?${buildQuery(query)}`
);
return response.data.data.orderList;
} catch (error) {
const errorResponse = error as ErrorResponse;
const errorData = errorResponse.response.data;
throw errorData;
}
};

export const useFetchOrders = (query: queryType) => {
return useQuery({
queryKey: [QUERY_KEY.SAILED_PRODUCT, query],
queryFn: () => getOrders(query),
});
};
23 changes: 23 additions & 0 deletions src/apis/domains/admin/useFetchProductAll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { get } from "@apis/api";
import { QUERY_KEY } from "@apis/queryKeys/queryKeys";
import { useQuery } from "@tanstack/react-query";
import { ApiResponseType, ProductListWithSailed } from "@types";

const getProductList = async (): Promise<ProductListWithSailed | null> => {
try {
const response = await get<ApiResponseType<ProductListWithSailed>>(
"api/v1/product/all"
);
return response.data.data;
} catch (error) {
console.log(error);
return null;
}
};

export const useFetchProductAll = () => {
return useQuery({
queryKey: [QUERY_KEY.PRODUCT_LIST_ALL],
queryFn: () => getProductList(),
});
};
25 changes: 25 additions & 0 deletions src/apis/domains/admin/useFetchSailedProduct.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { get } from "@apis/api";
import { QUERY_KEY } from "@apis/queryKeys/queryKeys";
import { useQuery } from "@tanstack/react-query";
import { ApiResponseType, ErrorResponse } from "@types";
import { SailedProductList } from "@types";

const getSailedProduct = async (): Promise<SailedProductList | null> => {
try {
const response = await get<ApiResponseType<SailedProductList>>(
"api/v1/product/sailed"
);
return response.data.data;
} catch (error) {
const errorResponse = error as ErrorResponse;
const errorData = errorResponse.response.data;
throw errorData;
}
};

export const useFetchSailedProduct = () => {
return useQuery({
queryKey: [QUERY_KEY.SAILED_PRODUCT],
queryFn: () => getSailedProduct(),
});
};
27 changes: 27 additions & 0 deletions src/apis/domains/admin/usePatchDeliveryDate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { patch } from "@apis/api";
import { QUERY_KEY } from "@apis/queryKeys/queryKeys";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { ErrorResponse, MutateResponseType } from "@types";

const patchDeliveryDate = async (date: number): Promise<MutateResponseType> => {
try {
const response = await patch<MutateResponseType>(
`api/v1/delivery/${date.toString()}`
);
return response.data;
} catch (error) {
const errorResponse = error as ErrorResponse;
const errorData = errorResponse.response.data;
throw errorData;
}
};

export const usePatchDeliveryDate = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (date: number) => patchDeliveryDate(date),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [QUERY_KEY.DELIVERY_DATE] });
},
});
};
27 changes: 27 additions & 0 deletions src/apis/domains/admin/usePatchProduct.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { patch } from "@apis/api";
import { QUERY_KEY } from "@apis/queryKeys/queryKeys";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { ErrorResponse, MutateResponseType } from "@types";

const patchProduct = async (productId: number): Promise<MutateResponseType> => {
try {
const response = await patch<MutateResponseType>(
`api/v1/product/${productId.toString()}`
);
return response.data;
} catch (error) {
const errorResponse = error as ErrorResponse;
const errorData = errorResponse.response.data;
throw errorData;
}
};

export const usePatchProduct = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (productId: number) => patchProduct(productId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [QUERY_KEY.PRODUCT_LIST_ALL] });
},
});
};
30 changes: 30 additions & 0 deletions src/apis/domains/admin/usePostProduct.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { post } from "@apis/api";
import { QUERY_KEY } from "@apis/queryKeys/queryKeys";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { ErrorResponse, MutateResponseType, ProductAddType } from "@types";

const postProduct = async (
productData: ProductAddType
): Promise<MutateResponseType> => {
try {
const response = await post<MutateResponseType>(
`api/v1/product`,
productData
);
return response.data;
} catch (error) {
const errorResponse = error as ErrorResponse;
const errorData = errorResponse.response.data;
throw errorData;
}
};

export const usePostProduct = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (productData: ProductAddType) => postProduct(productData),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [QUERY_KEY.PRODUCT_LIST_ALL] });
},
});
};
3 changes: 3 additions & 0 deletions src/apis/queryKeys/queryKeys.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
export const QUERY_KEY = {
ORDER_POST: "orderPost",
DELIVERY_DATE: "deliveryDate",
PRODUCT_LIST: "productList",
PRODUCT_LIST_ALL: "productListAll",
SAILED_PRODUCT: "sailedProduct",
} as const;
30 changes: 30 additions & 0 deletions src/assets/svg/IcArrowDown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { SVGProps } from "react";
const SvgIcArrowDown = (props: SVGProps<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 25 25"
{...props}
>
<mask
id="ic_arrow_down_svg__a"
width={25}
height={25}
x={0}
y={0}
maskUnits="userSpaceOnUse"
style={{
maskType: "alpha",
}}
>
<path fill="#D9D9D9" d="M.307.222h24v24h-24z" />
</mask>
<g mask="url(#ic_arrow_down_svg__a)">
<path
fill="#B6B6B6"
d="m12.307 15.622-6-6 1.4-1.4 4.6 4.6 4.6-4.6 1.4 1.4z"
/>
</g>
</svg>
);
export default SvgIcArrowDown;
30 changes: 30 additions & 0 deletions src/assets/svg/IcArrowUp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { SVGProps } from "react";
const SvgIcArrowUp = (props: SVGProps<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 25 25"
{...props}
>
<mask
id="ic_arrow_up_svg__a"
width={25}
height={25}
x={0}
y={0}
maskUnits="userSpaceOnUse"
style={{
maskType: "alpha",
}}
>
<path fill="#D9D9D9" d="M.435.811h24v24h-24z" />
</mask>
<g mask="url(#ic_arrow_up_svg__a)">
<path
fill="#B6B6B6"
d="m12.435 11.611-4.6 4.6-1.4-1.4 6-6 6 6-1.4 1.4z"
/>
</g>
</svg>
);
export default SvgIcArrowUp;
2 changes: 2 additions & 0 deletions src/assets/svg/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export { default as IcArrowDown } from "./IcArrowDown";
export { default as IcArrowUp } from "./IcArrowUp";
export { default as IcBack } from "./IcBack";
export { default as IcCheckbox } from "./IcCheckbox";
export { default as IcCheckedTrue } from "./IcCheckedTrue";
Expand Down
11 changes: 11 additions & 0 deletions src/components/common/Button/Button.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,17 @@ export const buttonVariant = {
color: ${theme.color.orange};
background-color: transparent;
`,
smallFill: (theme: Theme) => css`
width: 100%;
height: 3rem;
border: 1px solid ${theme.color.orange};
border-radius: 4px;
${theme.font["pretendard-01"]}
color: ${theme.color.white};
background-color: ${theme.color.orange};
`,
};

export const disabledStyle = (theme: Theme) => css`
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
import { IcFix } from "@svg";

export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant: "fill" | "stroke" | "smallStroke";
variant: "fill" | "stroke" | "smallStroke" | "smallFill";
disabled?: boolean;
isIcon?: boolean;
}
Expand Down
Loading

0 comments on commit 173d232

Please sign in to comment.