From 17eb5354d5af7374dbd52ad4fef088992a173503 Mon Sep 17 00:00:00 2001 From: YouD0313 <102004480+YouD0313@users.noreply.github.com> Date: Mon, 16 Jun 2025 17:08:38 +0900 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20=EC=8A=A4=ED=82=AC=ED=83=9C?= =?UTF-8?q?=EA=B7=B8=EA=B4=80=EB=A6=AC=20=EB=93=B1=EB=A1=9D,=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{Inquiry.api.ts => inquiry.api.ts} | 0 .../{Notice.api.ts => notice.api.ts} | 0 src/api/admin/tag.api.ts | 39 +++ .../admin/adminTags/AdminTagCRUD.styled.ts | 80 ++++++ .../admin/adminTags/AdminTagCRUD.tsx | 233 ++++++++++++++++++ .../admin/adminTags/AdminTagsBasic.styled.ts | 23 ++ .../admin/adminTags/AdminTagsBasic.tsx | 71 ++++++ .../positions/AdminPositionItems.styled.ts | 3 + .../positions/AdminPositionItems.tsx | 11 + .../adminTags/positions/AdminPositionTags.tsx | 5 + .../skills/AdminSkillTagItems.styled.ts | 3 + .../adminTags/skills/AdminSkillTagItems.tsx | 28 +++ .../admin/adminTags/skills/AdminSkillTags.tsx | 5 + .../sidebar/sidebarList/AdminSidebarList.tsx | 3 +- .../positionButton/PositionButton.styled.ts | 2 - .../common/positionButton/PositionButton.tsx | 25 +- .../common/skillTagBox/SkillTagBox.tsx | 4 +- .../projectCardLists/cardList/CardList.tsx | 1 - .../filteringContents/FilteringContents.tsx | 2 +- .../positionComponent/PositionComponent.tsx | 12 +- src/constants/admin/sidebar.ts | 11 +- src/constants/routes.ts | 3 +- src/constants/user/modalMessage.ts | 3 + src/hooks/admin/useAdminInquiry.ts | 2 +- src/hooks/admin/useAdminNotice.ts | 2 +- src/hooks/admin/useAdminTag.ts | 61 +++++ src/hooks/admin/useGetAllInquiries.ts | 3 +- src/hooks/queries/keys.ts | 6 + src/hooks/user/useSearchFilteringSkillTag.ts | 7 +- src/models/tags.ts | 5 + src/pages/admin/adminTags/AdminTags.tsx | 3 - .../position/AdminPositionTagsPage.tsx | 5 + .../adminTags/skill/AdminSkillTagsPage.tsx | 5 + src/routes/AdminRoutes.tsx | 59 ++--- 34 files changed, 649 insertions(+), 76 deletions(-) rename src/api/admin/customerService/{Inquiry.api.ts => inquiry.api.ts} (100%) rename src/api/admin/customerService/{Notice.api.ts => notice.api.ts} (100%) create mode 100644 src/api/admin/tag.api.ts create mode 100644 src/components/admin/adminTags/AdminTagCRUD.styled.ts create mode 100644 src/components/admin/adminTags/AdminTagCRUD.tsx create mode 100644 src/components/admin/adminTags/AdminTagsBasic.styled.ts create mode 100644 src/components/admin/adminTags/AdminTagsBasic.tsx create mode 100644 src/components/admin/adminTags/positions/AdminPositionItems.styled.ts create mode 100644 src/components/admin/adminTags/positions/AdminPositionItems.tsx create mode 100644 src/components/admin/adminTags/positions/AdminPositionTags.tsx create mode 100644 src/components/admin/adminTags/skills/AdminSkillTagItems.styled.ts create mode 100644 src/components/admin/adminTags/skills/AdminSkillTagItems.tsx create mode 100644 src/components/admin/adminTags/skills/AdminSkillTags.tsx create mode 100644 src/hooks/admin/useAdminTag.ts delete mode 100644 src/pages/admin/adminTags/AdminTags.tsx create mode 100644 src/pages/admin/adminTags/position/AdminPositionTagsPage.tsx create mode 100644 src/pages/admin/adminTags/skill/AdminSkillTagsPage.tsx diff --git a/src/api/admin/customerService/Inquiry.api.ts b/src/api/admin/customerService/inquiry.api.ts similarity index 100% rename from src/api/admin/customerService/Inquiry.api.ts rename to src/api/admin/customerService/inquiry.api.ts diff --git a/src/api/admin/customerService/Notice.api.ts b/src/api/admin/customerService/notice.api.ts similarity index 100% rename from src/api/admin/customerService/Notice.api.ts rename to src/api/admin/customerService/notice.api.ts diff --git a/src/api/admin/tag.api.ts b/src/api/admin/tag.api.ts new file mode 100644 index 00000000..74d96c5f --- /dev/null +++ b/src/api/admin/tag.api.ts @@ -0,0 +1,39 @@ +import type { ApiCommonBasicType } from '../../models/apiCommon'; +import type { TagFormType } from '../../models/tags'; +import { httpClient } from '../http.api'; + +export const postSkillTag = async (formData: FormData) => { + try { + await httpClient.post(`/skill-tag`, formData); + } catch (e) { + console.error(e); + throw e; + } +}; + +export const deleteSkillTag = async (id: number) => { + try { + await httpClient.delete(`/skill-tag/${id}`); + } catch (e) { + console.error(e); + throw e; + } +}; + +export const postPositionTag = async ({ name }: Pick) => { + try { + await httpClient.post(`/position-tag`, { name }); + } catch (e) { + console.error(e); + throw e; + } +}; + +export const deletePositionTag = async (id: number) => { + try { + await httpClient.delete(`/position-tag/${id}`); + } catch (e) { + console.error(e); + throw e; + } +}; diff --git a/src/components/admin/adminTags/AdminTagCRUD.styled.ts b/src/components/admin/adminTags/AdminTagCRUD.styled.ts new file mode 100644 index 00000000..43305deb --- /dev/null +++ b/src/components/admin/adminTags/AdminTagCRUD.styled.ts @@ -0,0 +1,80 @@ +import styled from 'styled-components'; +import { SendButton } from '../../user/customerService/inquiry/Inquiry.styled'; + +export const CRUDContainer = styled.form` + width: 100%; +`; + +export const CRUDWrapper = styled.div` + width: 70%; + display: flex; + gap: 1rem; + font-size: 1.2rem; + justify-content: space-between; +`; + +export const InfoContainer = styled.div` + display: flex; + flex-direction: column; + gap: 1.5rem; + justify-content: center; +`; + +export const CRUDButtonWrapper = styled.div` + display: grid; + gap: 1rem; +`; + +export const CRUDButton = styled(SendButton)` + height: 2.3rem; +`; + +export const CRUDTitleWrapper = styled.div` + display: flex; + align-items: center; + gap: 1rem; +`; + +export const CRUDTitleHead = styled.span``; + +export const CRUDTitle = styled.input` + border-bottom: 1px solid ${({ theme }) => theme.color.placeholder}; + padding-left: 0.3rem; + font-size: 1rem; +`; + +export const CRUDDefaultButton = styled.button` + svg { + width: 1rem; + height: 1rem; + } +`; + +export const CRUDImgWrapper = styled.div` + display: flex; + align-items: center; + gap: 1rem; +`; + +export const CRUDImgHead = styled.span``; + +export const CRUDImg = styled.img` + width: 3rem; + border: 1px solid ${({ theme }) => theme.color.grey}; +`; + +export const CRUDImgExplore = styled(SendButton)` + cursor: pointer; + border-radius: ${({ theme }) => theme.borderRadius.primary}; + padding: 0.4rem 1rem; +`; + +export const CRUDImgExplain = styled.span` + max-width: 10rem; +`; + +export const CRUDImgInput = styled.input` + visibility: hidden; + width: 0; + height: 0; +`; diff --git a/src/components/admin/adminTags/AdminTagCRUD.tsx b/src/components/admin/adminTags/AdminTagCRUD.tsx new file mode 100644 index 00000000..cc6e03b9 --- /dev/null +++ b/src/components/admin/adminTags/AdminTagCRUD.tsx @@ -0,0 +1,233 @@ +import * as S from './AdminTagCRUD.styled'; +import defaultImg from './../../../assets/defaultImg.png'; +import React, { useEffect, useRef, useState } from 'react'; +import type { PositionTag, SkillTag, TagFormType } from '../../../models/tags'; +import Modal from '../../common/modal/Modal'; +import { useModal } from '../../../hooks/useModal'; +import { MODAL_MESSAGE } from '../../../constants/user/modalMessage'; +import { useSearchFilteringSkillTag } from '../../../hooks/user/useSearchFilteringSkillTag'; +import { XMarkIcon } from '@heroicons/react/24/outline'; + +interface TagState { + type: string; + label: string; + needImgFile: boolean; + handlePostTag: (params: T) => void; + handleDeleteTag: (id: number) => void; +} + +interface AdminTagCRUDProps { + state: TagState; + itemId: number | null; + onGetItemId: (id: null) => void; +} + +interface FormDataType extends TagFormType { + imgName: string; + preview: string; +} + +type Skill = Omit; + +type CRUDDataType = Skill | PositionTag; + +type SubmitButtonType = '등록' | '수정' | '삭제'; + +export default function AdminTagCRUD({ + state, + itemId, + onGetItemId, +}: AdminTagCRUDProps) { + const fileInputRef = useRef(null); + const textInputRef = useRef(null); + const { isOpen, message, handleModalOpen, handleModalClose } = useModal(); + const { skillTagsData, positionTagsData } = useSearchFilteringSkillTag(); + + const [buttonType, setButtonType] = useState('등록'); + const [formState, setFormState] = useState({ + name: '', + imgName: '', + preview: '', + img: undefined, + }); + + const data: CRUDDataType = + state.type === 'skill' + ? skillTagsData.filter((list) => list.id === itemId)[0] + : positionTagsData.filter((list) => list.id === itemId)[0]; + + const handleSubmitTag = (e: React.FormEvent) => { + e.preventDefault(); + + const formData = new FormData(e.currentTarget as HTMLFormElement); + + const name = String(formData.get('name')); + + const isValid = { + name: formState.name.trim() !== '', + imgName: formState.imgName.trim() !== '', + }; + + if (!isValid.name) { + return handleModalOpen(MODAL_MESSAGE.emptyTag); + } + if (!isValid.imgName && !itemId) { + return handleModalOpen(MODAL_MESSAGE.emptySkillImg); + } + + switch (buttonType) { + case '등록': + { + const duplication = + state.type === 'skill' + ? skillTagsData.filter((data) => data.name === name) + : positionTagsData.filter((data) => data.name === name); + if (duplication.length > 0) { + return handleModalOpen(MODAL_MESSAGE.duplicationTag); + } + if (state.type === 'skill') { + state.handlePostTag(formData as T); + } else { + state.handlePostTag(name as T); + } + } + break; + case '삭제': + if (itemId) { + state.handleDeleteTag(itemId); + } + break; + default: + break; + } + handleClickReset(); + onGetItemId(null); + }; + + const handleChangeValue = (e: React.ChangeEvent) => { + const name = e.target.value; + setFormState((prev) => ({ ...prev, name })); + }; + + const handleChangeFile = (e: React.ChangeEvent) => { + const img = e.target.files?.[0]; + const imgName = img?.name || ''; + const preview = img ? URL.createObjectURL(img) : ''; + setFormState((prev) => ({ ...prev, imgName, preview, img })); + }; + + const handleClickReset = () => { + setFormState({ + name: '', + imgName: '', + preview: '', + img: undefined, + }); + onGetItemId(null); + setTimeout(() => { + if (fileInputRef.current) { + fileInputRef.current.value = ''; + } + if (textInputRef.current) { + textInputRef.current.value = ''; + } + }, 1000); + }; + + const handleClickChangeButtonType = ( + e: React.MouseEvent + ) => { + const id = e.currentTarget.id as SubmitButtonType; + console.log('커런트 타겟: ', id, 'itemId: ', itemId); + + setButtonType(id); + }; + + useEffect(() => { + if (data) { + if (state.type === 'skill') { + const skillData = data as unknown as Skill; + if (skillData.img) { + const preview = skillData.img as string; + setFormState((prev) => ({ ...prev, name: data.name, preview })); + } + } else { + setFormState((prev) => ({ ...prev, name: data.name })); + } + } + }, [data, state]); + + useEffect(() => { + return () => { + if (formState.preview) { + URL.revokeObjectURL(formState.preview); + } + }; + }, [formState]); + + return ( + + + + + {state.label}: + + {itemId && ( + + + + )} + + {state.type === 'skill' && ( + + 이미지: + + + 파일찾기 + + {formState.imgName} + + + )} + + + + {itemId ? '수정' : '등록'} + + {Boolean(itemId) && ( + + 삭제 + + )} + + + + {message} + + + ); +} diff --git a/src/components/admin/adminTags/AdminTagsBasic.styled.ts b/src/components/admin/adminTags/AdminTagsBasic.styled.ts new file mode 100644 index 00000000..c6810198 --- /dev/null +++ b/src/components/admin/adminTags/AdminTagsBasic.styled.ts @@ -0,0 +1,23 @@ +import styled from 'styled-components'; + +export const Container = styled.main` + width: 100%; + display: flex; + gap: 3rem; + flex-direction: column; +`; + +export const CRUDContainer = styled.section` + width: 90%; + border: 1px solid ${({ theme }) => theme.color.placeholder}; + border-radius: ${({ theme }) => theme.borderRadius.large}; + display: flex; + justify-content: center; + padding: 2rem 3rem; +`; + +export const ItemContainer = styled.section` + width: 90%; + border: 1px solid ${({ theme }) => theme.color.placeholder}; + border-radius: ${({ theme }) => theme.borderRadius.large}; +`; diff --git a/src/components/admin/adminTags/AdminTagsBasic.tsx b/src/components/admin/adminTags/AdminTagsBasic.tsx new file mode 100644 index 00000000..ef0c3987 --- /dev/null +++ b/src/components/admin/adminTags/AdminTagsBasic.tsx @@ -0,0 +1,71 @@ +import { useState } from 'react'; +import * as S from './AdminTagsBasic.styled'; +import { useAdminSkillTag } from '../../../hooks/admin/useAdminTag'; +import { useLocation } from 'react-router-dom'; +import AdminTagCRUD from './AdminTagCRUD'; +import AdminSkillTagItems from './skills/AdminSkillTagItems'; +import type { TagFormType } from '../../../models/tags'; +import AdminPositionItems from './positions/AdminPositionItems'; + +export type TWitchTag = 'skill' | 'position'; + +export default function AdminTagsBasic() { + const location = useLocation(); + const pathname = location.pathname; + const witchTag: TWitchTag = pathname.includes('skill') ? 'skill' : 'position'; + + const { + postSkillTagMutate, + deleteSkillTagMutate, + postPositionTagMutate, + deletePositionTagMutate, + } = useAdminSkillTag(); + const [itemId, setItemId] = useState(null); + + const tagState = { + skill: { + type: 'skill', + label: '스킬', + needImgFile: true, + handlePostTag: (formData: FormData) => + postSkillTagMutate.mutate(formData), + handleDeleteTag: (id: number) => deleteSkillTagMutate.mutate(id), + }, + position: { + type: 'position', + label: '포지션', + needImgFile: false, + handlePostTag: (name: Pick) => + postPositionTagMutate.mutate(name), + handleDeleteTag: (id: number) => deletePositionTagMutate.mutate(id), + }, + }; + + const handleGetItemId = (id: number | null) => { + setItemId(id); + }; + + return ( + + + {witchTag === 'skill' ? ( + + state={tagState.skill} + itemId={itemId} + onGetItemId={handleGetItemId} + /> + ) : ( + > + state={tagState.position} + itemId={itemId} + onGetItemId={handleGetItemId} + /> + )} + + + + + + + ); +} diff --git a/src/components/admin/adminTags/positions/AdminPositionItems.styled.ts b/src/components/admin/adminTags/positions/AdminPositionItems.styled.ts new file mode 100644 index 00000000..926ed2ed --- /dev/null +++ b/src/components/admin/adminTags/positions/AdminPositionItems.styled.ts @@ -0,0 +1,3 @@ +import styled from 'styled-components'; + +export const Container = styled.section``; diff --git a/src/components/admin/adminTags/positions/AdminPositionItems.tsx b/src/components/admin/adminTags/positions/AdminPositionItems.tsx new file mode 100644 index 00000000..4a6bae5e --- /dev/null +++ b/src/components/admin/adminTags/positions/AdminPositionItems.tsx @@ -0,0 +1,11 @@ +import * as S from './AdminPositionItems.styled'; + +interface AdminPositionItemsProps { + onGetItemId: (id: number) => void; +} + +export default function AdminPositionItems({ + onGetItemId, +}: AdminPositionItemsProps) { + return ; +} diff --git a/src/components/admin/adminTags/positions/AdminPositionTags.tsx b/src/components/admin/adminTags/positions/AdminPositionTags.tsx new file mode 100644 index 00000000..5d4fb814 --- /dev/null +++ b/src/components/admin/adminTags/positions/AdminPositionTags.tsx @@ -0,0 +1,5 @@ +import AdminTagsBasic from '../AdminTagsBasic'; + +export default function AdminPositionTags() { + return ; +} diff --git a/src/components/admin/adminTags/skills/AdminSkillTagItems.styled.ts b/src/components/admin/adminTags/skills/AdminSkillTagItems.styled.ts new file mode 100644 index 00000000..505a0efe --- /dev/null +++ b/src/components/admin/adminTags/skills/AdminSkillTagItems.styled.ts @@ -0,0 +1,3 @@ +import styled from 'styled-components'; + +export const SkillTagItemWrapper = styled.section``; diff --git a/src/components/admin/adminTags/skills/AdminSkillTagItems.tsx b/src/components/admin/adminTags/skills/AdminSkillTagItems.tsx new file mode 100644 index 00000000..7ffce171 --- /dev/null +++ b/src/components/admin/adminTags/skills/AdminSkillTagItems.tsx @@ -0,0 +1,28 @@ +import * as S from './AdminSkillTagItems.styled'; +import SkillTagBox from '../../../common/skillTagBox/SkillTagBox'; + +interface AdminSKillTagItemsProps { + onGetItemId: (id: number) => void; +} + +export default function AdminSkillTagItems({ + onGetItemId, +}: AdminSKillTagItemsProps) { + const handleClickGetId = (e: React.MouseEvent) => { + e.stopPropagation(); + const target = e.target as HTMLElement; + + const id = Number( + target.dataset.id || target.closest('[data-id]')?.getAttribute('data-id') + ); + + if (!id) return; + onGetItemId(id); + }; + + return ( + + + + ); +} diff --git a/src/components/admin/adminTags/skills/AdminSkillTags.tsx b/src/components/admin/adminTags/skills/AdminSkillTags.tsx new file mode 100644 index 00000000..ee314178 --- /dev/null +++ b/src/components/admin/adminTags/skills/AdminSkillTags.tsx @@ -0,0 +1,5 @@ +import AdminTagsBasic from '../AdminTagsBasic'; + +export default function AdminSkillTags() { + return ; +} diff --git a/src/components/common/admin/sidebar/sidebarList/AdminSidebarList.tsx b/src/components/common/admin/sidebar/sidebarList/AdminSidebarList.tsx index 19caf835..7502472f 100644 --- a/src/components/common/admin/sidebar/sidebarList/AdminSidebarList.tsx +++ b/src/components/common/admin/sidebar/sidebarList/AdminSidebarList.tsx @@ -18,7 +18,8 @@ const iconMap = { notice: , faq: , banner: , - tags: , + skillTags: , + positionTags: , allUser: , reports: , inquiries: , diff --git a/src/components/common/positionButton/PositionButton.styled.ts b/src/components/common/positionButton/PositionButton.styled.ts index 86d6acdd..58d284ec 100644 --- a/src/components/common/positionButton/PositionButton.styled.ts +++ b/src/components/common/positionButton/PositionButton.styled.ts @@ -1,7 +1,5 @@ import styled, { css } from 'styled-components'; -export const Container = styled.div``; - export const PositionButton = styled.button<{ $isSelected: boolean; $isHover: boolean; diff --git a/src/components/common/positionButton/PositionButton.tsx b/src/components/common/positionButton/PositionButton.tsx index 18384464..8a0508f5 100644 --- a/src/components/common/positionButton/PositionButton.tsx +++ b/src/components/common/positionButton/PositionButton.tsx @@ -2,29 +2,28 @@ import * as S from './PositionButton.styled'; interface PositionButtonProps { position: string; - onClick?: (e: React.MouseEvent) => void; + onClickSelect?: (e: React.MouseEvent) => void; isSelected?: boolean; isHover?: boolean; - fontSize: boolean; + fontSize?: boolean; } export default function PositionButton({ position, - onClick, + onClickSelect, isSelected = false, isHover = false, fontSize = false, }: PositionButtonProps) { return ( - - - {position} - - + onClickSelect?.(e)} + > + {position} + ); } diff --git a/src/components/common/skillTagBox/SkillTagBox.tsx b/src/components/common/skillTagBox/SkillTagBox.tsx index d59aa050..1c2ac766 100644 --- a/src/components/common/skillTagBox/SkillTagBox.tsx +++ b/src/components/common/skillTagBox/SkillTagBox.tsx @@ -6,7 +6,7 @@ import { ArrowUturnLeftIcon } from '@heroicons/react/24/outline'; import { useSaveSearchFiltering } from '../../../hooks/user/useSaveSearchFiltering'; export interface SkillTagBoxProps { - width: string; + width?: string; onHandleSkillTagReset?: React.MouseEventHandler; selectedTag?: number[]; isMain?: boolean; @@ -14,7 +14,7 @@ export interface SkillTagBoxProps { } export default function SkillTagBox({ - width, + width = '100%', onHandleSkillTagReset, selectedTag, isMain = false, diff --git a/src/components/user/home/projectCardLists/cardList/CardList.tsx b/src/components/user/home/projectCardLists/cardList/CardList.tsx index 4975a5bb..82c0f65a 100644 --- a/src/components/user/home/projectCardLists/cardList/CardList.tsx +++ b/src/components/user/home/projectCardLists/cardList/CardList.tsx @@ -28,7 +28,6 @@ export default function CardList({ list }: CardListProps) { ))} {list.positions.length > listPositionTag.length && ( diff --git a/src/components/user/home/searchFiltering/filteringContents/FilteringContents.tsx b/src/components/user/home/searchFiltering/filteringContents/FilteringContents.tsx index 9288dbcd..979ffc2c 100644 --- a/src/components/user/home/searchFiltering/filteringContents/FilteringContents.tsx +++ b/src/components/user/home/searchFiltering/filteringContents/FilteringContents.tsx @@ -18,7 +18,7 @@ export default function FilteringContents() { setSkillTagButtonToggle((prev) => !prev); }; - const handleSkillTagFilterClick = (e: React.MouseEvent) => { + const handleSkillTagFilterClick = (e: React.MouseEvent) => { e.stopPropagation(); const target = e.target as HTMLElement; diff --git a/src/components/user/projectFormComponents/projectInformationInput/positionComponent/PositionComponent.tsx b/src/components/user/projectFormComponents/projectInformationInput/positionComponent/PositionComponent.tsx index ad267d8e..00d0e4a1 100644 --- a/src/components/user/projectFormComponents/projectInformationInput/positionComponent/PositionComponent.tsx +++ b/src/components/user/projectFormComponents/projectInformationInput/positionComponent/PositionComponent.tsx @@ -1,3 +1,4 @@ +import React from 'react'; import useTagSelectors from '../../../../../hooks/user/ProjectHooks/useTagSelectors'; import type { CreateProjectFormValues } from '../../../../../models/createProject'; import type { PositionTag } from '../../../../../models/tags'; @@ -27,6 +28,13 @@ const MozipCategoryComponent = ({ fieldName: 'position', }); + const handleClickSelect = ( + e: React.MouseEvent, + idx: number + ) => { + handleClick(e, idx + 1); + }; + return ( @@ -36,9 +44,7 @@ const MozipCategoryComponent = ({ ) => - handleClick(e, idx + 1) - } + onClickSelect={(e) => handleClickSelect(e, idx)} key={idx + 1} isHover={true} fontSize={true} diff --git a/src/constants/admin/sidebar.ts b/src/constants/admin/sidebar.ts index 541e8f49..40df363e 100644 --- a/src/constants/admin/sidebar.ts +++ b/src/constants/admin/sidebar.ts @@ -30,9 +30,14 @@ export const SIDEBAR_LIST = { router: ADMIN_ROUTE.banner, }, { - name: 'tags', - title: '태그관리', - router: ADMIN_ROUTE.tags, + name: 'skillTags', + title: '스킬 태그', + router: ADMIN_ROUTE.skillTags, + }, + { + name: 'positionTags', + title: '포지션 태그', + router: ADMIN_ROUTE.positionTags, }, ], user: [ diff --git a/src/constants/routes.ts b/src/constants/routes.ts index d93d7150..3f1cc3c1 100644 --- a/src/constants/routes.ts +++ b/src/constants/routes.ts @@ -37,7 +37,8 @@ export const ADMIN_ROUTE = { notice: 'notice', faq: 'faq', banner: 'banner', - tags: 'tags', + skillTags: 'skill-tags', + positionTags: 'position-tags', users: 'users', reports: 'reports', inquiries: 'inquiries', diff --git a/src/constants/user/modalMessage.ts b/src/constants/user/modalMessage.ts index 0fa52b62..0556fbe4 100644 --- a/src/constants/user/modalMessage.ts +++ b/src/constants/user/modalMessage.ts @@ -27,4 +27,7 @@ export const MODAL_MESSAGE = { alreadyApply: '이미 참여한/지원하신 공고 입니다.', noMemberToEvaluate: '평가 할 멤버가 없습니다.', noTagsData: '대표 스킬/포지션을 입력 하셔야 사용할 수 있습니다.', + duplicationTag: '이미 존재하는 태그입니다.', + emptyTag: '태그명을 입력하세요.', + emptySkillImg: '스킬 이미지를 추가하세요.', } as const; diff --git a/src/hooks/admin/useAdminInquiry.ts b/src/hooks/admin/useAdminInquiry.ts index ebb7d3a1..a24e311c 100644 --- a/src/hooks/admin/useAdminInquiry.ts +++ b/src/hooks/admin/useAdminInquiry.ts @@ -4,7 +4,7 @@ import { getInquiryDetail, patchInquiryAnswer, postInquiryAnswer, -} from '../../api/admin/customerService/Inquiry.api'; +} from '../../api/admin/customerService/inquiry.api'; import type { InquiryAnswerBody } from '../../models/inquiry'; import { AxiosError } from 'axios'; import { CustomerService } from '../queries/keys'; diff --git a/src/hooks/admin/useAdminNotice.ts b/src/hooks/admin/useAdminNotice.ts index e06a5cac..e4345710 100644 --- a/src/hooks/admin/useAdminNotice.ts +++ b/src/hooks/admin/useAdminNotice.ts @@ -3,7 +3,7 @@ import { deleteNotice, postNotice, putNotice, -} from '../../api/admin/customerService/Notice.api'; +} from '../../api/admin/customerService/notice.api'; import { AxiosError } from 'axios'; import { CustomerService } from '../queries/keys'; import { useNavigate } from 'react-router-dom'; diff --git a/src/hooks/admin/useAdminTag.ts b/src/hooks/admin/useAdminTag.ts new file mode 100644 index 00000000..746c721e --- /dev/null +++ b/src/hooks/admin/useAdminTag.ts @@ -0,0 +1,61 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { + deletePositionTag, + deleteSkillTag, + postPositionTag, + postSkillTag, +} from '../../api/admin/tag.api'; +import { AxiosError } from 'axios'; +import type { TagFormType } from '../../models/tags'; +import { Tag } from '../queries/keys'; + +export const useAdminSkillTag = () => { + const queryClient = useQueryClient(); + + const postSkillTagMutate = useMutation({ + mutationFn: (formData) => postSkillTag(formData), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: Tag.skillTag, + }); + }, + }); + + const deleteSkillTagMutate = useMutation({ + mutationFn: (id: number) => deleteSkillTag(id), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: Tag.skillTag, + }); + }, + }); + + const postPositionTagMutate = useMutation< + void, + AxiosError, + Pick + >({ + mutationFn: ({ name }) => postPositionTag({ name }), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: Tag.positionTag, + }); + }, + }); + + const deletePositionTagMutate = useMutation({ + mutationFn: (id: number) => deletePositionTag(id), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: Tag.positionTag, + }); + }, + }); + + return { + postSkillTagMutate, + deleteSkillTagMutate, + postPositionTagMutate, + deletePositionTagMutate, + }; +}; diff --git a/src/hooks/admin/useGetAllInquiries.ts b/src/hooks/admin/useGetAllInquiries.ts index 8f1d21b9..5f7fc046 100644 --- a/src/hooks/admin/useGetAllInquiries.ts +++ b/src/hooks/admin/useGetAllInquiries.ts @@ -1,6 +1,6 @@ import { useQuery } from '@tanstack/react-query'; import { Inquiries } from '../queries/keys'; -import { getAllInquiries } from '../../api/admin/customerService/Inquiry.api'; +import { getAllInquiries } from '../../api/admin/customerService/inquiry.api'; export const useGetAllInquiries = () => { const { @@ -10,7 +10,6 @@ export const useGetAllInquiries = () => { } = useQuery({ queryKey: [Inquiries.allInquiries], queryFn: () => getAllInquiries(), - select: (allInquiries) => allInquiries.slice(0, 5), }); return { allInquiriesData, isLoading, isFetching }; diff --git a/src/hooks/queries/keys.ts b/src/hooks/queries/keys.ts index 8caeccc2..e6fecdba 100644 --- a/src/hooks/queries/keys.ts +++ b/src/hooks/queries/keys.ts @@ -69,3 +69,9 @@ export const UserData = { allUserPreview: ['AllUserPreview'], userInfo: ['userInfo'], }; + +export const Tag = { + skillTag: ['skillTagsData'], + positionTag: ['positionsData'], + method: ['fetchMethodTag'], +}; diff --git a/src/hooks/user/useSearchFilteringSkillTag.ts b/src/hooks/user/useSearchFilteringSkillTag.ts index c5a87130..5d318c9d 100644 --- a/src/hooks/user/useSearchFilteringSkillTag.ts +++ b/src/hooks/user/useSearchFilteringSkillTag.ts @@ -6,6 +6,7 @@ import { getPositionTag, getSkillTag, } from '../../api/projectSearchFiltering.api'; +import { Tag } from '../queries/keys'; export const useSearchFilteringSkillTag = () => { const [skillTagsData, setSkillTagsData] = useState([]); @@ -15,19 +16,19 @@ export const useSearchFilteringSkillTag = () => { const queries = useQueries({ queries: [ { - queryKey: ['skillTagsData', skillTagsData], + queryKey: Tag.skillTag, queryFn: () => getSkillTag(), staleTime: Infinity, gcTime: Infinity, }, { - queryKey: ['positionsData', positionTagsData], + queryKey: Tag.positionTag, queryFn: () => getPositionTag(), staleTime: Infinity, gcTime: Infinity, }, { - queryKey: ['fetchMethodTag', methodTagsData], + queryKey: Tag.method, queryFn: () => getMethodTag(), staleTime: Infinity, gcTime: Infinity, diff --git a/src/models/tags.ts b/src/models/tags.ts index bb7922b2..9bbaabe0 100644 --- a/src/models/tags.ts +++ b/src/models/tags.ts @@ -30,3 +30,8 @@ export interface ApiPositionTag extends ApiCommonType { export interface ApiMethodTag extends ApiCommonType { data: MethodTag[] | null; } + +export interface TagFormType { + name: string; + img?: File | undefined; +} diff --git a/src/pages/admin/adminTags/AdminTags.tsx b/src/pages/admin/adminTags/AdminTags.tsx deleted file mode 100644 index b8bd4f27..00000000 --- a/src/pages/admin/adminTags/AdminTags.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function AdminTags() { - return
; -} diff --git a/src/pages/admin/adminTags/position/AdminPositionTagsPage.tsx b/src/pages/admin/adminTags/position/AdminPositionTagsPage.tsx new file mode 100644 index 00000000..45b2d834 --- /dev/null +++ b/src/pages/admin/adminTags/position/AdminPositionTagsPage.tsx @@ -0,0 +1,5 @@ +import CommonAdminPage from '../../CommonAdminPage'; + +export default function AdminPositionTagsPage() { + return ; +} diff --git a/src/pages/admin/adminTags/skill/AdminSkillTagsPage.tsx b/src/pages/admin/adminTags/skill/AdminSkillTagsPage.tsx new file mode 100644 index 00000000..67a6d4a9 --- /dev/null +++ b/src/pages/admin/adminTags/skill/AdminSkillTagsPage.tsx @@ -0,0 +1,5 @@ +import CommonAdminPage from '../../CommonAdminPage'; + +export default function AdminSkillTagsPage() { + return ; +} diff --git a/src/routes/AdminRoutes.tsx b/src/routes/AdminRoutes.tsx index f60a7d1c..675ca8bd 100644 --- a/src/routes/AdminRoutes.tsx +++ b/src/routes/AdminRoutes.tsx @@ -2,12 +2,6 @@ import NotFoundPage from '../pages/notFoundPage/NotFoundPage'; import { lazy, Suspense } from 'react'; import { ADMIN_ROUTE } from '../constants/routes'; import ProtectAdminRoute from './ProtectAdminRoute'; -import AdminUserDetail from '../components/admin/adminUserDetail/AdminUserDetail'; -import UserProjects from '../components/user/userPage/userProjectList/UserProjectList'; -import Profile from '../components/user/mypage/myProfile/profile/Profile'; -import { Navigate } from 'react-router-dom'; -import ActivityLog from '../components/user/mypage/activityLog/ActivityLog'; -import Notifications from '../components/user/mypage/notifications/Notifications'; import { Spinner } from '../components/common/loadingSpinner/LoadingSpinner.styled'; const Sidebar = lazy( @@ -33,7 +27,18 @@ const FAQWrite = lazy( () => import('../pages/admin/adminFAQ/adminFAQWrite/AdminFAQWritePage') ); const Banner = lazy(() => import('../pages/admin/adminBanner/AdminBanner')); -const Tags = lazy(() => import('../pages/admin/adminTags/AdminTags')); +const SkillTagPage = lazy( + () => import('../pages/admin/adminTags/skill/AdminSkillTagsPage') +); +const SkillTags = lazy( + () => import('../components/admin/adminTags/skills/AdminSkillTags') +); +const PositionTagPage = lazy( + () => import('../pages/admin/adminTags/position/AdminPositionTagsPage') +); +const PositionTags = lazy( + () => import('../components/admin/adminTags/positions/AdminPositionTags') +); const AdminUser = lazy(() => import('../pages/admin/adminUser/AdminUser')); const Reports = lazy(() => import('../pages/admin/adminReports/AdminReports')); const Inquiries = lazy( @@ -114,42 +119,18 @@ export const AdminRoutes = () => { element: , }, { - path: ADMIN_ROUTE.tags, - element: , + path: ADMIN_ROUTE.skillTags, + element: , + children: [{ index: true, element: }], }, { - path: ADMIN_ROUTE.users, - element: , + path: ADMIN_ROUTE.positionTags, + element: , + children: [{ index: true, element: }], }, { - path: `${ADMIN_ROUTE.users}/:userId`, - element: , - children: [ - { - index: true, - element: , - }, - { - path: `${ADMIN_ROUTE.basic}`, - element: , - }, - { - path: `${ADMIN_ROUTE.log}`, - element: , - }, - { - path: `${ADMIN_ROUTE.appliedProject}`, - element: , - }, - { - path: `${ADMIN_ROUTE.joinedProject}`, - element: , - }, - { - path: `${ADMIN_ROUTE.createdProject}`, - element: , - }, - ], + path: ADMIN_ROUTE.users, + element: , }, { path: ADMIN_ROUTE.reports, From f5474a9c943e9dab058142781d133b51f2231be5 Mon Sep 17 00:00:00 2001 From: YouD0313 <102004480+YouD0313@users.noreply.github.com> Date: Mon, 16 Jun 2025 17:43:47 +0900 Subject: [PATCH 2/7] =?UTF-8?q?feat:=20=EC=8A=A4=ED=82=AC=ED=83=9C?= =?UTF-8?q?=EA=B7=B8=EA=B4=80=EB=A6=AC=20=EC=84=A0=ED=83=9D=EC=8B=9C=20css?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/admin/adminTags/AdminTagCRUD.tsx | 2 -- .../admin/adminTags/AdminTagsBasic.styled.ts | 1 + src/components/admin/adminTags/AdminTagsBasic.tsx | 1 + .../admin/adminTags/skills/AdminSkillTagItems.tsx | 12 ++++++++++-- src/components/common/skillTagBox/SkillTagBox.tsx | 5 +++-- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/components/admin/adminTags/AdminTagCRUD.tsx b/src/components/admin/adminTags/AdminTagCRUD.tsx index cc6e03b9..06d12e3f 100644 --- a/src/components/admin/adminTags/AdminTagCRUD.tsx +++ b/src/components/admin/adminTags/AdminTagCRUD.tsx @@ -138,8 +138,6 @@ export default function AdminTagCRUD({ e: React.MouseEvent ) => { const id = e.currentTarget.id as SubmitButtonType; - console.log('커런트 타겟: ', id, 'itemId: ', itemId); - setButtonType(id); }; diff --git a/src/components/admin/adminTags/AdminTagsBasic.styled.ts b/src/components/admin/adminTags/AdminTagsBasic.styled.ts index c6810198..ac761104 100644 --- a/src/components/admin/adminTags/AdminTagsBasic.styled.ts +++ b/src/components/admin/adminTags/AdminTagsBasic.styled.ts @@ -5,6 +5,7 @@ export const Container = styled.main` display: flex; gap: 3rem; flex-direction: column; + align-items: center; `; export const CRUDContainer = styled.section` diff --git a/src/components/admin/adminTags/AdminTagsBasic.tsx b/src/components/admin/adminTags/AdminTagsBasic.tsx index ef0c3987..829ea55d 100644 --- a/src/components/admin/adminTags/AdminTagsBasic.tsx +++ b/src/components/admin/adminTags/AdminTagsBasic.tsx @@ -43,6 +43,7 @@ export default function AdminTagsBasic() { const handleGetItemId = (id: number | null) => { setItemId(id); + return id === null ? [] : [id]; }; return ( diff --git a/src/components/admin/adminTags/skills/AdminSkillTagItems.tsx b/src/components/admin/adminTags/skills/AdminSkillTagItems.tsx index 7ffce171..3a27d928 100644 --- a/src/components/admin/adminTags/skills/AdminSkillTagItems.tsx +++ b/src/components/admin/adminTags/skills/AdminSkillTagItems.tsx @@ -1,13 +1,20 @@ import * as S from './AdminSkillTagItems.styled'; import SkillTagBox from '../../../common/skillTagBox/SkillTagBox'; +import { useEffect, useState } from 'react'; interface AdminSKillTagItemsProps { - onGetItemId: (id: number) => void; + onGetItemId: (id: number) => number[]; } export default function AdminSkillTagItems({ onGetItemId, }: AdminSKillTagItemsProps) { + const [selectTagId, setSelectTagId] = useState(); + + useEffect(() => { + console.log(selectTagId); + }, [selectTagId]); + const handleClickGetId = (e: React.MouseEvent) => { e.stopPropagation(); const target = e.target as HTMLElement; @@ -18,11 +25,12 @@ export default function AdminSkillTagItems({ if (!id) return; onGetItemId(id); + setSelectTagId(onGetItemId(id)); }; return ( - + ); } diff --git a/src/components/common/skillTagBox/SkillTagBox.tsx b/src/components/common/skillTagBox/SkillTagBox.tsx index 1c2ac766..bb100fab 100644 --- a/src/components/common/skillTagBox/SkillTagBox.tsx +++ b/src/components/common/skillTagBox/SkillTagBox.tsx @@ -36,11 +36,12 @@ export default function SkillTagBox({ skillTagData={skillTagData} key={`skillTagBox-${skillTagData.id}`} $select={ - (isMain && + selectedTag?.includes(skillTagData.id) || + ((isMain && searchFiltersSkillTag?.includes(skillTagData.id)) || (isCreate && selectedTag?.includes(skillTagData.id)) ? true - : false + : false) } /> ))} From 283201fe4e216531eadcf4f9e1e08ebd5d608e90 Mon Sep 17 00:00:00 2001 From: YouD0313 <102004480+YouD0313@users.noreply.github.com> Date: Tue, 17 Jun 2025 20:57:17 +0900 Subject: [PATCH 3/7] =?UTF-8?q?feat:=20=EC=8A=A4=ED=82=AC=20=EB=B0=8F=20?= =?UTF-8?q?=ED=8F=AC=EC=A7=80=EC=85=98=20=ED=83=9C=EA=B7=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/admin/tag.api.ts | 32 +++++++++++++++++ .../admin/adminTags/AdminTagCRUD.styled.ts | 4 +++ .../admin/adminTags/AdminTagCRUD.tsx | 31 ++++++++++++++-- .../admin/adminTags/AdminTagsBasic.styled.ts | 1 + .../admin/adminTags/AdminTagsBasic.tsx | 23 ++++++++++-- .../positions/AdminPositionItems.styled.ts | 7 +++- .../positions/AdminPositionItems.tsx | 17 ++++++++- .../adminTags/skills/AdminSkillTagItems.tsx | 12 ++----- src/hooks/admin/useAdminTag.ts | 36 +++++++++++++++++++ 9 files changed, 146 insertions(+), 17 deletions(-) diff --git a/src/api/admin/tag.api.ts b/src/api/admin/tag.api.ts index 74d96c5f..ea2343e0 100644 --- a/src/api/admin/tag.api.ts +++ b/src/api/admin/tag.api.ts @@ -11,6 +11,21 @@ export const postSkillTag = async (formData: FormData) => { } }; +export const putSkillTag = async ({ + formData, + id, +}: { + formData: FormData; + id: number; +}) => { + try { + await httpClient.put(`/skill-tag/${id}`, formData); + } catch (e) { + console.error(e); + throw e; + } +}; + export const deleteSkillTag = async (id: number) => { try { await httpClient.delete(`/skill-tag/${id}`); @@ -21,6 +36,8 @@ export const deleteSkillTag = async (id: number) => { }; export const postPositionTag = async ({ name }: Pick) => { + console.log(name); + try { await httpClient.post(`/position-tag`, { name }); } catch (e) { @@ -29,6 +46,21 @@ export const postPositionTag = async ({ name }: Pick) => { } }; +export const putPositionTag = async ({ + name, + id, +}: { + name: Pick; + id: number; +}) => { + try { + await httpClient.put(`/position-tag/${id}`, { name }); + } catch (e) { + console.error(e); + throw e; + } +}; + export const deletePositionTag = async (id: number) => { try { await httpClient.delete(`/position-tag/${id}`); diff --git a/src/components/admin/adminTags/AdminTagCRUD.styled.ts b/src/components/admin/adminTags/AdminTagCRUD.styled.ts index 43305deb..55b660eb 100644 --- a/src/components/admin/adminTags/AdminTagCRUD.styled.ts +++ b/src/components/admin/adminTags/AdminTagCRUD.styled.ts @@ -3,10 +3,12 @@ import { SendButton } from '../../user/customerService/inquiry/Inquiry.styled'; export const CRUDContainer = styled.form` width: 100%; + height: 100%; `; export const CRUDWrapper = styled.div` width: 70%; + height: 100%; display: flex; gap: 1rem; font-size: 1.2rem; @@ -15,9 +17,11 @@ export const CRUDWrapper = styled.div` export const InfoContainer = styled.div` display: flex; + height: 100%; flex-direction: column; gap: 1.5rem; justify-content: center; + /* align-items: center; */ `; export const CRUDButtonWrapper = styled.div` diff --git a/src/components/admin/adminTags/AdminTagCRUD.tsx b/src/components/admin/adminTags/AdminTagCRUD.tsx index 06d12e3f..adfc325d 100644 --- a/src/components/admin/adminTags/AdminTagCRUD.tsx +++ b/src/components/admin/adminTags/AdminTagCRUD.tsx @@ -13,6 +13,7 @@ interface TagState { label: string; needImgFile: boolean; handlePostTag: (params: T) => void; + handlePutTag: ({ params, id }: { params: T; id: number }) => void; handleDeleteTag: (id: number) => void; } @@ -71,7 +72,7 @@ export default function AdminTagCRUD({ if (!isValid.name) { return handleModalOpen(MODAL_MESSAGE.emptyTag); } - if (!isValid.imgName && !itemId) { + if (state.type === 'skill' && !isValid.imgName && !itemId) { return handleModalOpen(MODAL_MESSAGE.emptySkillImg); } @@ -88,9 +89,34 @@ export default function AdminTagCRUD({ if (state.type === 'skill') { state.handlePostTag(formData as T); } else { - state.handlePostTag(name as T); + state.handlePostTag({ name } as T); } } + break; + case '수정': + { + const duplication = + state.type === 'skill' + ? skillTagsData.filter((data) => data.name === name) + : positionTagsData.filter((data) => data.name === name); + if (duplication.length > 0) { + return handleModalOpen(MODAL_MESSAGE.duplicationTag); + } + if (state.type === 'skill') { + console.log('스킬'); + state.handlePutTag({ params: formData, id: itemId } as { + params: T; + id: number; + }); + } else { + console.log('포지션'); + state.handlePutTag({ params: name, id: itemId } as { + params: T; + id: number; + }); + } + } + break; case '삭제': if (itemId) { @@ -101,7 +127,6 @@ export default function AdminTagCRUD({ break; } handleClickReset(); - onGetItemId(null); }; const handleChangeValue = (e: React.ChangeEvent) => { diff --git a/src/components/admin/adminTags/AdminTagsBasic.styled.ts b/src/components/admin/adminTags/AdminTagsBasic.styled.ts index ac761104..dc5b271b 100644 --- a/src/components/admin/adminTags/AdminTagsBasic.styled.ts +++ b/src/components/admin/adminTags/AdminTagsBasic.styled.ts @@ -10,6 +10,7 @@ export const Container = styled.main` export const CRUDContainer = styled.section` width: 90%; + min-height: 11rem; border: 1px solid ${({ theme }) => theme.color.placeholder}; border-radius: ${({ theme }) => theme.borderRadius.large}; display: flex; diff --git a/src/components/admin/adminTags/AdminTagsBasic.tsx b/src/components/admin/adminTags/AdminTagsBasic.tsx index 829ea55d..e4746573 100644 --- a/src/components/admin/adminTags/AdminTagsBasic.tsx +++ b/src/components/admin/adminTags/AdminTagsBasic.tsx @@ -16,11 +16,14 @@ export default function AdminTagsBasic() { const { postSkillTagMutate, + putSkillTagMutate, deleteSkillTagMutate, postPositionTagMutate, + putPositionTagMutate, deletePositionTagMutate, } = useAdminSkillTag(); const [itemId, setItemId] = useState(null); + const selectTagId = itemId ? [itemId] : []; const tagState = { skill: { @@ -29,6 +32,8 @@ export default function AdminTagsBasic() { needImgFile: true, handlePostTag: (formData: FormData) => postSkillTagMutate.mutate(formData), + handlePutTag: ({ params, id }: { params: FormData; id: number }) => + putSkillTagMutate.mutate({ formData: params, id }), handleDeleteTag: (id: number) => deleteSkillTagMutate.mutate(id), }, position: { @@ -37,13 +42,19 @@ export default function AdminTagsBasic() { needImgFile: false, handlePostTag: (name: Pick) => postPositionTagMutate.mutate(name), + handlePutTag: ({ + params, + id, + }: { + params: Pick; + id: number; + }) => putPositionTagMutate.mutate({ name: params, id }), handleDeleteTag: (id: number) => deletePositionTagMutate.mutate(id), }, }; const handleGetItemId = (id: number | null) => { setItemId(id); - return id === null ? [] : [id]; }; return ( @@ -64,8 +75,14 @@ export default function AdminTagsBasic() { )} - - + {witchTag === 'skill' ? ( + + ) : ( + + )}
); diff --git a/src/components/admin/adminTags/positions/AdminPositionItems.styled.ts b/src/components/admin/adminTags/positions/AdminPositionItems.styled.ts index 926ed2ed..8bb7c5c6 100644 --- a/src/components/admin/adminTags/positions/AdminPositionItems.styled.ts +++ b/src/components/admin/adminTags/positions/AdminPositionItems.styled.ts @@ -1,3 +1,8 @@ import styled from 'styled-components'; -export const Container = styled.section``; +export const Container = styled.section` + padding: 2rem; + display: flex; + flex-wrap: wrap; + gap: 0.5rem; +`; diff --git a/src/components/admin/adminTags/positions/AdminPositionItems.tsx b/src/components/admin/adminTags/positions/AdminPositionItems.tsx index 4a6bae5e..34c1f65e 100644 --- a/src/components/admin/adminTags/positions/AdminPositionItems.tsx +++ b/src/components/admin/adminTags/positions/AdminPositionItems.tsx @@ -1,3 +1,5 @@ +import { useSearchFilteringSkillTag } from '../../../../hooks/user/useSearchFilteringSkillTag'; +import PositionButton from '../../../common/positionButton/PositionButton'; import * as S from './AdminPositionItems.styled'; interface AdminPositionItemsProps { @@ -7,5 +9,18 @@ interface AdminPositionItemsProps { export default function AdminPositionItems({ onGetItemId, }: AdminPositionItemsProps) { - return ; + const { positionTagsData } = useSearchFilteringSkillTag(); + + return ( + + {positionTagsData.map((list) => ( + onGetItemId(list.id)} + /> + ))} + + ); } diff --git a/src/components/admin/adminTags/skills/AdminSkillTagItems.tsx b/src/components/admin/adminTags/skills/AdminSkillTagItems.tsx index 3a27d928..e3385a79 100644 --- a/src/components/admin/adminTags/skills/AdminSkillTagItems.tsx +++ b/src/components/admin/adminTags/skills/AdminSkillTagItems.tsx @@ -1,20 +1,15 @@ import * as S from './AdminSkillTagItems.styled'; import SkillTagBox from '../../../common/skillTagBox/SkillTagBox'; -import { useEffect, useState } from 'react'; interface AdminSKillTagItemsProps { - onGetItemId: (id: number) => number[]; + onGetItemId: (id: number) => void; + selectTagId: number[]; } export default function AdminSkillTagItems({ onGetItemId, + selectTagId, }: AdminSKillTagItemsProps) { - const [selectTagId, setSelectTagId] = useState(); - - useEffect(() => { - console.log(selectTagId); - }, [selectTagId]); - const handleClickGetId = (e: React.MouseEvent) => { e.stopPropagation(); const target = e.target as HTMLElement; @@ -25,7 +20,6 @@ export default function AdminSkillTagItems({ if (!id) return; onGetItemId(id); - setSelectTagId(onGetItemId(id)); }; return ( diff --git a/src/hooks/admin/useAdminTag.ts b/src/hooks/admin/useAdminTag.ts index 746c721e..9c71cbc7 100644 --- a/src/hooks/admin/useAdminTag.ts +++ b/src/hooks/admin/useAdminTag.ts @@ -4,6 +4,8 @@ import { deleteSkillTag, postPositionTag, postSkillTag, + putPositionTag, + putSkillTag, } from '../../api/admin/tag.api'; import { AxiosError } from 'axios'; import type { TagFormType } from '../../models/tags'; @@ -21,6 +23,22 @@ export const useAdminSkillTag = () => { }, }); + const putSkillTagMutate = useMutation< + void, + AxiosError, + { + formData: FormData; + id: number; + } + >({ + mutationFn: ({ formData, id }) => putSkillTag({ formData, id }), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: Tag.skillTag, + }); + }, + }); + const deleteSkillTagMutate = useMutation({ mutationFn: (id: number) => deleteSkillTag(id), onSuccess: () => { @@ -43,6 +61,22 @@ export const useAdminSkillTag = () => { }, }); + const putPositionTagMutate = useMutation< + void, + AxiosError, + { + name: Pick; + id: number; + } + >({ + mutationFn: ({ name, id }) => putPositionTag({ name, id }), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: Tag.positionTag, + }); + }, + }); + const deletePositionTagMutate = useMutation({ mutationFn: (id: number) => deletePositionTag(id), onSuccess: () => { @@ -54,8 +88,10 @@ export const useAdminSkillTag = () => { return { postSkillTagMutate, + putSkillTagMutate, deleteSkillTagMutate, postPositionTagMutate, + putPositionTagMutate, deletePositionTagMutate, }; }; From 902ce49492570cb7bb6584a29d5f12d3953ac982 Mon Sep 17 00:00:00 2001 From: YouD0313 <102004480+YouD0313@users.noreply.github.com> Date: Tue, 17 Jun 2025 21:00:39 +0900 Subject: [PATCH 4/7] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20console.log=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/admin/tag.api.ts | 2 -- src/components/admin/adminTags/AdminTagCRUD.tsx | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/api/admin/tag.api.ts b/src/api/admin/tag.api.ts index ea2343e0..ec21b0e5 100644 --- a/src/api/admin/tag.api.ts +++ b/src/api/admin/tag.api.ts @@ -36,8 +36,6 @@ export const deleteSkillTag = async (id: number) => { }; export const postPositionTag = async ({ name }: Pick) => { - console.log(name); - try { await httpClient.post(`/position-tag`, { name }); } catch (e) { diff --git a/src/components/admin/adminTags/AdminTagCRUD.tsx b/src/components/admin/adminTags/AdminTagCRUD.tsx index adfc325d..3d8773a9 100644 --- a/src/components/admin/adminTags/AdminTagCRUD.tsx +++ b/src/components/admin/adminTags/AdminTagCRUD.tsx @@ -103,13 +103,11 @@ export default function AdminTagCRUD({ return handleModalOpen(MODAL_MESSAGE.duplicationTag); } if (state.type === 'skill') { - console.log('스킬'); state.handlePutTag({ params: formData, id: itemId } as { params: T; id: number; }); } else { - console.log('포지션'); state.handlePutTag({ params: name, id: itemId } as { params: T; id: number; From 086edccb0088bba5812429e5fe2d78ad21271d35 Mon Sep 17 00:00:00 2001 From: YouD0313 <102004480+YouD0313@users.noreply.github.com> Date: Tue, 17 Jun 2025 22:10:35 +0900 Subject: [PATCH 5/7] =?UTF-8?q?refactor:=20=EB=A6=AC=EB=B7=B0=EB=B0=98?= =?UTF-8?q?=EC=98=81=20-=20=EC=8A=A4=ED=82=AC=20=EB=B0=8F=20=ED=8F=AC?= =?UTF-8?q?=EC=A7=80=EC=85=98=20=ED=83=9C=EA=B7=B8=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=ED=95=84=ED=84=B0=EB=A7=81=20=ED=9B=85=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B4=80=EB=A0=A8=20=EC=BD=94=EB=93=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/admin/tag.api.ts | 4 ++-- src/components/admin/adminTags/AdminTagCRUD.tsx | 16 ++++++++++------ .../admin/adminTags/AdminTagsBasic.tsx | 14 ++++---------- .../adminTags/positions/AdminPositionItems.tsx | 4 ++-- .../common/skillTagBox/SkillTagBox.tsx | 4 ++-- .../skillTag/skillTagImg/SkillTagImg.tsx | 6 +++++- .../filteringContents/FilteringContents.tsx | 4 ++-- .../mypage/myProfile/editProfile/EditProfile.tsx | 4 ++-- .../ProjectInformationInput.tsx | 4 ++-- src/hooks/admin/useAdminTag.ts | 4 ++-- src/hooks/queries/keys.ts | 12 ++++++------ ...ringSkillTag.ts => useSearchFilteringTags.ts} | 2 +- 12 files changed, 40 insertions(+), 38 deletions(-) rename src/hooks/user/{useSearchFilteringSkillTag.ts => useSearchFilteringTags.ts} (96%) diff --git a/src/api/admin/tag.api.ts b/src/api/admin/tag.api.ts index ec21b0e5..7d1d467c 100644 --- a/src/api/admin/tag.api.ts +++ b/src/api/admin/tag.api.ts @@ -35,7 +35,7 @@ export const deleteSkillTag = async (id: number) => { } }; -export const postPositionTag = async ({ name }: Pick) => { +export const postPositionTag = async (name: string) => { try { await httpClient.post(`/position-tag`, { name }); } catch (e) { @@ -48,7 +48,7 @@ export const putPositionTag = async ({ name, id, }: { - name: Pick; + name: string; id: number; }) => { try { diff --git a/src/components/admin/adminTags/AdminTagCRUD.tsx b/src/components/admin/adminTags/AdminTagCRUD.tsx index 3d8773a9..3c378371 100644 --- a/src/components/admin/adminTags/AdminTagCRUD.tsx +++ b/src/components/admin/adminTags/AdminTagCRUD.tsx @@ -5,7 +5,7 @@ import type { PositionTag, SkillTag, TagFormType } from '../../../models/tags'; import Modal from '../../common/modal/Modal'; import { useModal } from '../../../hooks/useModal'; import { MODAL_MESSAGE } from '../../../constants/user/modalMessage'; -import { useSearchFilteringSkillTag } from '../../../hooks/user/useSearchFilteringSkillTag'; +import { useSearchFilteringTags } from '../../../hooks/user/useSearchFilteringTags'; import { XMarkIcon } from '@heroicons/react/24/outline'; interface TagState { @@ -20,7 +20,7 @@ interface TagState { interface AdminTagCRUDProps { state: TagState; itemId: number | null; - onGetItemId: (id: null) => void; + onGetItemId: (id: number | null) => void; } interface FormDataType extends TagFormType { @@ -42,7 +42,7 @@ export default function AdminTagCRUD({ const fileInputRef = useRef(null); const textInputRef = useRef(null); const { isOpen, message, handleModalOpen, handleModalClose } = useModal(); - const { skillTagsData, positionTagsData } = useSearchFilteringSkillTag(); + const { skillTagsData, positionTagsData } = useSearchFilteringTags(); const [buttonType, setButtonType] = useState('등록'); const [formState, setFormState] = useState({ @@ -89,7 +89,7 @@ export default function AdminTagCRUD({ if (state.type === 'skill') { state.handlePostTag(formData as T); } else { - state.handlePostTag({ name } as T); + state.handlePostTag(name as T); } } break; @@ -97,8 +97,12 @@ export default function AdminTagCRUD({ { const duplication = state.type === 'skill' - ? skillTagsData.filter((data) => data.name === name) - : positionTagsData.filter((data) => data.name === name); + ? skillTagsData + .filter((data) => data.id !== itemId) + .filter((data) => data.name === name) + : positionTagsData + .filter((data) => data.id !== itemId) + .filter((data) => data.name === name); if (duplication.length > 0) { return handleModalOpen(MODAL_MESSAGE.duplicationTag); } diff --git a/src/components/admin/adminTags/AdminTagsBasic.tsx b/src/components/admin/adminTags/AdminTagsBasic.tsx index e4746573..671b9f7a 100644 --- a/src/components/admin/adminTags/AdminTagsBasic.tsx +++ b/src/components/admin/adminTags/AdminTagsBasic.tsx @@ -40,15 +40,9 @@ export default function AdminTagsBasic() { type: 'position', label: '포지션', needImgFile: false, - handlePostTag: (name: Pick) => - postPositionTagMutate.mutate(name), - handlePutTag: ({ - params, - id, - }: { - params: Pick; - id: number; - }) => putPositionTagMutate.mutate({ name: params, id }), + handlePostTag: (name: string) => postPositionTagMutate.mutate({ name }), + handlePutTag: ({ params, id }: { params: string; id: number }) => + putPositionTagMutate.mutate({ name: params, id }), handleDeleteTag: (id: number) => deletePositionTagMutate.mutate(id), }, }; @@ -67,7 +61,7 @@ export default function AdminTagsBasic() { onGetItemId={handleGetItemId} /> ) : ( - > + state={tagState.position} itemId={itemId} onGetItemId={handleGetItemId} diff --git a/src/components/admin/adminTags/positions/AdminPositionItems.tsx b/src/components/admin/adminTags/positions/AdminPositionItems.tsx index 34c1f65e..395808d5 100644 --- a/src/components/admin/adminTags/positions/AdminPositionItems.tsx +++ b/src/components/admin/adminTags/positions/AdminPositionItems.tsx @@ -1,4 +1,4 @@ -import { useSearchFilteringSkillTag } from '../../../../hooks/user/useSearchFilteringSkillTag'; +import { useSearchFilteringTags } from '../../../../hooks/user/useSearchFilteringTags'; import PositionButton from '../../../common/positionButton/PositionButton'; import * as S from './AdminPositionItems.styled'; @@ -9,7 +9,7 @@ interface AdminPositionItemsProps { export default function AdminPositionItems({ onGetItemId, }: AdminPositionItemsProps) { - const { positionTagsData } = useSearchFilteringSkillTag(); + const { positionTagsData } = useSearchFilteringTags(); return ( diff --git a/src/components/common/skillTagBox/SkillTagBox.tsx b/src/components/common/skillTagBox/SkillTagBox.tsx index bb100fab..50a58037 100644 --- a/src/components/common/skillTagBox/SkillTagBox.tsx +++ b/src/components/common/skillTagBox/SkillTagBox.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { useSearchFilteringSkillTag } from '../../../hooks/user/useSearchFilteringSkillTag'; +import { useSearchFilteringTags } from '../../../hooks/user/useSearchFilteringTags'; import SkillTag from './skillTag/SkillTag'; import * as S from './SkillTagBox.styled'; import { ArrowUturnLeftIcon } from '@heroicons/react/24/outline'; @@ -20,7 +20,7 @@ export default function SkillTagBox({ isMain = false, isCreate = false, }: SkillTagBoxProps) { - const { skillTagsData } = useSearchFilteringSkillTag(); + const { skillTagsData } = useSearchFilteringTags(); const { searchFilters } = useSaveSearchFiltering(); const searchFiltersSkillTag = searchFilters.skillTag; diff --git a/src/components/common/skillTagBox/skillTag/skillTagImg/SkillTagImg.tsx b/src/components/common/skillTagBox/skillTag/skillTagImg/SkillTagImg.tsx index 924444a3..610d77ac 100644 --- a/src/components/common/skillTagBox/skillTag/skillTagImg/SkillTagImg.tsx +++ b/src/components/common/skillTagBox/skillTag/skillTagImg/SkillTagImg.tsx @@ -14,7 +14,11 @@ export default function SkillTagImg({ }: SkillTagImgProps) { return ( - + ); } diff --git a/src/components/user/home/searchFiltering/filteringContents/FilteringContents.tsx b/src/components/user/home/searchFiltering/filteringContents/FilteringContents.tsx index 979ffc2c..bd434ad1 100644 --- a/src/components/user/home/searchFiltering/filteringContents/FilteringContents.tsx +++ b/src/components/user/home/searchFiltering/filteringContents/FilteringContents.tsx @@ -3,14 +3,14 @@ import * as S from './FilteringContents.styled'; import beginner from '../../../../../assets/beginner.svg'; import { ChevronDownIcon } from '@heroicons/react/24/outline'; import React, { useState } from 'react'; -import { useSearchFilteringSkillTag } from '../../../../../hooks/user/useSearchFilteringSkillTag'; +import { useSearchFilteringTags } from '../../../../../hooks/user/useSearchFilteringTags'; import { useOutsideClick } from '../../../../../hooks/user/useOutsideClick'; import { useSaveSearchFiltering } from '../../../../../hooks/user/useSaveSearchFiltering'; import { SEARCH_FILTERING_DEFAULT_VALUE } from '../../../../../constants/user/homeConstants'; import SkillTagBox from '../../../../common/skillTagBox/SkillTagBox'; export default function FilteringContents() { - const { positionTagsData, methodTagsData } = useSearchFilteringSkillTag(); + const { positionTagsData, methodTagsData } = useSearchFilteringTags(); const { searchFilters, handleUpdateFilters } = useSaveSearchFiltering(); const [skillTagButtonToggle, setSkillTagButtonToggle] = useState(false); diff --git a/src/components/user/mypage/myProfile/editProfile/EditProfile.tsx b/src/components/user/mypage/myProfile/editProfile/EditProfile.tsx index d89ead01..eb89563b 100644 --- a/src/components/user/mypage/myProfile/editProfile/EditProfile.tsx +++ b/src/components/user/mypage/myProfile/editProfile/EditProfile.tsx @@ -10,7 +10,7 @@ import { SquaresPlusIcon, XMarkIcon } from '@heroicons/react/24/outline'; import { useNavigate, useOutletContext } from 'react-router-dom'; import MyProfileWrapper from '../MyProfileWrapper'; import type { UserInfo } from '../../../../../models/userInfo'; -import { useSearchFilteringSkillTag } from '../../../../../hooks/user/useSearchFilteringSkillTag'; +import { useSearchFilteringTags } from '../../../../../hooks/user/useSearchFilteringTags'; import { useEditMyProfileInfo } from '../../../../../hooks/user/useMyInfo'; import useNickNameVerification from '../../../../../hooks/user/useNicknameVerification'; import { ROUTES } from '../../../../../constants/routes'; @@ -34,7 +34,7 @@ export default function EditProfile() { scrollRef: React.RefObject; handleModalOpen: (message: string) => void; } = useOutletContext(); - const { skillTagsData, positionTagsData } = useSearchFilteringSkillTag(); + const { skillTagsData, positionTagsData } = useSearchFilteringTags(); const { editMyProfile } = useEditMyProfileInfo(handleModalOpen); const { nicknameMessage, handleDuplicationNickname } = useNickNameVerification(); diff --git a/src/components/user/projectFormComponents/projectInformationInput/ProjectInformationInput.tsx b/src/components/user/projectFormComponents/projectInformationInput/ProjectInformationInput.tsx index 6edd443e..b3a89955 100644 --- a/src/components/user/projectFormComponents/projectInformationInput/ProjectInformationInput.tsx +++ b/src/components/user/projectFormComponents/projectInformationInput/ProjectInformationInput.tsx @@ -6,7 +6,7 @@ import * as S from './ProjectInformationInput.styled'; import Input from '../inputComponent/InputComponent'; import type { ProjectDetailPlusExtended } from '../../../../models/projectDetail'; import type { CreateProjectFormValues } from '../../../../models/createProject'; -import { useSearchFilteringSkillTag } from '../../../../hooks/user/useSearchFilteringSkillTag'; +import { useSearchFilteringTags } from '../../../../hooks/user/useSearchFilteringTags'; import { PROJECT_DATA } from '../../../../constants/user/projectConstants'; interface ProjectInformationProps { @@ -22,7 +22,7 @@ const ProjectInformationInput = ({ setValue, apiData, }: ProjectInformationProps) => { - const { positionTagsData, methodTagsData } = useSearchFilteringSkillTag(); + const { positionTagsData, methodTagsData } = useSearchFilteringTags(); return ( <> diff --git a/src/hooks/admin/useAdminTag.ts b/src/hooks/admin/useAdminTag.ts index 9c71cbc7..3fc12ec4 100644 --- a/src/hooks/admin/useAdminTag.ts +++ b/src/hooks/admin/useAdminTag.ts @@ -53,7 +53,7 @@ export const useAdminSkillTag = () => { AxiosError, Pick >({ - mutationFn: ({ name }) => postPositionTag({ name }), + mutationFn: ({ name }) => postPositionTag(name), onSuccess: () => { queryClient.invalidateQueries({ queryKey: Tag.positionTag, @@ -65,7 +65,7 @@ export const useAdminSkillTag = () => { void, AxiosError, { - name: Pick; + name: string; id: number; } >({ diff --git a/src/hooks/queries/keys.ts b/src/hooks/queries/keys.ts index e6fecdba..bb4b97a4 100644 --- a/src/hooks/queries/keys.ts +++ b/src/hooks/queries/keys.ts @@ -47,31 +47,31 @@ export const ProjectMemberListEval = { export const ActivityLog = { myComments: ['MyComments'], myInquiries: ['MyInquiries'], -}; +} as const; export const Inquiries = { allInquiries: ['AllInquiries'], -}; +} as const; export const CustomerService = { faq: 'faq', notice: 'notice', noticeDetail: 'noticeDetail', inquiryDetail: 'inquiryDetail', -}; +} as const; export const ReportData = { allReports: ['AllReports'], -}; +} as const; export const UserData = { allUser: ['AllUser'], allUserPreview: ['AllUserPreview'], userInfo: ['userInfo'], -}; +} as const; export const Tag = { skillTag: ['skillTagsData'], positionTag: ['positionsData'], method: ['fetchMethodTag'], -}; +} as const; diff --git a/src/hooks/user/useSearchFilteringSkillTag.ts b/src/hooks/user/useSearchFilteringTags.ts similarity index 96% rename from src/hooks/user/useSearchFilteringSkillTag.ts rename to src/hooks/user/useSearchFilteringTags.ts index 5d318c9d..2dcbe5b4 100644 --- a/src/hooks/user/useSearchFilteringSkillTag.ts +++ b/src/hooks/user/useSearchFilteringTags.ts @@ -8,7 +8,7 @@ import { } from '../../api/projectSearchFiltering.api'; import { Tag } from '../queries/keys'; -export const useSearchFilteringSkillTag = () => { +export const useSearchFilteringTags = () => { const [skillTagsData, setSkillTagsData] = useState([]); const [positionTagsData, setPositionTagsData] = useState([]); const [methodTagsData, setMethodTagsData] = useState([]); From 7d3d928f0f2e31ed6b07f2cee4adced439e506ef Mon Sep 17 00:00:00 2001 From: YouD0313 <102004480+YouD0313@users.noreply.github.com> Date: Thu, 19 Jun 2025 00:16:49 +0900 Subject: [PATCH 6/7] =?UTF-8?q?refactor:=20=ED=83=9C=EA=B7=B8=20=EB=AA=A8?= =?UTF-8?q?=EB=8D=B8=EC=97=90=EC=84=9C=20createdAt=EC=9D=84=20updatedAt?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/admin/adminTags/AdminTagCRUD.tsx | 12 ++++-------- .../common/skillTagBox/skillTag/SkillTag.tsx | 1 + .../skillTagBox/skillTag/skillTagImg/SkillTagImg.tsx | 4 +++- src/models/tags.ts | 2 +- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/components/admin/adminTags/AdminTagCRUD.tsx b/src/components/admin/adminTags/AdminTagCRUD.tsx index 3c378371..03dac0cc 100644 --- a/src/components/admin/adminTags/AdminTagCRUD.tsx +++ b/src/components/admin/adminTags/AdminTagCRUD.tsx @@ -24,7 +24,6 @@ interface AdminTagCRUDProps { } interface FormDataType extends TagFormType { - imgName: string; preview: string; } @@ -47,7 +46,6 @@ export default function AdminTagCRUD({ const [buttonType, setButtonType] = useState('등록'); const [formState, setFormState] = useState({ name: '', - imgName: '', preview: '', img: undefined, }); @@ -66,13 +64,13 @@ export default function AdminTagCRUD({ const isValid = { name: formState.name.trim() !== '', - imgName: formState.imgName.trim() !== '', + preview: formState.preview.trim() !== '', }; if (!isValid.name) { return handleModalOpen(MODAL_MESSAGE.emptyTag); } - if (state.type === 'skill' && !isValid.imgName && !itemId) { + if (state.type === 'skill' && !isValid.preview && !itemId) { return handleModalOpen(MODAL_MESSAGE.emptySkillImg); } @@ -106,6 +104,7 @@ export default function AdminTagCRUD({ if (duplication.length > 0) { return handleModalOpen(MODAL_MESSAGE.duplicationTag); } + if (!itemId) return; if (state.type === 'skill') { state.handlePutTag({ params: formData, id: itemId } as { params: T; @@ -138,15 +137,13 @@ export default function AdminTagCRUD({ const handleChangeFile = (e: React.ChangeEvent) => { const img = e.target.files?.[0]; - const imgName = img?.name || ''; const preview = img ? URL.createObjectURL(img) : ''; - setFormState((prev) => ({ ...prev, imgName, preview, img })); + setFormState((prev) => ({ ...prev, preview, img })); }; const handleClickReset = () => { setFormState({ name: '', - imgName: '', preview: '', img: undefined, }); @@ -220,7 +217,6 @@ export default function AdminTagCRUD({ 파일찾기 - {formState.imgName} {skillTagData.name} diff --git a/src/components/common/skillTagBox/skillTag/skillTagImg/SkillTagImg.tsx b/src/components/common/skillTagBox/skillTag/skillTagImg/SkillTagImg.tsx index 610d77ac..d52e4db9 100644 --- a/src/components/common/skillTagBox/skillTag/skillTagImg/SkillTagImg.tsx +++ b/src/components/common/skillTagBox/skillTag/skillTagImg/SkillTagImg.tsx @@ -3,19 +3,21 @@ import * as S from './SkillTagImg.styled'; export interface SkillTagImgProps { image: string; skillTag: string; + updatedAt: string; $select?: boolean; skillTagId?: number; } export default function SkillTagImg({ image, skillTag, + updatedAt, $select, skillTagId, }: SkillTagImgProps) { return ( diff --git a/src/models/tags.ts b/src/models/tags.ts index 9bbaabe0..5d206b09 100644 --- a/src/models/tags.ts +++ b/src/models/tags.ts @@ -4,7 +4,7 @@ export interface SkillTag { id: number; name: string; img: string; - createdAt: string; + updatedAt: string; } export interface PositionTag { From 8d2477ab63c8f9d945add7e146f42bab1b8929de Mon Sep 17 00:00:00 2001 From: YouD0313 <102004480+YouD0313@users.noreply.github.com> Date: Thu, 19 Jun 2025 00:24:14 +0900 Subject: [PATCH 7/7] =?UTF-8?q?refactor:=20=EC=8A=A4=ED=82=AC=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=EC=97=90=EC=84=9C=20createdAt=EC=9D=84=20updatedAt?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/admin/adminTags/AdminTagCRUD.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/admin/adminTags/AdminTagCRUD.tsx b/src/components/admin/adminTags/AdminTagCRUD.tsx index 03dac0cc..f99c0935 100644 --- a/src/components/admin/adminTags/AdminTagCRUD.tsx +++ b/src/components/admin/adminTags/AdminTagCRUD.tsx @@ -27,7 +27,7 @@ interface FormDataType extends TagFormType { preview: string; } -type Skill = Omit; +type Skill = Omit; type CRUDDataType = Skill | PositionTag;