Skip to content
Original file line number Diff line number Diff line change
@@ -1,7 +1,74 @@
import { style } from '@vanilla-extract/css';
import { style, styleVariants } from '@vanilla-extract/css';
import { vars } from '@/styles/theme.css';
import * as fonts from '@/styles/font.css';

export const bodyRow = style([
fonts.Label,
{
padding: '1rem 0.5rem',
marginBottom: '1rem',
borderBottom: `1px solid ${vars.color.bluegray6}`,

display: 'grid',
gridTemplateColumns: 'repeat(8, 1fr)',
alignItems: 'center',

textAlign: 'center',
},
]);

export const rowItem = style({
gridColumn: 'span 2',

display: 'flex',
flexDirection: 'column',
gap: '0.5rem',
});

export const rowText = style({
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
});

export const buttons = style({
display: 'flex',
justifyContent: 'center',
gap: '0.5rem',

whiteSpace: 'nowrap',
});

const button = style({
padding: '0.5rem 1rem',
borderRadius: '4px',
backgroundColor: vars.color.blue,
color: vars.color.white,

whiteSpace: 'nowrap',

':hover': {
opacity: 0.7,
},
});

export const variantsButton = styleVariants({
default: [button],
disabled: [
button,
{
opacity: 0.7,
cursor: 'default',
},
],
});

export const modal = style({
width: '100%',
height: '100vh',
overflow: 'scroll',
});

