Skip to content

Commit 7ca0611

Browse files
authored
Merge pull request #35 from codeit-maso/feature/Yun
롤링페이지 생성 페이지 구현 및 라우팅 설정
2 parents 74c89a9 + 9a95e58 commit 7ca0611

File tree

13 files changed

+361
-6
lines changed

13 files changed

+361
-6
lines changed

src/App.jsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
11
import './assets/styles/base.scss';
22
import { Route, Routes } from 'react-router-dom';
33
import Header from './components/layout/Header/Header';
4-
import Home from './pages/Home/home';
4+
import Home from './pages/Home/Home';
5+
import RecipientList from './pages/RecipientList/RecipientList';
6+
import CreateRecipient from './pages/CreateRecipient/CreateRecipient';
7+
import Recipient from './pages/Recipient/Recipient';
8+
import MessageForm from './pages/MessageForm/MessageForm';
59

610
export default function App() {
711
return (
812
<>
913
<Header />
1014
<Routes>
1115
<Route path="/" element={<Home />} />
16+
<Route path="/list" element={<RecipientList />} />
17+
<Route path="/post" element={<CreateRecipient />} />
18+
<Route path="/post/:id" element={<Recipient />} />
19+
<Route path="/post/:id/message" element={<MessageForm />} />
1220
</Routes>
1321
</>
1422
);

src/api/getBackgroundImage.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import api from './api';
2+
3+
export default async function getBackgroundImage() {
4+
const res = await api.get('/background-images/');
5+
return res.data.imageUrls;
6+
}

src/api/postRecipient.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import api from './api';
2+
3+
export default async function createPost({
4+
team,
5+
name,
6+
backgroundColor,
7+
backgroundImageURL,
8+
}) {
9+
const res = await api.post('/15-7/recipients/', {
10+
team,
11+
name,
12+
backgroundColor,
13+
backgroundImageURL,
14+
});
15+
return res.data.id;
16+
}

src/assets/images/checked.svg

Lines changed: 4 additions & 0 deletions
Loading
File renamed without changes.

src/pages/Posts/components/Badge/Badge.module.scss renamed to src/components/Badge/Badge.module.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
@use '../../../../assets/styles/variables.scss' as *;
1+
@use '../../assets/styles/variables.scss' as *;
22

33
.badge {
44
display: inline-block;
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import deleteIcon from '../../../../assets/images/delete.svg';
2-
import plus from '../../../../assets/images/plus.svg';
1+
import deleteIcon from '../../assets/images/delete.svg';
2+
import plus from '../../assets/images/plus.svg';
33
import Badge from '../Badge/Badge';
44
import styles from './Card.module.scss';
55

src/pages/Posts/components/Card/Card.module.scss renamed to src/components/Card/Card.module.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
@use '../../../../assets/styles/variables.scss' as *;
1+
@use '../../assets/styles/variables.scss' as *;
22

33
.card {
44
display: flex;
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
import { useState, useEffect } from 'react';
2+
import { useNavigate } from 'react-router-dom';
3+
import styles from './CreateRecipient.module.scss';
4+
import FormInput from '../../components/common/FormInput';
5+
import Button from '../../components/common/Button';
6+
import getBackgroundImage from '../../api/getBackgroundImage';
7+
import checked from '../../assets/images/checked.svg';
8+
import postRecipient from '../../api/postRecipient';
9+
10+
const colors = ['beige', 'purple', 'blue', 'green'];
11+
12+
export default function CreateRecipient() {
13+
const [data, setData] = useState(null);
14+
const [value, setValue] = useState('');
15+
const [isError, setIsError] = useState(false);
16+
const [selectedType, setSelectedType] = useState('color');
17+
const [selectedColor, setSelectedColor] = useState('beige');
18+
const [selectedImage, setSelectedImage] = useState(-1);
19+
const navigate = useNavigate();
20+
21+
useEffect(() => {
22+
const fetch = async () => {
23+
try {
24+
const result = await getBackgroundImage();
25+
setData(result);
26+
} catch (error) {
27+
console.error('데이터 로딩 실패:', error);
28+
}
29+
};
30+
31+
fetch();
32+
}, []);
33+
34+
function handleInputChange(e) {
35+
setValue(e.target.value);
36+
}
37+
38+
function handleBlur() {
39+
if (!value) {
40+
setIsError(true);
41+
} else {
42+
setIsError(false);
43+
}
44+
}
45+
46+
function handleColorClick(color) {
47+
setSelectedColor(color);
48+
}
49+
50+
function handleImageClick(index) {
51+
setSelectedImage(index);
52+
}
53+
54+
async function handleButtonClick() {
55+
try {
56+
const id = await postRecipient({
57+
team: '15-7',
58+
name: value,
59+
backgroundColor: selectedColor,
60+
backgroundImageURL: data[selectedImage] ?? null,
61+
});
62+
navigate(`/post/${id}`);
63+
} catch (error) {
64+
console.error('페이지 생성 중 오류:', error.response.data);
65+
}
66+
}
67+
68+
return (
69+
<div className={styles['create-page']}>
70+
<div className={styles['create-page__input-section']}>
71+
<FormInput
72+
label="To."
73+
placeholder="받는 사람 이름을 입력해 주세요"
74+
value={value}
75+
onChange={handleInputChange}
76+
onBlur={handleBlur}
77+
isError={isError}
78+
/>
79+
</div>
80+
<div className={styles['create-page__background-select']}>
81+
<div className={styles['create-page__text-section']}>
82+
<p>
83+
<span>배경화면을 선택해 주세요.</span>
84+
</p>
85+
<p>컬러를 선택하거나, 이미지를 선택할 수 있습니다.</p>
86+
</div>
87+
<div className={styles['create-page__toggle-button']}>
88+
<button
89+
className={`${styles['create-page__select-color']} ${
90+
selectedType === 'color'
91+
? styles['create-page__select-color--active']
92+
: ''
93+
}`}
94+
onClick={() => {
95+
setSelectedType('color');
96+
setSelectedImage(-1);
97+
}}
98+
>
99+
컬러
100+
</button>
101+
<button
102+
className={`${styles['create-page__select-image']} ${
103+
selectedType === 'image'
104+
? styles['create-page__select-image--active']
105+
: ''
106+
}`}
107+
onClick={() => {
108+
setSelectedType('image');
109+
setSelectedImage(0);
110+
}}
111+
>
112+
이미지
113+
</button>
114+
</div>
115+
{selectedType === 'color' && (
116+
<ul className={styles['create-page__color-list']}>
117+
{colors.map((color) => (
118+
<li
119+
key={color}
120+
className={`${styles[`create-page__color--${color}`]} ${
121+
selectedColor === color
122+
? styles['create-page__color--selected']
123+
: ''
124+
}`}
125+
onClick={() => handleColorClick(color)}
126+
>
127+
{selectedColor === color && (
128+
<img
129+
src={checked}
130+
alt="선택됨"
131+
className={styles['create-page__check-icon']}
132+
/>
133+
)}
134+
</li>
135+
))}
136+
</ul>
137+
)}
138+
139+
{selectedType === 'image' && (
140+
<ul className={styles['create-page__image-list']}>
141+
{data.map((url, index) => (
142+
<li
143+
key={index}
144+
className={`${styles[`create-page__image--${index + 1}`]} ${
145+
selectedImage === index
146+
? styles['create-page__image--selected']
147+
: ''
148+
}`}
149+
onClick={() => handleImageClick(index)}
150+
>
151+
<img
152+
src={url}
153+
alt={`배경이미지${index + 1}`}
154+
className={styles['create-page__background-img']}
155+
/>
156+
{selectedImage === index && (
157+
<img
158+
src={checked}
159+
alt="선택됨"
160+
className={styles['create-page__check-icon']}
161+
/>
162+
)}
163+
</li>
164+
))}
165+
</ul>
166+
)}
167+
</div>
168+
<Button
169+
type="create"
170+
onClick={handleButtonClick}
171+
disabled={!value.trim()}
172+
>
173+
생성하기
174+
</Button>
175+
</div>
176+
);
177+
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
@use '../../assets/styles/variables.scss' as *;
2+
3+
.create-page {
4+
display: flex;
5+
flex-direction: column;
6+
margin: 57px auto;
7+
max-width: 720px;
8+
gap: 50px;
9+
10+
&__background-select {
11+
display: flex;
12+
flex-direction: column;
13+
gap: 25px;
14+
}
15+
16+
&__text-section {
17+
display: flex;
18+
flex-direction: column;
19+
gap: 4px;
20+
21+
p {
22+
@include font-16-regular;
23+
24+
span {
25+
@include font-24-bold;
26+
}
27+
}
28+
}
29+
30+
&__toggle-button {
31+
button {
32+
width: 122px;
33+
height: 40px;
34+
padding: 8px 16px;
35+
border: none;
36+
border-radius: 6px;
37+
@include font-16-regular;
38+
color: $gray-900;
39+
}
40+
41+
.create-page__select-color--active,
42+
.create-page__select-image--active {
43+
border: 2px solid $purple-600;
44+
background-color: $white;
45+
@include font-16-bold;
46+
color: $purple-700;
47+
}
48+
}
49+
50+
&__color-list {
51+
display: flex;
52+
margin: 20px 0;
53+
gap: 16px;
54+
55+
.create-page__color--beige,
56+
.create-page__color--purple,
57+
.create-page__color--blue,
58+
.create-page__color--green {
59+
position: relative;
60+
width: 168px;
61+
height: 168px;
62+
border: 1px solid rgba(0, 0, 0, 0.08);
63+
border-radius: 16px;
64+
list-style: none;
65+
flex-shrink: 0;
66+
cursor: pointer;
67+
}
68+
69+
.create-page__color--selected {
70+
cursor: default;
71+
}
72+
73+
.create-page__color--beige {
74+
background-color: $beige-200;
75+
}
76+
.create-page__color--purple {
77+
background-color: $purple-200;
78+
}
79+
.create-page__color--blue {
80+
background-color: $blue-200;
81+
}
82+
.create-page__color--green {
83+
background-color: $green-200;
84+
}
85+
86+
.create-page__check-icon {
87+
position: absolute;
88+
top: 50%;
89+
left: 50%;
90+
width: 44px;
91+
height: 44px;
92+
transform: translate(-50%, -50%);
93+
pointer-events: none;
94+
}
95+
}
96+
97+
&__image-list {
98+
display: flex;
99+
margin: 20px 0;
100+
gap: 16px;
101+
102+
.create-page__image--1,
103+
.create-page__image--2,
104+
.create-page__image--3,
105+
.create-page__image--4 {
106+
position: relative;
107+
width: 168px;
108+
height: 168px;
109+
border-radius: 16px;
110+
list-style: none;
111+
overflow: hidden;
112+
flex-shrink: 0;
113+
cursor: pointer;
114+
115+
.create-page__background-img {
116+
position: absolute;
117+
width: 100%;
118+
height: 100%;
119+
object-fit: cover;
120+
}
121+
}
122+
123+
.create-page__image--selected .create-page__background-img {
124+
opacity: 0.5;
125+
cursor: default;
126+
}
127+
128+
.create-page__check-icon {
129+
position: absolute;
130+
top: 50%;
131+
left: 50%;
132+
width: 44px;
133+
height: 44px;
134+
transform: translate(-50%, -50%);
135+
pointer-events: none;
136+
}
137+
}
138+
}

0 commit comments

Comments
 (0)