-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat: 이미지 첨부 기능 구현 및 리스트 생성 제출 API 연동 #17
Changes from 16 commits
6469078
446a431
bee77d4
272bc40
e128a37
6b18093
da8917b
209eb15
756ed34
261dd45
9fc4009
c344b91
6289709
4c369e4
0b92fab
2ec9815
a2eb657
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,8 @@ | ||
import axiosInstance from '@/lib/axios/axiosInstance'; | ||
import { ListCreateType } from '@/lib/types/listType'; | ||
import { ListCreateType, ListIdType } from '@/lib/types/listType'; | ||
|
||
export const createList = async (data: ListCreateType) => { | ||
const response = await axiosInstance.post<ListCreateType>('/lists', data); | ||
const response = await axiosInstance.post<ListIdType>('/lists', data); | ||
|
||
return response.data; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import axiosInstance from '@/lib/axios/axiosInstance'; | ||
import { ItemImagesType, PresignedUrlListType } from '@/lib/types/listType'; | ||
import axios from 'axios'; | ||
|
||
interface uploadItemImagesProps { | ||
listId: number; | ||
imageData: ItemImagesType; | ||
imageFileList: File[]; | ||
} | ||
|
||
export const uploadItemImages = async ({ listId, imageData, imageFileList }: uploadItemImagesProps) => { | ||
imageData.listId = listId; | ||
|
||
//PresignedUrl 생성 요청 | ||
const response = await axiosInstance.post<PresignedUrlListType>('/lists/upload-url', imageData); | ||
|
||
//PresignedUrl에 이미지 업로드 | ||
for (let i = 0; i < response.data.length; i++) { | ||
const result = await axios.put(response.data[i].presignedUrl, imageFileList[i], { | ||
headers: { | ||
'Content-Type': imageFileList[i]?.type, | ||
}, | ||
}); | ||
if (result.status !== 200) return; | ||
} | ||
|
||
//서버에 성공 완료 알림 | ||
if (imageFileList.length !== 0) { | ||
await axiosInstance.post('/lists/upload-complete', imageData); | ||
} | ||
}; | ||
Comment on lines
+1
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👩💻리스트 생성 성공 후 리스트 아이템 이미지를 업로드하는 과정입니다! queryFn, MutationFn 모두 파라미터 1개만 전달할 수 있는 것 같아요! |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,7 +22,7 @@ import { UserProfileType } from '@/lib/types/userProfileType'; | |
import { getCategories } from '@/app/_api/category/getCategories'; | ||
import { getUsers } from '@/app/_api/user/getUsers'; | ||
import { listDescriptionRules, listLabelRules, listTitleRules } from '@/lib/constants/formInputValidationRules'; | ||
import { listDescription } from '@/app/[userNickname]/[listId]/_components/ListDetailOuter/ListInformation.css'; | ||
// import { listDescription } from '@/app/[userNickname]/[listId]/_components/ListDetailOuter/ListInformation.css'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Eugene-A-01 🖊️ 배포시 미사용 코드로 워닝이 떠서 주석처리했습니다~! |
||
|
||
interface CreateListProps { | ||
onNextClick: () => void; | ||
|
@@ -39,9 +39,10 @@ function CreateList({ onNextClick }: CreateListProps) { | |
const [categories, setCategories] = useState<CategoryType[]>([]); | ||
const [users, setUsers] = useState<UserProfileType[]>([]); | ||
|
||
const { setValue, control, formState } = useFormContext(); | ||
const { isValid } = formState; | ||
const { setValue, control } = useFormContext(); | ||
const collaboIDs = useWatch({ control, name: 'collaboratorIds' }); | ||
const title = useWatch({ control, name: 'title' }); | ||
const category = useWatch({ control, name: 'category' }); | ||
|
||
const searchParams = useSearchParams(); | ||
const isTemplateCreation = searchParams.has('title') && searchParams.has('category'); | ||
|
@@ -75,7 +76,7 @@ function CreateList({ onNextClick }: CreateListProps) { | |
return ( | ||
<div> | ||
{/* 헤더 */} | ||
<Header isNextActive={isValid} onClickNext={onNextClick} /> | ||
<Header isNextActive={title && category} onClickNext={onNextClick} /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Eugene-A-01 🖊️isValid가 아이템 페이지까지 채워져야 true가 돼서 title, category를 watch하여 값이 있을때 true가 되도록 코드를 수정했습니다! 급하게 수정한 것이라 옳은 방식인지는 확신이 없습니다ㅠㅠ! |
||
|
||
<div className={styles.body}> | ||
{/* 리스트 제목 */} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { style } from '@vanilla-extract/css'; | ||
|
||
export const header = style({ | ||
width: '100%', | ||
height: '90px', | ||
paddingLeft: '20px', | ||
paddingRight: '20px', | ||
|
||
position: 'sticky', | ||
top: '0', | ||
left: '0', | ||
zIndex: '10', | ||
|
||
display: 'flex', | ||
flexDirection: 'row', | ||
alignItems: 'center', | ||
justifyContent: 'space-between', | ||
|
||
backgroundColor: '#fff', | ||
|
||
borderBottom: '1px solid rgba(0, 0, 0, 0.10)', | ||
}); | ||
|
||
export const headerTitle = style({ | ||
fontSize: '2rem', | ||
}); | ||
|
||
export const headerNextButton = style({ | ||
fontSize: '1.6rem', | ||
color: '#AFB1B6', | ||
cursor: 'default', | ||
}); | ||
|
||
export const headerNextButtonActive = style({ | ||
fontSize: '1.6rem', | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import BackIcon from '/public/icons/back.svg'; | ||
import * as styles from './Header.css'; | ||
|
||
interface HeaderProps { | ||
onBackClick: () => void; | ||
isSubmitActive: boolean; | ||
onSubmitClick: () => void; | ||
} | ||
|
||
function Header({ onBackClick, isSubmitActive, onSubmitClick }: HeaderProps) { | ||
return ( | ||
<div className={styles.header}> | ||
<button onClick={onBackClick}> | ||
<BackIcon alt="뒤로가기 버튼" /> | ||
</button> | ||
<h1 className={styles.headerTitle}>리스트 생성</h1> | ||
<button | ||
className={isSubmitActive ? styles.headerNextButtonActive : styles.headerNextButton} | ||
disabled={!isSubmitActive} | ||
onClick={onSubmitClick} | ||
> | ||
완료 | ||
</button> | ||
</div> | ||
); | ||
} | ||
export default Header; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { style } from '@vanilla-extract/css'; | ||
|
||
export const label = style({ | ||
cursor: 'pointer', | ||
}); | ||
|
||
export const input = style({ | ||
display: 'none', | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { ReactNode } from 'react'; | ||
import ImageIcon from '/public/icons/attach_image.svg'; | ||
import * as styles from './ImageUploader.css'; | ||
|
||
interface ImageUploaderProps { | ||
index: number; | ||
children: ReactNode; | ||
} | ||
|
||
export default function ImageUploader({ index, children }: ImageUploaderProps) { | ||
return ( | ||
<> | ||
<label className={styles.label} htmlFor={`${index}-image`}> | ||
<ImageIcon /> | ||
</label> | ||
{children} | ||
</> | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Eugene-A-01 🖊️ 제네릭으로 response로 받을 타입으로 넣어야 해서 수정했습니다!