Skip to content

Commit 2fcd4ff

Browse files
committed
refactor(frontend): Content Dialog 섹션 컴포넌트 분리 및 N+1 쿼리 해결
두 다이얼로그(ContentDetailsDialog, ContentGroupDetailsDialog)에서 중복되던 섹션 렌더링 로직을 공통 컴포넌트로 분리 - ContentBasicInfoSection: 기본 정보 섹션 - ContentRewardSection: 보상 정보 섹션 - ContentSeeMoreRewardSection: 더보기 보상 섹션 - ContentSection: 위 섹션들을 통합한 컴포넌트 ContentGroupDetailsDialog의 N+1 쿼리 문제 해결을 위해 GraphQL 쿼리에서 전체 content 데이터를 한 번에 fetch하도록 변경 fix #274
1 parent 831cba7 commit 2fcd4ff

File tree

9 files changed

+412
-346
lines changed

9 files changed

+412
-346
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { DataGrid } from "~/components/data-grid";
2+
import { Section } from "~/components/section";
3+
4+
import { ItemNameWithImage } from "../../item";
5+
6+
type ContentBasicInfoSectionProps = {
7+
content: {
8+
contentCategory: {
9+
imageUrl: string;
10+
name: string;
11+
};
12+
displayName: string;
13+
level: number;
14+
};
15+
};
16+
17+
export const ContentBasicInfoSection = ({ content }: ContentBasicInfoSectionProps) => {
18+
const items = [
19+
{
20+
label: "종류",
21+
value: (
22+
<ItemNameWithImage
23+
name={content.contentCategory.name}
24+
src={content.contentCategory.imageUrl}
25+
/>
26+
),
27+
},
28+
{ label: "레벨", value: content.level },
29+
{ label: "이름", value: content.displayName },
30+
];
31+
32+
return (
33+
<Section title="기본 정보">
34+
<DataGrid items={items} />
35+
</Section>
36+
);
37+
};
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { Flex, IconButton, Text } from "@chakra-ui/react";
2+
import { IoIosSettings } from "react-icons/io";
3+
4+
import { DataGrid } from "~/components/data-grid";
5+
import { Dialog } from "~/components/dialog";
6+
import { Section } from "~/components/section";
7+
import { LoginTooltip } from "~/components/tooltip";
8+
import { useAuth } from "~/libs/auth";
9+
10+
import { ItemNameWithImage } from "../../item";
11+
import { ContentRewardEditDialog } from "../content-reward-edit-dialog";
12+
13+
type ContentRewardSectionProps = {
14+
contentId: number;
15+
contentRewards: Array<{
16+
averageQuantity: number | null;
17+
item: {
18+
imageUrl: string;
19+
name: string;
20+
};
21+
}>;
22+
onRefetch: () => void;
23+
};
24+
25+
export const ContentRewardSection = ({
26+
contentId,
27+
contentRewards,
28+
onRefetch,
29+
}: ContentRewardSectionProps) => {
30+
const { isAuthenticated } = useAuth();
31+
32+
const items = contentRewards.map((contentReward) => ({
33+
label: (
34+
<ItemNameWithImage name={contentReward.item.name} reverse src={contentReward.item.imageUrl} />
35+
),
36+
value: contentReward.averageQuantity || "-",
37+
}));
38+
39+
return (
40+
<Section
41+
title={
42+
<Flex alignItems="center" gap={2}>
43+
<Text>보상 정보</Text>
44+
<Dialog.Trigger
45+
dialog={ContentRewardEditDialog}
46+
dialogProps={{
47+
contentId,
48+
onComplete: onRefetch,
49+
}}
50+
disabled={!isAuthenticated}
51+
>
52+
<LoginTooltip content="로그인 후 보상을 수정할 수 있습니다">
53+
<IconButton disabled={!isAuthenticated} size="2xs" variant="surface">
54+
<IoIosSettings />
55+
</IconButton>
56+
</LoginTooltip>
57+
</Dialog.Trigger>
58+
</Flex>
59+
}
60+
>
61+
<DataGrid items={items} />
62+
</Section>
63+
);
64+
};
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { Flex } from "@chakra-ui/react";
2+
3+
import { RewardItem } from "../types";
4+
import { ContentBasicInfoSection } from "./content-basic-info-section";
5+
import { ContentRewardSection } from "./content-reward-section";
6+
import { ContentSeeMoreRewardSection } from "./content-see-more-reward-section";
7+
import { ContentWageSection } from "./content-wage-section";
8+
9+
type ContentSectionProps = {
10+
content: {
11+
contentCategory: {
12+
imageUrl: string;
13+
name: string;
14+
};
15+
contentRewards: Array<{
16+
averageQuantity: number;
17+
item: {
18+
imageUrl: string;
19+
name: string;
20+
};
21+
}>;
22+
contentSeeMoreRewards: Array<{
23+
item: {
24+
imageUrl: string;
25+
name: string;
26+
};
27+
quantity: number;
28+
}>;
29+
displayName: string;
30+
id: number;
31+
level: number;
32+
};
33+
items: RewardItem[];
34+
onRefetch: () => void;
35+
};
36+
37+
export const ContentSection = ({ content, items, onRefetch }: ContentSectionProps) => {
38+
return (
39+
<Flex direction="column" gap={4}>
40+
<ContentBasicInfoSection content={content} />
41+
<ContentWageSection contentId={content.id} items={items} />
42+
<ContentRewardSection
43+
contentId={content.id}
44+
contentRewards={content.contentRewards}
45+
onRefetch={onRefetch}
46+
/>
47+
{content.contentSeeMoreRewards.length > 0 && (
48+
<ContentSeeMoreRewardSection
49+
contentId={content.id}
50+
contentSeeMoreRewards={content.contentSeeMoreRewards}
51+
onRefetch={onRefetch}
52+
/>
53+
)}
54+
</Flex>
55+
);
56+
};
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { Flex, IconButton, Text } from "@chakra-ui/react";
2+
import { IoIosSettings } from "react-icons/io";
3+
4+
import { DataGrid } from "~/components/data-grid";
5+
import { Dialog } from "~/components/dialog";
6+
import { Section } from "~/components/section";
7+
import { LoginTooltip } from "~/components/tooltip";
8+
import { useAuth } from "~/libs/auth";
9+
10+
import { ItemNameWithImage } from "../../item";
11+
import { ContentSeeMoreRewardEditDialog } from "../content-see-more-reward-edit-dialog";
12+
13+
type ContentSeeMoreRewardSectionProps = {
14+
contentId: number;
15+
contentSeeMoreRewards: Array<{
16+
item: {
17+
imageUrl: string;
18+
name: string;
19+
};
20+
quantity: number;
21+
}>;
22+
onRefetch: () => void;
23+
};
24+
25+
export const ContentSeeMoreRewardSection = ({
26+
contentId,
27+
contentSeeMoreRewards,
28+
onRefetch,
29+
}: ContentSeeMoreRewardSectionProps) => {
30+
const { isAuthenticated } = useAuth();
31+
32+
const items = contentSeeMoreRewards.map((contentSeeMoreReward) => ({
33+
label: (
34+
<ItemNameWithImage
35+
name={contentSeeMoreReward.item.name}
36+
reverse
37+
src={contentSeeMoreReward.item.imageUrl}
38+
/>
39+
),
40+
value: contentSeeMoreReward.quantity,
41+
}));
42+
43+
return (
44+
<Section
45+
title={
46+
<Flex alignItems="center" gap={2}>
47+
<Text>더보기 보상 정보</Text>
48+
<Dialog.Trigger
49+
dialog={ContentSeeMoreRewardEditDialog}
50+
dialogProps={{
51+
contentId,
52+
onComplete: onRefetch,
53+
}}
54+
disabled={!isAuthenticated}
55+
>
56+
<LoginTooltip content="로그인 후 보상을 수정할 수 있습니다">
57+
<IconButton disabled={!isAuthenticated} size="2xs" variant="surface">
58+
<IoIosSettings />
59+
</IconButton>
60+
</LoginTooltip>
61+
</Dialog.Trigger>
62+
</Flex>
63+
}
64+
>
65+
<DataGrid items={items} />
66+
</Section>
67+
);
68+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
1+
export { ContentBasicInfoSection } from "./content-basic-info-section";
2+
export { ContentRewardSection } from "./content-reward-section";
3+
export { ContentSection } from "./content-section";
4+
export { ContentSeeMoreRewardSection } from "./content-see-more-reward-section";
15
export { ContentWageSection } from "./content-wage-section";

0 commit comments

Comments
 (0)