Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
ade08e1
fix: 유저 아이콘 변경
heeeeyong Oct 13, 2025
062120e
fix: 투표 생성 시 제목 가이드 멘트 수정
heeeeyong Oct 13, 2025
8d5414e
Merge pull request #282 from THIP-TextHip/main
ho0010 Oct 18, 2025
569633a
feat: 피드 추천 영역 구현 - 지금 뜨는 추천 글 섹션 추가
ljh130334 Oct 27, 2025
917e1b6
style: 헤더 섹션 스타일 수정
ljh130334 Oct 27, 2025
a73b81d
feat: Embla Carousel 라이브러리 설치
ljh130334 Oct 27, 2025
0aa0b56
feat: Embla Carousel로 카드 가로 스크롤 구현
ljh130334 Oct 27, 2025
fdff236
style: 공식 인플루언서 색상 변경
ljh130334 Oct 27, 2025
c36bb56
refactor: PostFooter를 하단에 고정
ljh130334 Oct 27, 2025
928fd6d
refactor: 북마크 아이콘 삭제 및 일부 스타일링 수정
ljh130334 Oct 27, 2025
4eb60f4
refactor: 더보기 아이콘 교체
ljh130334 Oct 27, 2025
82319c0
feat: 목업 데이터 수정 및 책 카드 클릭 이벤트 무효화
ljh130334 Oct 27, 2025
5cbfd82
fix: 피드 10개마다 추천 글 섹션이 반복되도록 수정
ljh130334 Oct 27, 2025
194c2e8
Refactor: 섹션마다 다른 데이터 표시 및 목업 데이터 분리
ljh130334 Oct 28, 2025
e67e20f
chore: 일부 주석 제거
ljh130334 Oct 28, 2025
c8ef24e
Merge branch 'develop' of https://github.com/THIP-TextHip/THIP-Web in…
heeeeyong Oct 28, 2025
154caf7
Merge pull request #284 from THIP-TextHip/feat/influencer
ljh130334 Oct 28, 2025
2ffe34a
Merge branch 'develop' of https://github.com/THIP-TextHip/THIP-Web in…
heeeeyong Oct 28, 2025
7b28ca4
feat: AI 독서감상문 생성 버튼 추가
ljh130334 Oct 28, 2025
e97d8a9
feat: ConfirmModal 외부 영역 클릭 시 모달 닫히도록 구현
ljh130334 Oct 28, 2025
2990f48
refactor: AI 독서 감상문 생성 모달에서 취소/확인으로 받도록 프로퍼티 추가
ljh130334 Oct 28, 2025
aadd7d8
feat: AI 독서감상문 로딩 페이지 생성
ljh130334 Oct 28, 2025
694abe7
feat: AI 독서감상문 결과 화면 구현
ljh130334 Oct 28, 2025
b26fef7
chore: 파일명을 AiWrite로 수정
ljh130334 Oct 28, 2025
9e779c9
feat: 클립보드 복사 기능 구현
ljh130334 Oct 28, 2025
58352ae
feat: 뒤로가기 버튼 확인 모달 추가
ljh130334 Oct 28, 2025
390eac5
Merge pull request #286 from THIP-TextHip/feat/aiwriting
ljh130334 Oct 29, 2025
c29e140
feat: AI 독서 감상문 생성 API 연동
ljh130334 Oct 29, 2025
70a645f
feat: AI 이용 횟수 조회 API 연동 및 검증 로직 구현
ljh130334 Oct 29, 2025
9558164
fix: AI 이용 횟수 검증 로직 수정
ljh130334 Oct 29, 2025
f82faca
style: AIWrite 페이지 너비를 다른 페이지들과 동일하게 수정
ljh130334 Oct 29, 2025
d24985d
chore: 주석 수정
ljh130334 Oct 29, 2025
63c2bc6
Merge pull request #288 from THIP-TextHip/feat/api-ai
ljh130334 Oct 30, 2025
162e032
feat: 모임 만들기 책 선택에서 모임 책 탭 제거
ljh130334 Oct 31, 2025
c29acee
feat: 모임 생성 시 책 선택에서 모임 책 탭 숨김 처리
ljh130334 Oct 31, 2025
dedc38f
Merge pull request #291 from THIP-TextHip/qa/jihyeon
ljh130334 Oct 31, 2025
1e3084e
fix: Fragment에 key가 없어서 생기는 오류 해결
ho0010 Nov 2, 2025
03853b1
fix: 완료된 모임방을 내 모임방 옵션으로 변경
ho0010 Nov 2, 2025
967390c
remove: 불필요한 코드 제거
ho0010 Nov 2, 2025
f4eb20b
remove: 중복된 타입 선언
ho0010 Nov 2, 2025
cad5ced
Merge pull request #292 from THIP-TextHip/fix/QA10-3
ho0010 Nov 3, 2025
db251ed
Merge branch 'develop' of https://github.com/THIP-TextHip/THIP-Web in…
heeeeyong Nov 7, 2025
2ad171b
Revert "Merge pull request #284 from THIP-TextHip/feat/influencer"
ljh130334 Nov 11, 2025
0a7c70f
Merge pull request #293 from THIP-TextHip/chore/revert-influencer
heeeeyong Nov 11, 2025
2375b11
Merge branch 'develop' of https://github.com/THIP-TextHip/THIP-Web in…
heeeeyong Nov 11, 2025
f4a52ab
Merge pull request #294 from THIP-TextHip/chore/minor-updates
heeeeyong Nov 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions src/api/record/createAiReview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { apiClient } from '../index';
import type { CreateAiReviewData, ApiResponse } from '@/types/record';

