Skip to content

Commit

Permalink
[Feat/#71] 관리자 페이지 UI개선 및 기능 추가 (#72)
Browse files Browse the repository at this point in the history
* refactor: 관리자 사이드바 -> 헤더로 변경

* feat: 관리자페이지 비고란 추가

* design: 태블릿 화면 스타일 수정 (스크롤, 높이 설정)

* feat: 관리자/태블릿 주문시간 추가

* design: 태블릿 화면 css 수정 (반응형 디자인 추가)

* feat: validation alert -> modal 변경

* feat: 관리자 주문 리스트 useInfiniteQuery로 수정

* feat: Intersection Observer 를 이용한 무한스크롤 구현

* feat: 관리자 페이지 주문 비고 기능 추가
  • Loading branch information
gudusol authored Jan 8, 2025
1 parent 7529e65 commit ec2bfdc
Show file tree
Hide file tree
Showing 26 changed files with 526 additions and 163 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"dependencies": {
"@emotion/react": "^11.13.3",
"@tanstack/react-query": "^5.56.2",
"@tanstack/react-query-devtools": "^5.62.16",
"axios": "^1.7.7",
"dayjs": "^1.11.13",
"jotai": "^2.9.3",
Expand All @@ -23,6 +24,7 @@
"react-dom": "^18.3.1",
"react-router-dom": "^6.26.1",
"react-select": "^5.8.1",
"react-spinners": "^0.15.0",
"react-switch": "^7.0.0",
"xlsx": "^0.18.5"
},
Expand Down
4 changes: 4 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import theme from "@styles/theme";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import PrivateRoute from "./routes/PrivateRoute/PrivateRoute";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";

const allRoutes = [
...homeRoutes,
Expand All @@ -36,6 +37,9 @@ function App() {
<Global styles={GlobalStyle} />
<RouterProvider router={router} />
</ThemeProvider>
<div style={{ fontSize: "16px" }}>
<ReactQueryDevtools />
</div>
</QueryClientProvider>
);
}
Expand Down
26 changes: 18 additions & 8 deletions src/apis/domains/admin/useFetchOrders.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
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";
import { useInfiniteQuery } from "@tanstack/react-query";
import { ApiResponseType, ErrorResponse, OrderData } from "@types";

interface queryType {
orderReceivedDate: string;
Expand All @@ -22,12 +22,17 @@ const buildQuery = (query: queryType): string => {
return queryString ? `${queryString}` : "";
};

const getOrders = async (query: queryType): Promise<Order[] | null> => {
const getOrders = async (
query: queryType,
pageParam?: string
): Promise<OrderData> => {
try {
const extendedQuery =
pageParam === "-1" ? query : { ...query, cursorOrderId: pageParam };
const response = await get<ApiResponseType<OrderData>>(
`api/v1/order?${buildQuery(query)}`
`api/v1/orders?${buildQuery(extendedQuery)}`
);
return response.data.data.orderList;
return response.data.data;
} catch (error) {
const errorResponse = error as ErrorResponse;
const errorData = errorResponse.response.data;
Expand All @@ -36,8 +41,13 @@ const getOrders = async (query: queryType): Promise<Order[] | null> => {
};

export const useFetchOrders = (query: queryType) => {
return useQuery({
queryKey: [QUERY_KEY.ORDER_LIST, query],
queryFn: () => getOrders(query),
return useInfiniteQuery({
queryKey: [QUERY_KEY.ORDER_LIST],
queryFn: ({ pageParam = null }) => getOrders(query, pageParam?.toString()),
getNextPageParam: (lastPage) => {
return lastPage?.nextCursor === null ? undefined : lastPage?.nextCursor;
},
initialPageParam: -1, // 초기 페이지 파라미터 설정
select: (data) => (data.pages ?? []).flatMap((page) => page?.orders),
});
};
1 change: 1 addition & 0 deletions src/apis/domains/admin/useFetchProductAll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ export const useFetchProductAll = () => {
return useQuery({
queryKey: [QUERY_KEY.PRODUCT_LIST_ALL],
queryFn: () => getProductList(),
staleTime: 1000 * 60 * 60, // 1시간
});
};
36 changes: 36 additions & 0 deletions src/apis/domains/admin/usePatchOrderNote.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { adminPatch } from "@apis/api";
import { QUERY_KEY } from "@apis/queryKeys/queryKeys";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { ErrorResponse, MutateResponseType } from "@types";

interface PatchOrderNote {
orderId: number;
note: string;
}

const patchOrderNote = async ({
orderId,
note,
}: PatchOrderNote): Promise<MutateResponseType> => {
try {
const response = await adminPatch<MutateResponseType>(`api/v1/order/note`, {
orderId,
note,
});
return response.data;
} catch (error) {
const errorResponse = error as ErrorResponse;
const errorData = errorResponse.response.data;
throw errorData;
}
};

export const usePatchOrderNote = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (date: PatchOrderNote) => patchOrderNote(date),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [QUERY_KEY.ORDER_LIST] });
},
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,23 @@ export const radioWrapper = css`
margin-top: 1rem;
margin-bottom: 1.2rem;
`;

export const alertModal = css`
width: 30rem;
${flexGenerator("column")};
padding: 3rem;
`;

export const alertModalText = (theme: Theme) =>
css`
${theme.font["head06-b-16"]}
color: ${theme.color.black};
text-align: center;
`;

export const buttonWrapper = css`
width: 100%;
margin-top: 2rem;
${flexGenerator("column")};
gap: 1rem;
`;
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ import {
CountProduct,
CustomCalendar,
Input,
Modal,
RadioInput,
} from "@components";
import {
addressFormWrapper,
alertModal,
alertModalText,
buttonWrapper,
deliveryDateContainer,
editReceiverLayout,
mainSectionStyle,
Expand Down Expand Up @@ -52,6 +56,13 @@ const EditReceiver = ({ receiverIndex }: EditReceiverProps) => {
const navigate = useNavigate();
const [category] = useAtom(categoryAtom);

const [alertText, setAlertText] = useState("");
const [isModalOpen, setIsModalOpen] = useState(false);

const handleModalClose = () => {
setIsModalOpen(false);
};

const [form, setForm] = useState({
address: receiver?.recipientAddress || "",
addressDetail: receiver?.recipientAddressDetail || "",
Expand Down Expand Up @@ -84,12 +95,26 @@ const EditReceiver = ({ receiverIndex }: EditReceiverProps) => {
}));
};

const handleButtonClick = () => {
if (!form.address || !form.addressDetail || !form.zonecode) {
alert("주소와 상세주소를 모두 입력해주세요.");
return;
const checkValidation = () => {
if (
!orderPostDataState.recipientInfo[receiverIndex]?.recipientName ||
!orderPostDataState.recipientInfo[receiverIndex]?.recipientPhone
) {
setAlertText("받는 분의 이름과 휴대폰 번호를 모두 입력해주세요.");
return false;
} else if (!form.address || !form.addressDetail || !form.zonecode) {
setAlertText("주소와 상세주소를 모두 입력해주세요.");
return false;
} else if (selectedOption !== "regular" && selectedDate.length < 1) {
alert("희망 배송일자를 선택해주세요");
setAlertText("희망 배송일자를 선택해주세요");
return false;
}
return true;
};

const handleButtonClick = () => {
if (!checkValidation()) {
setIsModalOpen(true);
return;
}

Expand Down Expand Up @@ -272,6 +297,18 @@ const EditReceiver = ({ receiverIndex }: EditReceiverProps) => {
수정 완료
</Button>
</footer>
{isModalOpen && (
<Modal onClose={handleModalClose}>
<article css={alertModal}>
<p css={alertModalText}>{alertText}</p>
<div css={buttonWrapper}>
<Button variant="fill" onClick={() => setIsModalOpen(false)}>
확인
</Button>
</div>
</article>
</Modal>
)}
</>
);
};
Expand Down
27 changes: 27 additions & 0 deletions src/components/common/steps/Receiver2/Receiver2.style.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { css, Theme } from "@emotion/react";
import { flexGenerator } from "@styles/generator";
import { CategoryType } from "@types";

export const mainSectionStyle = (theme: Theme) => css`
${flexGenerator("column")}
Expand All @@ -24,3 +25,29 @@ export const zonecodeWrapper = css`
border-radius: 10px;
}
`;

export const alertModal = css`
width: 30rem;
${flexGenerator("column")};
padding: 3rem;
`;

export const alertModalText = (category: CategoryType) => (theme: Theme) =>
css`
${theme.font["head06-b-16"]}
color: ${theme.color.black};
text-align: center;
& > strong {
color: ${category === "experience"
? theme.color.green
: theme.color.orange};
}
`;

export const buttonWrapper = css`
width: 100%;
margin-top: 2rem;
${flexGenerator("column")};
gap: 1rem;
`;
Loading

0 comments on commit ec2bfdc

Please sign in to comment.