Skip to content

Commit 657c68d

Browse files
authored
Merge pull request #49 from codeit-maso/feature/chan
✨ feat: HeaderService 컴포넌트 구현
2 parents ce76d18 + 152383d commit 657c68d

File tree

9 files changed

+321
-2
lines changed

9 files changed

+321
-2
lines changed

src/api/fetchReactions.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { api } from './api';
2+
3+
export default async function fetchReactions(recipientId, limit = 8) {
4+
try {
5+
const teamId = import.meta.env.VITE_TEAM_ID;
6+
const res = await api.get(
7+
`/${teamId}/recipients/${recipientId}/reactions/?limit=${limit}`,
8+
);
9+
return res.data.results;
10+
} catch {
11+
return [];
12+
}
13+
}

src/api/fetchRecipient.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { api } from './api';
2+
3+
export default async function fetchRecipient(recipientId) {
4+
try {
5+
const teamId = import.meta.env.VITE_TEAM_ID;
6+
const res = await api.get(`/${teamId}/recipients/${recipientId}/`);
7+
return res.data;
8+
} catch {
9+
return null;
10+
}
11+
}

src/assets/images/chevron-down.svg

Lines changed: 3 additions & 0 deletions
Loading

src/assets/images/emoji-add.svg

Lines changed: 3 additions & 0 deletions
Loading