// API 응답 타입
export type CreateAiReviewResponse = ApiResponse<CreateAiReviewData>;

// AI 독서감상문 생성 API 함수
export const createAiReview = async (roomId: number) => {
const response = await apiClient.post<CreateAiReviewResponse>(
`/rooms/${roomId}/record/ai-review`,
);
return response.data;
};

/*
사용 예시:
try {
const result = await createAiReview(1);
if (result.isSuccess) {
console.log("생성된 독서감상문:", result.data.content);
console.log("잔여 이용 횟수:", result.data.count);
// 성공 처리 로직
} else {
console.error("AI 독서감상문 생성 실패:", result.message);
// 실패 처리 로직
}
} catch (error) {
console.error("API 호출 오류:", error);
// 에러 처리 로직
}
*/
31 changes: 31 additions & 0 deletions src/api/record/getAiUsage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { apiClient } from '../index';
import type { AiUsageData, ApiResponse } from '@/types/record';

// API 응답 타입
export type GetAiUsageResponse = ApiResponse<AiUsageData>;

// AI 이용 횟수 조회 API 함수
export const getAiUsage = async (roomId: number) => {
const response = await apiClient.get<GetAiUsageResponse>(
`/rooms/${roomId}/users/ai-usage`,
);
return response.data;
};

/*
사용 예시:
try {
const result = await getAiUsage(1);
if (result.isSuccess) {
console.log("AI 독서감상문 작성 가능 횟수:", result.data.recordReviewCount);
console.log("기록 작성 횟수:", result.data.recordCount);
// 성공 처리 로직
} else {
console.error("AI 이용 횟수 조회 실패:", result.message);
// 실패 처리 로직
}
} catch (error) {
console.error("API 호출 오류:", error);
// 에러 처리 로직
}
*/
4 changes: 3 additions & 1 deletion src/api/record/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ export * from './createVote';
export * from './deleteRecord';
export * from './deleteVote';
export * from './postVote';
export * from './pinRecordToFeed';
export * from './pinRecordToFeed';
export * from './createAiReview';
export * from './getAiUsage';
5 changes: 5 additions & 0 deletions src/assets/common/infoIcon_white.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 6 additions & 5 deletions src/assets/feed/people.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/assets/memory/ai.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ interface BookSearchBottomSheetProps {
isOpen: boolean;
onClose: () => void;
onSelectBook: (book: Book) => void;
showGroupTab?: boolean;
}