export const container = style({
width: '100%',
padding: '12px',
Expand Down Expand Up @@ -39,28 +106,22 @@ export const buttonWrapper = style({
gap: '10px',
});

export const button = style({
padding: '6px 12px',

width: '80px',

display: 'flex',
alignItems: 'center',
justifyContent: 'center',

flexShrink: 0,

borderRadius: '14px',
});

export const exposeToggleButton = style([
export const exposedButton = style([
button,
{
backgroundColor: vars.color.blue,
color: vars.color.white,
},
]);

export const notExposedButton = style([
button,
{
backgroundColor: vars.color.lightgray,
color: vars.color.white,
},
]);

export const editButton = style([
button,
{
Expand Down
87 changes: 87 additions & 0 deletions src/app/admin/topics/_components/AdminTopicBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
'use client';
import { useMutation, useQueryClient } from '@tanstack/react-query';

import BottomSheet from './BottomSheet';

import { RequestedTopicType } from '@/lib/types/requestedTopicType';
import editAdminTopic from '@/app/_api/adminTopics/editAdminTopic';
import formatDate from '@/lib/utils/dateFormat';
import { QUERY_KEYS } from '@/lib/constants/queryKeys';
import useBooleanOutput from '@/hooks/useBooleanOutput';

import * as styles from './AdminTopicBox.css';

interface TopicBoxProps {
topic: RequestedTopicType;
}

function TopicBox({ topic }: TopicBoxProps) {
const queryClient = useQueryClient();
const { isOn: isBottomSheetOpen, handleSetOn, handleSetOff } = useBooleanOutput();

const editTopicMutation = useMutation({
mutationFn: () =>
editAdminTopic({
topicId: topic?.id,
isExposed: !topic.isExposed,
title: topic?.title,
categoryCode: topic?.categoryCode,
}),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: [QUERY_KEYS.getAdminTopics],
});
},
});

const handleClickEditButton = () => {
handleSetOn();
};

const handleToggleExposeButton = () => {
editTopicMutation.mutate();
};

return (
<>
<tr className={styles.bodyRow}>
<td>{formatDate(topic?.createdDate)}</td>
<td>{topic?.categoryKorName}</td>
<td className={styles.rowItem}>
<span className={styles.rowText}>{topic?.title}</span>
<span className={styles.rowText}>{topic?.description}</span>
</td>
<td className={styles.buttons}>
<span className={styles.rowText}>
{topic?.isAnonymous ? `${topic?.ownerNickname}(익명 요청)` : topic?.ownerNickname}
</span>
</td>
<td className={styles.buttons}>
<button className={styles.editButton} onClick={handleClickEditButton}>
수정하기
</button>
</td>
<td>
<select onChange={handleToggleExposeButton} value={topic?.isExposed ? '공개' : '비공개'}>
<option>공개</option>
<option>비공개</option>
</select>
</td>
Comment on lines +64 to +69
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(의견) 혹시 요청 주제 관리 페이지에서 토글로 노출상태를 바로 변경하는 기능을 두신 이유가 무엇인지 궁금합니당 👀
공지의 경우는 생성/수정 필드와 노출 관련 API가 분리되어 있는 상황이었는데, 요청 주제 관리는 수정 바텀시트에서 함께 수정할 수 있을 것 같다는 생각이 들어서요~! 중복 코드도 제거할 수 있고, 사용성 면에서도 불편함이 없을 것 같아서 제안드려봅니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

소현님 이 부분은 와이어프레임을 보고 그대로 만든 건데 바텀시트 안에 들어있어도 좋을 것 같습니다! 다만 이번 PR 말고 다른 PR로 따로 올려서 서영님과 상의를 거친 후 수정해보겠습니다!

</tr>

{isBottomSheetOpen && (
<BottomSheet
onClose={() => {
handleSetOff();
}}
topicTitle={topic?.title}
category={topic?.categoryKorName}
isExposed={topic?.isExposed}
topicId={topic?.id}
/>
)}
</>
);
}

export default TopicBox;
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,26 @@

import * as styles from './BottomSheet.css';
import { MouseEventHandler, useState } from 'react';
import { useMutation, useQuery } from '@tanstack/react-query';
import { useUser } from '@/store/useUser';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { QUERY_KEYS } from '@/lib/constants/queryKeys';
import useOnClickOutside from '@/hooks/useOnClickOutside';
import getCategories from '@/app/_api/category/getCategories';
import editAdminTopic from '@/app/_api/adminTopics/editAdminTopic';

import { CategoryType } from '@/lib/types/categoriesType';
import ArrowDown from '/public/icons/down_chevron.svg';
import useBooleanOutput from '@/hooks/useBooleanOutput';
import Modal from '@/components/Modal/Modal';

interface BottomSheetProps {
onClose: MouseEventHandler<HTMLDivElement>;
onClose: () => void;
topicTitle: string;
category: string;
isExposed: boolean;
topicId: number;
}
// TODO: 컴포넌트 공통화 작업
function BottomSheet({ onClose, topicTitle, category, isExposed }: BottomSheetProps) {
function BottomSheet({ onClose, topicTitle, category, isExposed, topicId }: BottomSheetProps) {
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const { isOn: isModalOn, handleSetOn: openModal, handleSetOff: closeModal } = useBooleanOutput(false);
const queryClient = useQueryClient();

const [title, setTitle] = useState(topicTitle);
const [selectedCategory, setSelectedCategory] = useState<string>(category);
Expand All @@ -35,17 +33,26 @@ function BottomSheet({ onClose, topicTitle, category, isExposed }: BottomSheetPr
queryFn: getCategories,
});

const convertCategoryKorNameToCode = (korName: string) => {
const category = categories?.find((cat) => cat.korName === korName);
return category ? category.code : null; // 찾지 못하면 null 반환
};

const editTopicMutation = useMutation({
// mutationFn: () =>
// editAdminTopic({
// isExposed,
// title,
// categoryCode,
// }),
mutationFn: () =>
editAdminTopic({
topicId,
isExposed,
title,
categoryCode: convertCategoryKorNameToCode(selectedCategory as string) || '',
}),
Comment on lines +36 to +48
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

추후 리팩토링 하실 때 드롭다운을 ul, li태그가 아닌 select 태그를 사용하는 것으로 수정하면 카테고리 선택하고, 뮤테이션에 넣어주는 부분에 대해 코드 가독성면에서 좋을 것 같아요, 아래 코드 참고 부탁드립니당

// 제거
// const convertCategoryKorNameToCode = (korName: string) => {
//  const category = categories?.find((cat) => cat.korName === korName);
//  return category ? category.code : null; // 찾지 못하면 null 반환
//  };

  const editTopicMutation = useMutation({
    mutationFn: () =>
      editAdminTopic({
        ...
        categoryCode: selectedCategory, // 수정
      }),
    ...
    },
  });

return (
   <select ... onChange={(e: ChangeEvent<HTMLSelectElement>) => setSelectedCategory(e.target.value)}>
    {categories?.map((category) => (
      <option ... value={category.code}>{category.korName}</option>))}
  </select>
)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리팩토링 시 꼭 코드 반영하겠습니다. 조언 감사합니다 소현님!

onSuccess: () => {
setTitle('');
setSelectedCategory(selectedCategory);
openModal();
queryClient.invalidateQueries({
queryKey: [QUERY_KEYS.getAdminTopics],
});
onClose();
},
onError: (error) => {
setErrorMessage('요청 중 오류가 발생했습니다. 다시 시도해 주세요. :(');
Expand Down Expand Up @@ -131,24 +138,10 @@ function BottomSheet({ onClose, topicTitle, category, isExposed }: BottomSheetPr
</div>

<button type="submit" className={styles.submitButton} disabled={!title || title.length > 30}>
요청 보내기
수정하기
</button>
</form>
</div>
{isModalOn && (
<Modal handleModalClose={closeModal} size="large">
<div className={styles.modalText}>{`요청 주제 수정이 완료되었어요.`} </div>
<button
className={styles.modalButton}
onClick={() => {
closeModal();
setIsDropdownOpen(false); //실행안됨
}}
>
닫기
</button>
</Modal>
)}
</div>
);
}
Expand Down
61 changes: 61 additions & 0 deletions src/app/admin/topics/page.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { style } from '@vanilla-extract/css';
import { vars } from '@/styles/theme.css';
import * as fonts from '@/styles/font.css';

export const body = style({
width: '100%',
minHeight: '100vh',
padding: '1.5rem',

display: 'flex',
flexDirection: 'column',
rowGap: '12px',
});

export const title = style([
fonts.Subtitle,
{
color: vars.color.black,
},
]);

export const subtitle = style([
fonts.Body,
{
paddingBottom: '15px',
color: vars.color.bluegray8,
},
]);

export const table = style({
maxWidth: '850px',
padding: '1rem',

display: 'flex',
flexDirection: 'column',
gap: '1rem',

backgroundColor: vars.color.white,
borderRadius: '8px',
});

Comment on lines +30 to +41
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 스타일 파일에 사용하지 않는 클래스네임이 많은 것 같아서 제거하면 좋을 것 같습니당!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Nahyun-Kang 나현님, 혹시 해당 리뷰는 반영되었을까요? 👀

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ParkSohyunee 네 소현님! 필요없는 css 코드는 삭제해두었습니다!👀

export const headRow = style([
fonts.BodyRegular,
{
padding: '1rem 0.5rem',

display: 'grid',
gridTemplateColumns: 'repeat(8, 1fr)',
alignItems: 'center',

textAlign: 'center',
},
]);

export const rowItem = style({
gridColumn: 'span 2',

display: 'flex',
flexDirection: 'column',
gap: '0.5rem',
});
Loading