Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
dbec029
[feat] ๊ธ€์“ฐ๊ธฐ ๊ธฐ๋Šฅ
EunbinJung Oct 10, 2025
64d9a8e
[feat] ํฌ์ŠคํŠธ ์ž‘์„ฑ ๊ธฐ๋Šฅ
EunbinJung Oct 10, 2025
0692f9d
Merge branch 'dev' into feat/write#19
EunbinJung Oct 10, 2025
c63aa72
Feat/communityscroll#23 (#114)
EunbinJung Oct 10, 2025
b381dc0
[feat] ๊ธ€์“ฐ๊ธฐ ๊ธฐ๋Šฅ
EunbinJung Oct 10, 2025
b1edcc9
์นดํ…Œ๊ณ ๋ฆฌํ•„์ˆ˜
EunbinJung Oct 10, 2025
5cf0cc4
[feat] ํฌ์ŠคํŠธ ๋ฌดํ•œ์Šคํฌ๋กค + ๊ธ€์“ฐ๊ธฐ๊ธฐ๋Šฅ ์ด๋ฏธ์ง€์ถ”๊ฐ€
EunbinJung Oct 10, 2025
37e6b64
[feat] ์ด๋ฏธ์ง€ ์Šค์™€์ดํผ
EunbinJung Oct 11, 2025
0df3876
[feat] ํ”„๋กœํ•„ ์‘ค๋ฆฌ ์ด๋ฏธ์ง€
EunbinJung Oct 11, 2025
c03685b
[feat] ๋Œ“๊ธ€ ๋ˆ„๋ฅด๋ฉด ๋Œ“๊ธ€ ์„น์…˜์œผ๋กœ ๊ฐ€๊ธฐ
EunbinJung Oct 11, 2025
0b9ff6a
[feat] ์ข‹์•„์š”๊ธฐ๋Šฅ(์•„์ง ์ข‹์•„์š”๋ฐ›์•„์˜ค๋Š”๊ฑด ๋ชปํ•จ apiํ•„์š”)
EunbinJung Oct 11, 2025
06f4218
[feat] ๊ฒŒ์‹œ๋ฌผ ์ˆ˜์ •
EunbinJung Oct 11, 2025
41bfd9f
[feat] ๊ธ€ ์ˆ˜์ •
EunbinJung Oct 12, 2025
a9013e6
[refactor] ์ฝ”๋“œ ์กฐ๊ธˆ์ •๋ฆฌ
EunbinJung Oct 12, 2025
7ca7f8b
[feat] ์ž‘์„ฑ์ž๋ณธ์ธ๋งŒ ๊ธ€์ˆ˜์ •์‚ญ์ œ
EunbinJung Oct 12, 2025
d555f39
[feat]๊ธ€ ์‚ญ์ œ๊ธฐ๋Šฅ
EunbinJung Oct 12, 2025
9439bfc
[feat] ์นตํ…Œ์ผํƒœ๊ทธ
EunbinJung Oct 12, 2025
5b72ae4
[fix]์นตํ…Œ์ผ, ์‰์–ด ๊ธฐ๋Šฅ
EunbinJung Oct 12, 2025
5801561
์ˆ˜ ๋ผ์šฐํ„ฐ, ๋น„๋กœ๊ทธ์ธ์ฒ˜๋ฆฌ
EunbinJung Oct 12, 2025
fc5d719
ํƒ€์ž… ์ˆ˜์ •
EunbinJung Oct 12, 2025
18a7eeb
ํƒ€์ž… ์ˆ˜์ •
EunbinJung Oct 12, 2025
9b385db
ํƒ€์ž…์ˆ˜์ •
EunbinJung Oct 12, 2025
379e6e2
ํƒ€์ž…์ˆ˜์ •
EunbinJung Oct 12, 2025
f5a3870
ํƒ€์ž…์ˆ˜์ •
EunbinJung Oct 12, 2025
f1dd851
์˜ค๋ฅ˜์ˆ˜์ •
EunbinJung Oct 12, 2025
1975647
์˜ค๋ฅ˜์ˆ˜์ •
EunbinJung Oct 12, 2025
8beb817
์˜ค๋ฅ˜์ˆ˜์ •
EunbinJung Oct 12, 2025
f811379
์˜ค๋ฅ˜์ˆ˜์ •
EunbinJung Oct 12, 2025
fabb821
์˜ค๋ฅ˜์ˆ˜์ •
EunbinJung Oct 12, 2025
e3f356b
Merge branch 'dev' into feat/write#19
EunbinJung Oct 12, 2025
1cba59c
์ถฉ๋Œํ•ด๊ฒฐ
EunbinJung Oct 12, 2025
2a783d6
์˜ค๋ฅ˜ ์ˆ˜์ •
EunbinJung Oct 12, 2025
641b8e8
์˜ค๋ฅ˜ ์ˆ˜์ •
EunbinJung Oct 12, 2025
d9ffb6f
์˜ค๋ฅ˜ ์ˆ˜์ •
EunbinJung Oct 12, 2025
c32ae58
[fix] ์ด๋ฏธ์ง€ ์นด์šดํŠธ, 10๊ฐœ ์ œํ•œ
EunbinJung Oct 12, 2025
74462d0
[fix] ๊ธ€์“ฐ๊ธฐ placeholder
EunbinJung Oct 12, 2025
03b3ab9
[fix] ๊ณต์œ  url ์ˆ˜์ •
EunbinJung Oct 12, 2025
01575c1
[fix] ํ”„๋กœํ•„๋ฐฐ๊ฒฝ ์ง€์šฐ๊ธฐ
EunbinJung Oct 12, 2025
aa5d0ef
[fix] ํ”Œ๋กœํŒ…ํƒญ ๋ฏธ๋””์–ด์ฟผ๋ฆฌ ์ˆ˜์ •
EunbinJung Oct 12, 2025
e59ce26
[fix] ์ˆ˜์ • ๋ชจ๋‹ฌ
EunbinJung Oct 12, 2025
d8bae3c
๋Œ“๊ธ€์‹ค์‹œ๊ฐ„๋ฐ˜์˜ ์‹œ๋„
EunbinJung Oct 12, 2025
18647b5
์ˆ˜์ •
EunbinJung Oct 12, 2025
b51e65b
Merge branch 'dev' into feat/write#19
EunbinJung Oct 12, 2025
0ce9b6d
[fix] ์ˆ˜์ •๋ชจ๋‹ฌ ๋กœ์ง ์ˆ˜์ •
EunbinJung Oct 12, 2025
3c5bb99
์ˆ˜์ •๋กœ์ง ์ˆ˜์ •
EunbinJung Oct 12, 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
1 change: 1 addition & 0 deletions src/app/community/edit/[postId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useParams } from 'next/navigation';