const BookSearchBottomSheet = ({ isOpen, onClose, onSelectBook }: BookSearchBottomSheetProps) => {
const BookSearchBottomSheet = ({ isOpen, onClose, onSelectBook, showGroupTab = true }: BookSearchBottomSheetProps) => {
const {
searchQuery,
filteredBooks,
Expand All @@ -37,7 +38,7 @@ const BookSearchBottomSheet = ({ isOpen, onClose, onSelectBook }: BookSearchBott
loadMoreSearchResults,
loadMoreSavedBooks,
loadMoreGroupBooks,
} = useBookSearch();
} = useBookSearch(showGroupTab);

// 컴포넌트가 열릴 때 초기 데이터 로드
useEffect(() => {
Expand Down Expand Up @@ -109,7 +110,7 @@ const BookSearchBottomSheet = ({ isOpen, onClose, onSelectBook }: BookSearchBott
/>

{/* 탭 영역 */}
{showTabs && <BookSearchTabs activeTab={activeTab} onTabChange={handleTabChange} />}
{showTabs && <BookSearchTabs activeTab={activeTab} onTabChange={handleTabChange} showGroupTab={showGroupTab} />}

{/* 책 목록 영역 */}
<BookListContainer>
Expand Down
11 changes: 7 additions & 4 deletions src/components/common/BookSearchBottomSheet/BookSearchTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,20 @@ export type TabType = 'saved' | 'group';
interface BookSearchTabsProps {
activeTab: TabType;
onTabChange: (tab: TabType) => void;
showGroupTab?: boolean;
}

const BookSearchTabs = ({ activeTab, onTabChange }: BookSearchTabsProps) => {
const BookSearchTabs = ({ activeTab, onTabChange, showGroupTab = true }: BookSearchTabsProps) => {
return (
<TabContainer>
<Tab active={activeTab === 'saved'} onClick={() => onTabChange('saved')}>
저장한 책
</Tab>
<Tab active={activeTab === 'group'} onClick={() => onTabChange('group')}>
모임 책
</Tab>
{showGroupTab && (
<Tab active={activeTab === 'group'} onClick={() => onTabChange('group')}>
모임 책
</Tab>
)}
</TabContainer>
);
};
Expand Down
12 changes: 6 additions & 6 deletions src/components/common/BookSearchBottomSheet/useBookSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { getSearchBooks, convertToSearchedBooks, type SearchedBook } from '@/api
import type { Book } from './BookList';
import type { TabType } from './BookSearchTabs';

export const useBookSearch = () => {
export const useBookSearch = (showGroupTab: boolean = true) => {
const [searchQuery, setSearchQuery] = useState('');
const [filteredBooks, setFilteredBooks] = useState<Book[]>([]);
const [activeTab, setActiveTab] = useState<TabType>('saved');
Expand All @@ -17,7 +17,7 @@ export const useBookSearch = () => {
const [currentPage, setCurrentPage] = useState(1);
const [hasNextPage, setHasNextPage] = useState(false);
const [isLoadingMore, setIsLoadingMore] = useState(false);

// 저장한 책/모임 책 무한 스크롤 관련 상태
const [savedBooksCursor, setSavedBooksCursor] = useState<string | null>(null);
const [groupBooksCursor, setGroupBooksCursor] = useState<string | null>(null);
Expand Down Expand Up @@ -185,7 +185,7 @@ export const useBookSearch = () => {
if (isLoadingMoreSavedBooks || !hasSavedBooksNext) {
return;
}

await fetchSavedBooks(true);
};

Expand All @@ -194,7 +194,7 @@ export const useBookSearch = () => {
if (isLoadingMoreGroupBooks || !hasGroupBooksNext) {
return;
}

await fetchGroupBooks(true);
};

Expand Down Expand Up @@ -251,7 +251,7 @@ export const useBookSearch = () => {
const loadInitialData = () => {
if (activeTab === 'saved' && savedBooks.length === 0) {
fetchSavedBooks();
} else if (activeTab === 'group' && groupBooks.length === 0) {
} else if (activeTab === 'group' && groupBooks.length === 0 && showGroupTab) {
fetchGroupBooks();
}
};
Expand All @@ -262,7 +262,7 @@ export const useBookSearch = () => {
const hasBooks = isSearchMode ? searchResults.length > 0 : currentTabBooks.length > 0;
const showEmptyState = !isLoading && !error && !hasBooks;
const showTabs = !isSearchMode; // 검색 모드가 아닐 때는 항상 탭 표시

// 현재 탭의 무한 스크롤 상태
const currentTabHasNext = activeTab === 'saved' ? hasSavedBooksNext : hasGroupBooksNext;
const currentTabIsLoadingMore = activeTab === 'saved' ? isLoadingMoreSavedBooks : isLoadingMoreGroupBooks;
Expand Down
10 changes: 4 additions & 6 deletions src/components/common/MainHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useEffect, useState } from 'react';
import headerLogo from '../../assets/header/header-logo.svg';
import groupDoneLogo from '../../assets/header/group-done.svg';
import findUserLogo from '../../assets/header/findUser.svg';
import bellLogo from '../../assets/header/bell.svg';
import bellExistLogo from '../../assets/header/exist-bell.svg';
Expand Down Expand Up @@ -40,11 +39,10 @@ const MainHeader = ({ type, leftButtonClick, rightButtonClick }: MainHeaderProps
<HeaderWrapper>
<LogoImg src={headerLogo} alt="headerLogo" />
<Actions>
<IconButton
onClick={leftButtonClick}
src={type === 'group' ? groupDoneLogo : findUserLogo}
alt={type === 'group' ? '모임 완료 아이콘' : '사용자 찾기 아이콘'}
/>
{type === 'home' && (
<IconButton onClick={leftButtonClick} src={findUserLogo} alt={'사용자 찾기 아이콘'} />
)}

<IconButton
onClick={rightButtonClick}
src={hasUnchecked ? bellExistLogo : bellLogo}
Expand Down
19 changes: 15 additions & 4 deletions src/components/common/Modal/ConfirmModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,28 @@ import styled from '@emotion/styled';
import { colors, typography } from '@/styles/global/global';
import type { ConfirmModalProps } from '@/stores/usePopupStore';

const ConfirmModal = ({ title, disc, onConfirm, onClose }: ConfirmModalProps) => {
const ConfirmModal = ({
title,
disc,
onConfirm,
onClose,
confirmText = '예',
cancelText = '아니요',
}: ConfirmModalProps) => {
const handleContainerClick = (e: React.MouseEvent) => {
e.stopPropagation();
};

return (
<Container>
<Container onClick={handleContainerClick}>
<div className="title">{title}</div>
<div className="disc" dangerouslySetInnerHTML={{ __html: disc }} />
<ButtonContainer>
<Button variant="no" onClick={onClose}>
아니요
{cancelText}
</Button>
<Button variant="yes" onClick={onConfirm}>
{confirmText}
</Button>
</ButtonContainer>
</Container>
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/Modal/PopupContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const PopupContainer = () => {
switch (popupType) {
case 'confirm-modal':
return (
<Wrapper>
<Wrapper onClick={closePopup}>
<ConfirmModal {...(popupProps as ConfirmModalProps)} onClose={closePopup} />
</Wrapper>
);
Expand Down
Loading