src/assets/images/share.svg

Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { useState, useEffect } from 'react';
2+
import styles from './HeaderService.module.scss';
3+
import emojiAdd from '../../../assets/images/emoji-add.svg';
4+
import shareIcon from '../../../assets/images/share.svg';
5+
import chevronDown from '../../../assets/images/chevron-down.svg';
6+
import fetchReactions from '../../../api/fetchReactions';
7+
8+
export default function HeaderService({ recipient }) {
9+
const [reactions, setReactions] = useState([]);
10+
11+
useEffect(() => {
12+
if (recipient?.id) {
13+
fetchReactions(recipient.id)
14+
.then((data) => {
15+
setReactions(data);
16+
})
17+
.catch((error) => {
18+
console.error('리액션 데이터 로딩 오류:', error);
19+
});
20+
}
21+
}, [recipient?.id]);
22+
23+
return (
24+
<div className={styles['header-service']}>
25+
<div className={styles['header-service__container']}>
26+
<div className={styles['header-service__name-section']}>
27+
<h1 className={styles['header-service__recipient-name']}>
28+
To. {recipient?.name || '받는사람 이름'}
29+
</h1>
30+
</div>
31+
32+
<div className={styles['header-service__right-content']}>
33+
<div className={styles['header-service__profile-section']}>
34+
<div className={styles['header-service__profile-images']}>
35+
{recipient?.recentMessages?.slice(0, 3).map((message, index) => (
36+
<img
37+
key={message.id}
38+
src={message.profileImageURL}
39+
alt="프로필"
40+
className={styles['header-service__profile-image']}
41+
style={{ zIndex: index + 1 }}
42+
/>
43+
))}
44+
{recipient?.messageCount > 3 && (
45+
<div className={styles['header-service__additional-count']}>
46+
+{recipient.messageCount - 3}
47+
</div>
48+
)}
49+
</div>
50+
<span className={styles['header-service__message-count']}>
51+
{recipient?.messageCount || 0}명이 작성했어요!
52+
</span>
53+
</div>
54+
55+
<div className={styles['header-service__divider--left']}></div>
56+
<div className={styles['header-service__actions']}>
57+
<div className={styles['header-service__emojis']}>
58+
{reactions.length > 0 ? (
59+
[...reactions]
60+
.sort((a, b) => b.count - a.count)
61+
.slice(0, 3)
62+
.map((reaction) => (
63+
<div
64+
key={reaction.id}
65+
className={styles['header-service__emoji-item']}
66+
>
67+
{reaction.emoji} {reaction.count}
68+
</div>
69+
))
70+
) : (
71+
<span>아직 리액션이 없어요</span>
72+
)}
73+
</div>
74+
<button className={styles['header-service__emoji-more']}>
75+
<img src={chevronDown} alt="이모지 더보기" />
76+
</button>
77+
<button className={styles['header-service__add-button']}>
78+
<img src={emojiAdd} alt="이모지 추가" />
79+
추가
80+
</button>
81+
<div className={styles['header-service__divider--right']}></div>
82+
<button className={styles['header-service__share-button']}>
83+
<img src={shareIcon} alt="공유" />
84+
</button>
85+
</div>
86+
</div>
87+
</div>
88+
</div>
89+
);
90+
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
@use '../../../assets/styles/variables.scss' as *;
2+
3+
.header-service {
4+
width: 100%;
5+
background-color: $white;
6+
border-bottom: 1px solid $gray-200;
7+
padding: 13px 0;
8+
9+
&__container {
10+
width: 100%;
11+
max-width: 1200px;
12+
margin: 0 auto;
13+
display: flex;
14+
justify-content: space-between;
15+
align-items: center;
16+
17+
@media (max-width: 1248px) {
18+
padding: 0 24px;
19+
}
20+
}
21+
22+
&__right-content {
23+
display: flex;
24+
align-items: center;
25+
margin-left: 263px;
26+
}
27+
28+
&__recipient-name {
29+
@include font-28-bold;
30+
color: $gray-800;
31+
margin: 0;
32+
}
33+
34+
&__profile-section {
35+
display: flex;
36+
align-items: center;
37+
}
38+
39+
&__profile-images {
40+
display: flex;
41+
position: relative;
42+
}
43+
44+
&__profile-image {
45+
width: 28px;
46+
height: 28px;
47+
border-radius: 50%;
48+
border: 1px solid $white;
49+
position: relative;
50+
51+
&:not(:first-child) {
52+
margin-left: -10px;
53+
}
54+
}
55+
56+
&__additional-count {
57+
display: flex;
58+
justify-content: center;
59+
align-items: center;
60+
width: 28px;
61+
height: 28px;
62+
border-radius: 50%;
63+
border: 1px solid #e3e3e3;
64+
background-color: $white;
65+
color: #484848;
66+
margin-left: -12px;
67+
position: relative;
68+
z-index: 4;
69+
@include font-12-regular;
70+
}
71+
72+
&__profile-section {
73+
display: flex;
74+
align-items: center;
75+
gap: 8px;
76+
}
77+
78+
&__message-count {
79+
@include font-16-regular;
80+
color: $gray-500;
81+
}
82+
83+
&__divider {
84+
&--left {
85+
width: 1px;
86+
height: 28px;
87+
background-color: $gray-200;
88+
margin: 0 28px;
89+
}
90+
91+
&--right {
92+
width: 1px;
93+
height: 28px;
94+
background-color: $gray-200;
95+
margin: 0 13px;
96+
}
97+
}
98+
99+
&__actions {
100+
display: flex;
101+
align-items: center;
102+
gap: 8px;
103+
}
104+
105+
&__emojis {
106+
display: flex;
107+
align-items: center;
108+
gap: 8px;
109+
border-radius: 32px;
110+
padding: 8px 0;
111+
@include font-16-regular;
112+
}
113+
114+
&__emoji-item {
115+
width: 63px;
116+
height: 36px;
117+
display: flex;
118+
align-items: center;
119+
justify-content: center;
120+
gap: 4px;
121+
white-space: nowrap;
122+
background-color: #757575;
123+
color: $white;
124+
border: none;
125+
padding: 8px 12px;
126+
border-radius: 32px;
127+
@include font-16-regular;
128+
cursor: pointer;
129+
}
130+
131+
&__emoji-more {
132+
display: flex;
133+
align-items: center;
134+
justify-content: center;
135+
background: none;
136+
border: none;
137+
cursor: pointer;
138+
139+
&:hover {
140+
opacity: 0.6;
141+
}
142+
}
143+
144+
&__add-button,
145+
&__share-button {
146+
display: flex;
147+
align-items: center;
148+
justify-content: center;
149+
height: 36px;
150+
padding: 6px 16px;
151+
border-radius: 6px;
152+
border: 1px solid $gray-300;
153+
background-color: $white;
154+
@include font-16-regular;
155+
cursor: pointer;
156+
157+
&:hover {
158+
background-color: $gray-100;
159+
}
160+
}
161+
162+
&__add-button {
163+
gap: 4px;
164+
}
165+
}

src/pages/Recipient/Recipient.jsx

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,34 @@
1+
import { useState, useEffect } from 'react';
2+
import { useParams } from 'react-router-dom';
3+
import HeaderService from '../../components/recipient/HeaderService/HeaderService';
4+
import fetchRecipient from '../../api/fetchRecipient';
5+
16
export default function Recipient() {
2-
return <div style={{ fontSize: '30px' }}>Route test component ^_^ </div>;
7+
const [recipient, setRecipient] = useState(null);
8+
const { id } = useParams();
9+
10+
useEffect(() => {
11+
if (!id) {
12+
console.error('Recipient ID가 제공되지 않았습니다');
13+
return;
14+
}
15+
16+
async function loadData() {
17+
try {
18+
const recipientData = await fetchRecipient(id);
19+
20+
setRecipient(recipientData);
21+
} catch (error) {
22+
console.error('데이터 가져오기 실패:', error);
23+
}
24+
}
25+
26+
loadData();
27+
}, [id]);
28+
29+
return (
30+
<div>
31+
<HeaderService recipient={recipient} />
32+
</div>
33+
);
334
}

src/pages/RecipientList/RecipientCard.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import styles from './RecipientCard.module.scss';
2-
import RecentMessages from './recentMessages';
2+
import RecentMessages from './RecentMessages';
33
import TopReactions from './TopReactions';
44

55
//캐러셀 내부 요소 - 카드 컴포넌트

0 commit comments

Comments
 (0)