diff --git a/frontend/src/components/ProductListSection/ProductListSection.tsx b/frontend/src/components/ProductListSection/ProductListSection.tsx
index 37e85039..01ed7109 100644
--- a/frontend/src/components/ProductListSection/ProductListSection.tsx
+++ b/frontend/src/components/ProductListSection/ProductListSection.tsx
@@ -9,6 +9,7 @@ import InfiniteScroll from '@/components/common/InfiniteScroll/InfiniteScroll';
import Masonry from '@/components/common/Masonry/Masonry';
import AsyncWrapper from '@/components/common/AsyncWrapper/AsyncWrapper';
import Loading from '@/components/common/Loading/Loading';
+import NoDataPlaceholder from '@/components/common/NoDataPlaceholder/NoDataPlaceholder';
type Props = {
title: string;
@@ -29,14 +30,6 @@ function ProductListSection({
isError,
getNextPage,
}: Props) {
- const ProductCardList = (data: Product[]) => {
- return data.map(({ id, imageUrl, name, rating }) => (
-
-
-
- ));
- };
-
return (
@@ -55,7 +48,7 @@ function ProductListSection({
isLoading={isLoading}
isError={isError}
>
- {ProductCardList(data)}
+
) : (
@@ -64,7 +57,7 @@ function ProductListSection({
isReady={isReady}
isError={isError}
>
- {ProductCardList(data)}
+
)}
@@ -72,4 +65,17 @@ function ProductListSection({
);
}
+const ProductCardList = ({ data }: { data: Product[] }) => {
+ return (
+
+ {data.map(({ id, imageUrl, name, rating }) => (
+
+
+
+ ))}
+ {data.length === 0 && }
+
+ );
+};
+
export default ProductListSection;
diff --git a/frontend/src/components/ProfileCard/ProfileCard.tsx b/frontend/src/components/ProfileCard/ProfileCard.tsx
index fded0aec..528aeb4c 100644
--- a/frontend/src/components/ProfileCard/ProfileCard.tsx
+++ b/frontend/src/components/ProfileCard/ProfileCard.tsx
@@ -36,7 +36,6 @@ const chipMapper = {
};
function ProfileCard({
- id,
gitHubId,
imageUrl,
careerLevel,
@@ -65,8 +64,6 @@ function ProfileCard({
(product) => product.category === 'keyboard'
);
- console.log(keyboard);
-
return (
diff --git a/frontend/src/components/ProfileSearchResult/ProfileSearchResult.tsx b/frontend/src/components/ProfileSearchResult/ProfileSearchResult.tsx
index dfe398d1..cc4cd3c4 100644
--- a/frontend/src/components/ProfileSearchResult/ProfileSearchResult.tsx
+++ b/frontend/src/components/ProfileSearchResult/ProfileSearchResult.tsx
@@ -1,8 +1,7 @@
import * as S from '@/components/ProfileSearchResult/ProfileSearchResult.style';
-import AsyncWrapper from '@/components/common/AsyncWrapper/AsyncWrapper';
-import Loading from '@/components/common/Loading/Loading';
import InfiniteScroll from '@/components/common/InfiniteScroll/InfiniteScroll';
import ProfileCard from '@/components/ProfileCard/ProfileCard';
+import NoDataPlaceholder from '@/components/common/NoDataPlaceholder/NoDataPlaceholder';
type Props = {
data: ProfileSearchResult[];
@@ -16,7 +15,6 @@ function ProfileSearchResult({
data: profileSearchData,
getNextPage,
isLoading,
- isReady,
isError,
}: Props) {
const profileSearchDataList = profileSearchData.map(
@@ -36,15 +34,14 @@ function ProfileSearchResult({
);
return (
- } isReady={isReady} isError={isError}>
-
- {profileSearchDataList}
-
-
+
+ {profileSearchDataList}
+ {profileSearchData.length === 0 && }
+
);
}
diff --git a/frontend/src/components/ReviewListSection/ReviewListSection.tsx b/frontend/src/components/ReviewListSection/ReviewListSection.tsx
index 5794a28b..a7109d1e 100644
--- a/frontend/src/components/ReviewListSection/ReviewListSection.tsx
+++ b/frontend/src/components/ReviewListSection/ReviewListSection.tsx
@@ -31,22 +31,6 @@ function ReviewListSection({
const userData = useContext(UserDataContext);
const loginUserGithubId = userData?.member.gitHubId;
- const reviewCardList = reviewData.map(
- ({ id, author, product, content, rating }) => (
-
- )
- );
return (
@@ -58,11 +42,44 @@ function ReviewListSection({
isLoading={isLoading}
isError={isError}
>
- {reviewCardList}
+
+
+
);
}
+const ReviewCardList = ({
+ data,
+ handleDelete,
+ handleEdit,
+ loginUserGithubId,
+}: Pick & {
+ loginUserGithubId: string;
+}) => (
+ <>
+ {data.map(({ id, author, product, content, rating }) => (
+
+ ))}
+ >
+);
+
export default ReviewListSection;
diff --git a/frontend/src/components/common/NoDataPlaceholder/NoDataPlaceholder.tsx b/frontend/src/components/common/NoDataPlaceholder/NoDataPlaceholder.tsx
new file mode 100644
index 00000000..5c2f3958
--- /dev/null
+++ b/frontend/src/components/common/NoDataPlaceholder/NoDataPlaceholder.tsx
@@ -0,0 +1,19 @@
+import { Player } from '@lottiefiles/react-lottie-player';
+
+function NoDataPlaceholder() {
+ return (
+ <>
+
+
+ 아무것도 찾지 못했어요..
+
+ >
+ );
+}
+
+export default NoDataPlaceholder;
diff --git a/frontend/src/components/common/ProductSelect/ProductSelect.tsx b/frontend/src/components/common/ProductSelect/ProductSelect.tsx
index dcfd54b6..a609522c 100644
--- a/frontend/src/components/common/ProductSelect/ProductSelect.tsx
+++ b/frontend/src/components/common/ProductSelect/ProductSelect.tsx
@@ -1,5 +1,5 @@
import ProductBar from '@/components/common/ProductBar/ProductBar';
-import { useReducer } from 'react';
+import { useState } from 'react';
import DownArrow from '@/assets/down_arrow.svg';
import * as S from '@/components/common/ProductSelect/ProductSelect.style';
@@ -10,7 +10,7 @@ type Props = {
selectedProduct: InventoryProduct;
setSelectedProduct: React.Dispatch>;
otherProducts: InventoryProduct[];
- updateProfileProduct: () => Promise;
+ updateProfileProduct: () => Promise;
};
function ProductSelect({
@@ -20,27 +20,21 @@ function ProductSelect({
otherProducts,
updateProfileProduct,
}: Props) {
- const [isEditMode, setEditMode] = useReducer(
- (isEditMode: boolean) => !isEditMode,
- false
- );
- const [isOptionsOpen, setOptionOpen] = useReducer(
- (isOptionsOpen: boolean) => !isOptionsOpen,
- false
- );
+ const [isEditMode, setEditMode] = useState(false);
+ const [isOptionsOpen, setOptionOpen] = useState(false);
const handleProductSelect = (value: InventoryProduct) => {
setSelectedProduct(value);
- setOptionOpen();
+ setOptionOpen(false);
};
const handleEditDone = () => {
if (isEditMode) {
updateProfileProduct()
- .then(() => {
- submitHandler();
- setOptionOpen();
- setEditMode();
+ .then((didPatch) => {
+ if (didPatch) submitHandler();
+ setOptionOpen(false);
+ setEditMode(false);
})
.catch((error) => {
console.error(error);
@@ -48,7 +42,11 @@ function ProductSelect({
return;
}
- setEditMode();
+ setEditMode(true);
+ };
+
+ const handleOptionOpen = () => {
+ setOptionOpen(true);
};
return (
@@ -59,7 +57,7 @@ function ProductSelect({
{isEditMode ? (
<>
-
+
{selectedProduct !== undefined ? (
= {
};
function useGetMany({ url, params, body, headers }: Props): Return {
- const [data, setData] = useState([]);
+ const [data, setData] = useState(null);
const [page, setPage] = useState(0);
const [hasNextPage, setHasNextPage] = useState(true);
@@ -64,7 +64,7 @@ function useGetMany({ url, params, body, headers }: Props): Return {
setRefetchTrigger((prevTrigger) => prevTrigger + 1);
setPage(0);
setHasNextPage(true);
- setData([]);
+ setData(null);
};
const getCurrentParamString = () =>
@@ -81,7 +81,8 @@ function useGetMany({ url, params, body, headers }: Props): Return {
fetchData()
.then(({ hasNext, items }) => {
- !!items && setData((prevData) => [...prevData, ...items]);
+ !!items &&
+ setData((prevData) => (prevData ? [...prevData, ...items] : items));
return hasNext;
})
.then((hasNext) => {
@@ -100,11 +101,11 @@ function useGetMany({ url, params, body, headers }: Props): Return {
const searchParams = new URLSearchParams(params);
useEffect(() => {
- if (data.length === 0) return; // 최초 렌더링 시 refetch 방지 임시 조치
+ if (!data) return; // 최초 렌더링 시 refetch 방지 임시 조치
refetch();
}, [searchParams.toString()]);
- const isReady = data.length !== 0;
+ const isReady = !!data;
return { data, getNextPage, refetch, isLoading, isReady, isError };
}
diff --git a/frontend/src/hooks/useInventory.tsx b/frontend/src/hooks/useInventory.tsx
index 30ee2316..ada32051 100644
--- a/frontend/src/hooks/useInventory.tsx
+++ b/frontend/src/hooks/useInventory.tsx
@@ -5,18 +5,18 @@ import usePatch from '@/hooks/api/usePatch';
import { useContext, useEffect, useMemo, useState } from 'react';
type InventoryResponse = {
- keyboards: InventoryProduct[];
+ items: InventoryProduct[];
};
type Return = {
- keyboards: InventoryProduct[];
+ items: InventoryProduct[];
isReady: boolean;
isError: boolean;
selectedProduct: InventoryProduct | null;
setSelectedProduct: React.Dispatch>;
otherProducts: InventoryProduct[];
refetch: () => void;
- updateProfileProduct: () => Promise;
+ updateProfileProduct: () => Promise;
};
function useInventory(): Return {
@@ -39,8 +39,7 @@ function useInventory(): Return {
});
const initialSelectedProduct = useMemo(
- () =>
- isReady && inventoryProducts.keyboards.find(({ selected }) => selected),
+ () => isReady && inventoryProducts.items.find(({ selected }) => selected),
[inventoryProducts]
);
@@ -50,7 +49,7 @@ function useInventory(): Return {
(initialSelectedProduct &&
selectedProduct.id === initialSelectedProduct.id)
) {
- return;
+ return false;
}
const patchBody = { selectedInventoryProductId: selectedProduct.id };
@@ -58,20 +57,19 @@ function useInventory(): Return {
patchBody['unselectedInventoryProductId'] = initialSelectedProduct.id;
}
await patchProfileProduct(patchBody);
+ return true;
};
const otherProducts =
isReady &&
(selectedProduct
- ? inventoryProducts.keyboards.filter(
- ({ id }) => id !== selectedProduct.id
- )
- : inventoryProducts.keyboards);
+ ? inventoryProducts.items.filter(({ id }) => id !== selectedProduct.id)
+ : inventoryProducts.items);
useEffect(() => {
if (!isReady) return;
- const newSelectedProduct = inventoryProducts.keyboards.find(
+ const newSelectedProduct = inventoryProducts.items.find(
({ selected }) => selected
);
@@ -79,7 +77,7 @@ function useInventory(): Return {
}, [inventoryProducts]);
return {
- keyboards: inventoryProducts?.keyboards,
+ items: inventoryProducts?.items,
isReady,
isError,
selectedProduct,
diff --git a/frontend/src/mocks/data.ts b/frontend/src/mocks/data.ts
index e7023a88..1c37d7fd 100644
--- a/frontend/src/mocks/data.ts
+++ b/frontend/src/mocks/data.ts
@@ -273,10 +273,10 @@ const getReviewProductData: (
});
export const InventoryProducts = {
- keyboards: [
+ items: [
{
id: 1,
- selected: false,
+ selected: true,
product: {
id: 3,
name: '레오폴드 FC900RBT PD 그레이 블루 한글 (저소음 적축)',
diff --git a/frontend/src/pages/Profile/Profile.tsx b/frontend/src/pages/Profile/Profile.tsx
index cf52d4a2..d82ebeec 100644
--- a/frontend/src/pages/Profile/Profile.tsx
+++ b/frontend/src/pages/Profile/Profile.tsx
@@ -23,7 +23,7 @@ type Member = {
function Profile() {
const userData = useContext(UserDataContext);
const {
- keyboards,
+ items,
isReady: isInventoryProductsReady,
refetch: refetchInventoryProducts,
selectedProduct,
@@ -67,7 +67,7 @@ function Profile() {
isReady={isInventoryProductsReady}
isError={isMyDataError}
>
-
+
diff --git a/frontend/src/pages/ProfileSearch/ProfileSearch.tsx b/frontend/src/pages/ProfileSearch/ProfileSearch.tsx
index 7dd51303..2e69dbad 100644
--- a/frontend/src/pages/ProfileSearch/ProfileSearch.tsx
+++ b/frontend/src/pages/ProfileSearch/ProfileSearch.tsx
@@ -6,6 +6,8 @@ import SearchFilter from '@/components/SearchFilter/SearchFilter';
import * as S from '@/pages/ProfileSearch/ProfileSearch.style';
import useProfileSearch from '@/hooks/useProfileSearch';
import ProfileSearchResult from '@/components/ProfileSearchResult/ProfileSearchResult';
+import Loading from '@/components/common/Loading/Loading';
+import AsyncWrapper from '@/components/common/AsyncWrapper/AsyncWrapper';
function ProfileSearch() {
const [careerLevelFilter, setCareerLevelFilter] = useState('');
@@ -50,13 +52,19 @@ function ProfileSearch() {
handleJobTypeFilterClick={handleJobTypeFilterClick}
/>
- }
isReady={isProfileSearchReady}
isError={isProfileSearchError}
- />
+ >
+
+
);
}
diff --git a/frontend/src/pages/Register/Register.tsx b/frontend/src/pages/Register/Register.tsx
index 933a9245..cdf7dff9 100644
--- a/frontend/src/pages/Register/Register.tsx
+++ b/frontend/src/pages/Register/Register.tsx
@@ -14,29 +14,29 @@ const messages = {
3: '입력한 정보를 확인해주세요',
};
-const careers = {
- NONE: '경력 없음',
- JUNIOR: '0-2년차',
- MID_LEVEL: '3-5년차',
- SENIOR: '6년차 이상',
+const careerLevel = {
+ none: '경력 없음',
+ junior: '0-2년차',
+ midlevel: '3-5년차',
+ senior: '6년차 이상',
} as const;
-const jobTypes = {
- FRONT_END: '프론트엔드',
- BACK_END: '백엔드',
- MOBILE: '모바일',
- ETC: '기타',
+const jobType = {
+ frontend: '프론트엔드',
+ backend: '백엔드',
+ mobile: '모바일',
+ etc: '기타',
} as const;
type UserInfo = {
- career: keyof typeof careers;
- jobType: keyof typeof jobTypes;
+ careerLevel: keyof typeof careerLevel;
+ jobType: keyof typeof jobType;
};
function Register() {
const [step, setStep] = useState(1);
const [additionalInfo, setAdditionalInfo] = useState({
- career: null,
+ careerLevel: null,
jobType: null,
});
const { showAlert, getConfirm } = useModal();
@@ -53,9 +53,9 @@ function Register() {
const renderSelectButton = (step: number) => {
switch (step) {
case 1:
- return careers;
+ return careerLevel;
case 2:
- return jobTypes;
+ return jobType;
}
};
@@ -63,24 +63,27 @@ function Register() {
e
) => {
if (!(e.target instanceof HTMLButtonElement)) return;
- if (!(e.target.value in careers) && !(e.target.value in jobTypes)) return;
+ if (!(e.target.value in careerLevel) && !(e.target.value in jobType))
+ return;
if (step === 1) {
setAdditionalInfo({
...additionalInfo,
- career: e.target.value as keyof typeof careers,
+ careerLevel: e.target.value as keyof typeof careerLevel,
});
} else {
setAdditionalInfo({
...additionalInfo,
- jobType: e.target.value as keyof typeof jobTypes,
+ jobType: e.target.value as keyof typeof jobType,
});
}
};
const handleAdditionalInfoSubmit = async (input: UserInfo) => {
const confirmation = await getConfirm(
- `${careers[input.career]}, ${jobTypes[input.jobType]} 개발자이신가요?`
+ `${careerLevel[input.careerLevel]}, ${
+ jobType[input.jobType]
+ } 개발자이신가요?`
);
if (confirmation) {
patchAdditionalInfo(input)
@@ -94,7 +97,7 @@ function Register() {
};
const handleConfirmButtonClick = async () => {
- if (step === 1 && additionalInfo.career === null) {
+ if (step === 1 && additionalInfo.careerLevel === null) {
showAlert('경력을 선택해주세요.');
return;
}
@@ -129,7 +132,7 @@ function Register() {
onClick={handleSelectButtonClick}
value={value}
selected={
- value === additionalInfo.career ||
+ value === additionalInfo.careerLevel ||
value === additionalInfo.jobType
}
>
@@ -139,8 +142,10 @@ function Register() {
)}
{step === 3 && (
<>
- {careers[additionalInfo.career]}
- {jobTypes[additionalInfo.jobType]}
+
+ {careerLevel[additionalInfo.careerLevel]}
+
+ {jobType[additionalInfo.jobType]}
>
)}