function Page() {
const params = useParams();
console.log(params);

return (
<div className="w-full mb-20 flex relative">
Expand Down
2 changes: 1 addition & 1 deletion src/domains/community/detail/DetailContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function DetailContent({
<CocktailTag use="detail" selectedTags={tags} />
<PostInfo createdAt={createdAt} viewCount={viewCount} commentCount={commentCount} />

<div className="block md:hidden mt-2">
<div className="block lg:hidden mt-2">
<DetailTabMobile
likeCount={prevLikeCount ?? 0}
like={like}
Expand Down
8 changes: 7 additions & 1 deletion src/domains/community/detail/DetailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@ import { useParams } from 'next/navigation';
import { useAuthStore } from '@/domains/shared/store/auth';
import Button from '@/shared/components/button/Button';
import { useRouter } from 'next/navigation';
import { useComments } from '../hook/useComment';

function DetailPage() {
const params = useParams();
const postId = params.id;

const user = useAuthStore((state) => state.user);

const [postDetail, setPostDetail] = useState<Post | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [like, setLike] = useState(false);
Expand All @@ -27,6 +30,8 @@ function DetailPage() {
const isLoggedIn = useAuthStore((state) => state.isLoggedIn);
const router = useRouter();

const { comments } = useComments(postId, user);

const commentRef = useRef<HTMLElement | null>(null);

useEffect(() => {
Expand Down Expand Up @@ -119,7 +124,7 @@ function DetailPage() {
</section>
</article>
{isLoggedIn && (
<div className="hidden md:block">
<div className="hidden lg:block">
<DetailTabDesktop
likeCount={prevLikeCount ?? 0}
commentCount={commentCount}
Expand All @@ -128,6 +133,7 @@ function DetailPage() {
onLikeToggle={handleLike}
title={title}
imageUrls={imageUrls}
comments={comments}
/>
</div>
)}
Expand Down
27 changes: 16 additions & 11 deletions src/domains/community/detail/tab/DetailTabDesktop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import CommentBtn from '../../components/comment/CommentBtn';
import LikeBtn from '../../components/like/LikeBtn';
import ShareModal from '@/domains/shared/components/share/ShareModal';
import { RefObject, useState } from 'react';
import { useParams } from 'next/navigation';
import { CommentType } from '../../types/post';

type Props = {
likeCount: number;
Expand All @@ -14,6 +16,7 @@ type Props = {
onLikeToggle: () => void;
title: string;
imageUrls: string[];
comments: CommentType[] | null;
};

interface Meta {
Expand All @@ -30,10 +33,14 @@ function DetailTabDesktop({
onLikeToggle,
title,
imageUrls,
comments,
}: Props) {
const [isShare, setIsShare] = useState(false);
const [meta, setMeta] = useState<Meta | null>(null);

const params = useParams();
const postId = params?.id;

const handleClick = () => {
if (commentRef.current) {
const top = commentRef.current.getBoundingClientRect().top + window.scrollY - 100; // 100px ์œ„๋กœ offset
Expand All @@ -43,15 +50,13 @@ function DetailTabDesktop({

// โœ… ๊ณต์œ  ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ meta ์ƒ์„ฑ
const handleShareClick = () => {
if (typeof window !== 'undefined') {
const currentUrl = window.location.href;
setMeta({
title,
url: currentUrl,
imageUrl: imageUrls[0] || getOgImage(),
});
setIsShare(true);
}
const currentUrl = `http://www.ssoul.life/community/${postId}`;
setMeta({
title,
url: currentUrl,
imageUrl: imageUrls[0] || getOgImage(),
});
setIsShare(true);
};

// โœ… og:image ๋ฉ”ํƒ€ํƒœ๊ทธ์—์„œ ์ด๋ฏธ์ง€ ๊ฐ€์ ธ์˜ค๊ธฐ (fallback์šฉ)
Expand All @@ -64,7 +69,7 @@ function DetailTabDesktop({
<>
<section
aria-label="๊ฒŒ์‹œ๊ธ€ ์ธํ„ฐ๋ž™์…˜ ๋ฒ„ํŠผ"
className="absolute top-[50px] 2xl:right-80 xl:right-50 lg:right-10 md:right-10 z-10 h-full transition-transform duration-300 ease-in-out"
className="absolute top-[50px] 2xl:right-60 xl:right-50 lg:right-10 md:right-10 z-10 h-full transition-transform duration-300 ease-in-out"
>
<div className="sticky top-[183px]">
<div className="flex md:flex-col md:gap-10 w-full h-full">
Expand All @@ -74,7 +79,7 @@ function DetailTabDesktop({
</div>
<div className="flex md:flex-col justify-center items-center gap-2 text-sm text-gray">
<CommentBtn size="md" onClick={handleClick} />
<span>{commentCount}</span>
<span>{comments?.length}</span>
</div>
<div>
<Share variants="community" size="md" onClick={handleShareClick} />
Expand Down
20 changes: 11 additions & 9 deletions src/domains/community/detail/tab/DetailTabMobile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Share from '@/domains/shared/components/share/Share';
import LikeBtn from '../../components/like/LikeBtn';
import { useState } from 'react';
import ShareModal from '@/domains/shared/components/share/ShareModal';
import { useParams } from 'next/navigation';

type Props = {
likeCount: number;
Expand All @@ -23,16 +24,17 @@ function DetailTabMobile({ likeCount, onLikeToggle, like, title, imageUrls }: Pr
const [isShare, setIsShare] = useState(false);
const [meta, setMeta] = useState<Meta | null>(null);

const params = useParams();
const postId = params?.id;

const handleShareClick = () => {
if (typeof window !== 'undefined') {
const currentUrl = window.location.href;
setMeta({
title,
url: currentUrl,
imageUrl: imageUrls[0] || getOgImage(),
});
setIsShare(true);
}
const currentUrl = `http://www.ssoul.life/community/${postId}`;
setMeta({
title,
url: currentUrl,
imageUrl: imageUrls[0] || getOgImage(),
});
setIsShare(true);
};

// โœ… og:image ๋ฉ”ํƒ€ํƒœ๊ทธ์—์„œ ์ด๋ฏธ์ง€ ๊ฐ€์ ธ์˜ค๊ธฐ (fallback์šฉ)
Expand Down
8 changes: 6 additions & 2 deletions src/domains/community/hook/useComment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { CommentType } from '../types/post';
import { User } from '@/domains/shared/store/auth';
import { ParamValue } from 'next/dist/server/request/params';

export function useComments(postId: ParamValue, user: User | null, accessToken: string | null) {
export function useComments(postId: ParamValue, user: User | null, accessToken?: string | null) {
const [comments, setComments] = useState<CommentType[] | null>(null);
const [isEnd, setIsEnd] = useState(false);
const [isLoading, setIsLoading] = useState(false);
Expand All @@ -23,7 +23,7 @@ export function useComments(postId: ParamValue, user: User | null, accessToken:

useEffect(() => {
fetchData();
}, [fetchData]);
}, [postId]);

const handleUpdateComment = async (commentId: number, content: string) => {
if (!user) {
Expand All @@ -39,6 +39,8 @@ export function useComments(postId: ParamValue, user: User | null, accessToken:
)
: prev
);
const updatedComments = await fetchComment(postId);
setComments(updatedComments);
} catch (err) {
console.error(err);
alert('๋Œ“๊ธ€ ์ˆ˜์ • ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.');
Expand All @@ -61,6 +63,8 @@ export function useComments(postId: ParamValue, user: User | null, accessToken:
setComments((prev) =>
prev ? prev.filter((c) => c.commentId !== deleteTarget.commentId) : prev
);
const updatedComments = await fetchComment(postId);
setComments(updatedComments);
} catch (err) {
console.error(err);
alert('๋Œ“๊ธ€ ์‚ญ์ œ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.');
Expand Down
4 changes: 0 additions & 4 deletions src/domains/community/main/CommunityFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ function CommunityFilter({ posts, setPosts }: Props) {
const query = searchParams.get('category');
const router = useRouter();

useEffect(() => {
console.log(query);
}, [query]);

const handleChange = async (selectTitle: string) => {
if (!query) return;

Expand Down
2 changes: 1 addition & 1 deletion src/domains/community/main/PostCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ function PostCard({ posts, isLoading, isEnd, onLoadMore }: Props) {
return (
<article
className="py-4 sm:py-5 border-b-1 border-gray-light"
key={postId}
key={postId + createdAt}
ref={(el) => {
if (index === 0) firstItemRef.current = el;
if (isLast) {
Expand Down
17 changes: 15 additions & 2 deletions src/domains/community/write/CompleteBtn.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
import Button from '@/shared/components/button/Button';

function CompleteBtn({ mode }: { mode: 'edit' | 'create' }) {
type Props = {
mode: 'edit' | 'create';
setEditDone: (value: boolean) => void;
handleEditLogic: () => Promise<boolean>;
};

function CompleteBtn({ mode, setEditDone, handleEditLogic }: Props) {
return (
<div className="w-full flex items-center justify-end mt-10">
<Button type="submit" size="default" color="default">
<Button
type={mode === 'create' ? 'submit' : 'button'}
size="default"
color="default"
onClick={async () => {
setEditDone(true);
}}
>
{mode === 'create' ? '์˜ฌ๋ฆฌ๊ธฐ' : '์ˆ˜์ •ํ•˜๊ธฐ'}
</Button>
</div>
Expand Down
1 change: 1 addition & 0 deletions src/domains/community/write/WriteForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ function WriteForm({ formData, setFormData }: Props) {
</label>
<textarea
id="content"
placeholder="๋‚ด์šฉ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."
role="textbox"
aria-multiline="true"
tabIndex={0}
Expand Down
50 changes: 39 additions & 11 deletions src/domains/community/write/WriteSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import TagModal from './cocktail-tag/TagModal';
import { FormType, TagType, UploadedItem } from '@/domains/recipe/types/types';
import { fetchPostById } from '../api/fetchPost';
import { debounce } from '@/shared/utills/debounce';
import ConfirmModal from '@/shared/components/modal-pop/ConfirmModal';
import DetailSkeleton from '../detail/DetailSkeleton';

type Props = {
mode: 'create' | 'edit';
Expand All @@ -31,6 +33,8 @@ function WriteSection({ mode, postId }: Props) {

const [uploadedFile, setUploadedFile] = useState<UploadedItem[]>([]);
const [isOpen, setIsOpen] = useState(false);
const [editDone, setEditDone] = useState(false);
const [isLoading, setIsLoading] = useState(false);

const [tags, setTags] = useState<TagType[] | null>(null);
const [selectedTags, setSelectedTags] = useState<string[]>([]);
Expand Down Expand Up @@ -169,27 +173,27 @@ function WriteSection({ mode, postId }: Props) {

const debouncedFetch = useMemo(() => debounce(fetchTags, 300), [fetchTags]);

const handleEdit = async (e: React.FormEvent) => {
e.preventDefault();

// ์‹ค์ œ ์ˆ˜์ • ์ฒ˜๋ฆฌ๋งŒ ๋‹ด๋‹น (์ด๋ฒคํŠธ ๋น„์˜์กด)
const handleEditLogic = async (): Promise<boolean> => {
if (!postId) {
toastError('๊ฒŒ์‹œ๊ธ€ ID๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.');
return;
return false; // ์‹คํŒจ ์‹œ false ๋ฐ˜ํ™˜
}

if (!formData.title.trim()) {
toastError('์ œ๋ชฉ์„ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”.');
return;
return false;
}

if (!formData.content.trim()) {
toastError('๋‚ด์šฉ์„ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”.');
return;
return false;
}

const categoryId = tabItem.findIndex((tab) => tab.label === formData.categoryName);
if (categoryId === -1) {
toastError('์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š”.');
return;
return false;
}

const postJson = {
Expand All @@ -205,6 +209,7 @@ function WriteSection({ mode, postId }: Props) {
payload.append('post', postBlob);

try {
setIsLoading(true);
const res = await fetch(`${getApi}/posts/${postId}`, {
method: 'PATCH',
credentials: 'include',
Expand All @@ -213,20 +218,30 @@ function WriteSection({ mode, postId }: Props) {

if (!res.ok) {
toastError('๊ธ€ ์ˆ˜์ •์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.');
return;
return false;
}

router.push(`/community/${postId}`);
setIsLoading(false);

return true;
} catch (err) {
console.error('๊ธ€์ˆ˜์ • ํผ ์ž‘์„ฑ ์—๋Ÿฌ', err);
toastError('์„œ๋ฒ„ ์š”์ฒญ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.');
return false;
}
};

// ํผ ์ œ์ถœ์šฉ ํ•ธ๋“ค๋Ÿฌ (์ด๋ฒคํŠธ ๊ฐ์ฒด ๋ฐ›์•„์„œ preventDefault ์ฒ˜๋ฆฌ)
const handleEditSubmit = async (e: React.FormEvent) => {
e.preventDefault();
};

if (isLoading) <DetailSkeleton />;

return (
<>
<form onSubmit={mode === 'create' ? handleSubmit : handleEdit}>
<CompleteBtn mode={mode} />
<form onSubmit={mode === 'create' ? handleSubmit : handleEditSubmit}>
<CompleteBtn mode={mode} setEditDone={setEditDone} handleEditLogic={handleEditLogic} />
<section>
<FormTitle formData={formData} setFormData={setFormData} />
<Category formData={formData} setFormData={setFormData} />
Expand Down Expand Up @@ -258,6 +273,19 @@ function WriteSection({ mode, postId }: Props) {
debouncedFetch={debouncedFetch}
/>
)}
{mode === 'edit' && editDone && (
<ConfirmModal
open={editDone}
onClose={() => setEditDone(false)}
onCancel={() => setEditDone(false)}
onConfirm={async () => {
setEditDone(false);
await handleEditLogic();
router.push(`/community/${postId}`);
}}
title="์ˆ˜์ • ์™„๋ฃŒ"
/>
)}
</>
);
}
Expand Down
Loading