diff --git a/src/app/(layout)/advice/_components/AdviceResultBox/index.tsx b/src/app/(layout)/advice/_components/AdviceResultBox/index.tsx index cc1c8b3..d841934 100644 --- a/src/app/(layout)/advice/_components/AdviceResultBox/index.tsx +++ b/src/app/(layout)/advice/_components/AdviceResultBox/index.tsx @@ -2,6 +2,7 @@ import { Spacing } from '@/components/ui/Spacing'; import IconSave from '@/assets/icons/icon-save.svg'; import IconCopy from '@/assets/icons/icon-copy.svg'; import { useModalStore } from '@/stores/useModalStore'; +import { useAuth } from '@/hooks/useAuth'; interface AdviceResultProps { content: string; @@ -9,6 +10,7 @@ interface AdviceResultProps { export default function AdviceResult({ content }: AdviceResultProps) { const { openModal } = useModalStore(); + const { requireAuth } = useAuth(); return (
@@ -27,11 +29,13 @@ export default function AdviceResult({ content }: AdviceResultProps) {
diff --git a/src/components/ui/Modal/ViewModal/index.tsx b/src/components/ui/Modal/ViewModal/index.tsx index 5a598cc..aea2ce7 100644 --- a/src/components/ui/Modal/ViewModal/index.tsx +++ b/src/components/ui/Modal/ViewModal/index.tsx @@ -8,8 +8,12 @@ import { useModalStore } from '@/stores/useModalStore'; import { useTemplateStore } from '@/stores/useTemplateStore'; import { useAuth } from '@/hooks/useAuth'; import { getTemplateDetail, TemplateDetail } from '@/services/template/getTemplateDetail'; +import { useDeleteTemplate } from '@/hooks/template/useTemplateDeletes'; +import { useTemplateUse } from '@/hooks/template/useTemplateUse'; +import { useLikePost } from '@/hooks/template/useTemplateLikes'; import { Spacing } from '../../Spacing'; import { Button } from '../../Button'; +import ArchiveModalWarning from '@/app/(layout)/archive/_components/ArchiveModalWarning'; import IconGlowScore from '@/assets/icons/icon-glow-score.svg'; import IconClose from '@/assets/icons/icon-close.svg'; import IconLike from '@/assets/icons/icon-like.svg'; @@ -22,16 +26,23 @@ import Toast from '../../Toast'; export default function ViewModal() { const router = useRouter(); + const { mutate: templateDelete } = useDeleteTemplate(); + const { mutate: templateUse } = useTemplateUse(); + const { mutate: templateLike } = useLikePost(); + const { selectedTemplateId, openModal, closeModal } = useModalStore(); const { setCurrentTemplate } = useTemplateStore(); const { requireAuth } = useAuth(); const [template, setTemplate] = useState(null); + const [hasLiked, setHasLiked] = useState(false); + const [likeCount, setLikeCount] = useState(); const [folderId, setFolderId] = useState(); const [loading, setLoading] = useState(true); const [dropdown, setDropdown] = useState(false); const [toastVisible, setToastVisible] = useState(false); const [toastMessage, setToastMessage] = useState(''); + const [deleteModalOpen, setDeleteModalOpen] = useState(false); const inputs = Array.from(template?.content?.matchAll(/{(.*?)}/g) ?? []).map((m) => m[1]); @@ -43,6 +54,8 @@ export default function ViewModal() { const data = await getTemplateDetail(selectedTemplateId); setTemplate(data); setFolderId(data?.savedFolder?.folderId ?? 0); + setLikeCount(data?.likeCount ?? 0); + setHasLiked(data?.hasLiked ?? false); } catch (err) { console.error('템플릿 상세 불러오기 실패:', err); closeModal(); @@ -58,6 +71,7 @@ export default function ViewModal() { const handleCilckUse = () => { if (requireAuth()) { + templateUse(template.templateId); closeModal(); openModal('using', { templateId: template.templateId, @@ -78,7 +92,17 @@ export default function ViewModal() { }); }; + const handleDeleteConfirm = () => { + templateDelete(template.templateId); + window.location.reload(); + }; + + const handleClickDelete = () => { + setDeleteModalOpen(true); + }; + const handleClickAiUse = () => { + templateUse(template.templateId); setCurrentTemplate({ templateId: template.templateId, content: template.content }); closeModal(); router.push('/advice'); @@ -113,6 +137,19 @@ export default function ViewModal() { } }; + const handleClickLike = () => { + // TODO : 리팩토링 시 debounce 넣기기 + templateLike(template.templateId); + + if (!hasLiked) { + setLikeCount((likeCount as number) + 1); + } else { + setLikeCount((likeCount as number) - 1); + } + + setHasLiked((prev) => (!prev)) + }; + return (
{/* 태그 */} @@ -159,7 +196,7 @@ export default function ViewModal() {
{template?.isAuthor && ( <> - @@ -212,10 +249,10 @@ export default function ViewModal() {
추천수
-
+
+ {likeCount} +
@@ -231,6 +268,13 @@ export default function ViewModal() {
{toastVisible && setToastVisible(false)} />} + { + setDeleteModalOpen(false); + }} + /> ); } diff --git a/src/hooks/template/useTemplateDeletes.ts b/src/hooks/template/useTemplateDeletes.ts new file mode 100644 index 0000000..97e62a6 --- /dev/null +++ b/src/hooks/template/useTemplateDeletes.ts @@ -0,0 +1,6 @@ +import { deleteTemplate } from '@/services/template/deleteTemplate'; +import { useMutation } from '@tanstack/react-query'; + +export const useDeleteTemplate = () => { + return useMutation({mutationFn : (templateId : number) => deleteTemplate(templateId)}); +} \ No newline at end of file diff --git a/src/hooks/template/useTemplateLikes.ts b/src/hooks/template/useTemplateLikes.ts index ef07891..3f7acfd 100644 --- a/src/hooks/template/useTemplateLikes.ts +++ b/src/hooks/template/useTemplateLikes.ts @@ -1,5 +1,6 @@ import { getTemplateLikes } from '@/services/template/getTemplateLikes'; -import { useQuery } from '@tanstack/react-query'; +import { postTemplateLike } from '@/services/template/postTemplateLike'; +import { useQuery, useMutation } from '@tanstack/react-query'; export const useTemplatesLikes = (options?: { enabled?: boolean }) => { return useQuery({ @@ -8,3 +9,7 @@ export const useTemplatesLikes = (options?: { enabled?: boolean }) => { enabled: options?.enabled ?? true, }); }; + +export const useLikePost = () => { + return useMutation({mutationFn : (templateId : number) => postTemplateLike(templateId)}); +} diff --git a/src/hooks/template/useTemplateUse.ts b/src/hooks/template/useTemplateUse.ts new file mode 100644 index 0000000..5fe9c31 --- /dev/null +++ b/src/hooks/template/useTemplateUse.ts @@ -0,0 +1,6 @@ +import { postTemplateUse } from '@/services/template/postTemplateUse'; +import { useMutation } from '@tanstack/react-query'; + +export const useTemplateUse = () => { + return useMutation({mutationFn : (templateId : number) => postTemplateUse(templateId)}); +} \ No newline at end of file diff --git a/src/services/template/deleteTemplate.ts b/src/services/template/deleteTemplate.ts new file mode 100644 index 0000000..40b2482 --- /dev/null +++ b/src/services/template/deleteTemplate.ts @@ -0,0 +1,5 @@ +import { customFetch } from '@/utils/customFetch'; + +export const deleteTemplate = async (templateId: number): Promise => { + return customFetch(`/templates/${templateId}`, { method: 'DELETE' }); +}; diff --git a/src/services/template/getTemplateDetail.ts b/src/services/template/getTemplateDetail.ts index af88862..04f447d 100644 --- a/src/services/template/getTemplateDetail.ts +++ b/src/services/template/getTemplateDetail.ts @@ -14,6 +14,7 @@ export interface TemplateDetail { tags: string[]; likeCount: number; isPrivate: boolean; + hasLiked: boolean; saved?: boolean; savedFolder?: { folderId: number; name: string }; } diff --git a/src/services/template/postTemplateLike.ts b/src/services/template/postTemplateLike.ts new file mode 100644 index 0000000..6914b5c --- /dev/null +++ b/src/services/template/postTemplateLike.ts @@ -0,0 +1,5 @@ +import { customFetch } from '@/utils/customFetch'; + +export const postTemplateLike = async (templateId: number): Promise => { + return customFetch(`/templates/${templateId}/likes`, { method: 'POST' }); +}; diff --git a/src/services/template/postTemplateUse.ts b/src/services/template/postTemplateUse.ts new file mode 100644 index 0000000..855ca9d --- /dev/null +++ b/src/services/template/postTemplateUse.ts @@ -0,0 +1,5 @@ +import { customFetch } from '@/utils/customFetch'; + +export const postTemplateUse = async (templateId: number): Promise => { + return customFetch(`/templates/${templateId}/use`, { method: 'POST' }); +};