-
Notifications
You must be signed in to change notification settings - Fork 5
[FE] feat: 채널 생성 구현 #73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
50c5701
[FE] feat: Toggle 컴포넌트 구현 (#63)
zelkovaria de0140c
[FE] feat: 카테고리 생성 모달 UI 구현 (#63)
zelkovaria 92ae760
[FE] feat: 길드 설정 dropdown에 카테고리 생성 모달 연결 (#63)
zelkovaria 0aa54a5
[FE] feat: 카테고리 생성 api 함수 및 type 구현 (#63)
zelkovaria 3164671
[FE] feat: 카테고리 생성 api 연결 (#63)
zelkovaria e6afd25
[FE] refactor: CategoryDataResult type 개선 (#63)
zelkovaria bfdeb53
[FE] feat: 단일 길드의 category list 조회 구현 (#63)
zelkovaria 482a126
[FE] feat: 카테고리별 채널 리스트 조회 구현 (#63)
zelkovaria 0465167
[FE] feat: theme dark 계열 색상 추가 (#63)
zelkovaria 96a33a6
[FE] feat: channel 생성 api 함수 구현 (#63)
zelkovaria 888cb33
[FE] feat: 채널 생성 클릭시 모달 열림 (#63)
zelkovaria 7679dfc
[FE] feat: 채널 생성 모달 UI 구현 (#63)
zelkovaria 4b00e81
[FE] feat: 채널 생성 api 연결 (#63)
zelkovaria 0dd4407
[FE] fix: 채널 생성 활성화 색상 변수 수정 (#63)
zelkovaria 865b70d
[FE] fix: 카테고리, 채널 공개값이 false로만 가던 오류 수정 (#63)
zelkovaria e3aafb7
[FE] fix: 채널, 카테고리 이름이 지워지지 않는 오류 수정 (#63)
zelkovaria 15d27a1
[FE] refactor: dark 색상 추가 및 채널 생성 모달 css 변경 (#63)
zelkovaria 8d56b89
[FE] refactor: CategoryName css 수정 (#63)
zelkovaria b48986b
[FE] refactor: Channels에 cursor:pointer 추가 (#63)
zelkovaria 1d2824a
[FE] refactor: Channels에 indent 추가 (#63)
zelkovaria 6208ae0
[FE] 채널 타입에 따른 icon 추가 (#63)
zelkovaria c61fd77
[FE] fix: 생성버튼 비활성화를 위한 disabled 속성 추가 (#63)
zelkovaria File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| import * as S from './styles'; | ||
|
|
||
| /** | ||
| * | ||
| * @example | ||
| * ```tsx | ||
| * <Toggle isOn={공개여부 상태값} | ||
| * onToggle={() => 해당 상태값 setter함수} | ||
| * /> | ||
| * ``` | ||
| * | ||
| * @param isOn - 토글 스위치 ON/OFF 상태입니다(공개/비공개) | ||
| * @param onToggle - 실행 시 실행될 콜백 함수 | ||
| */ | ||
| interface ToggleProps { | ||
| isOn: boolean; | ||
| onToggle: () => void; | ||
| } | ||
| const Toggle = ({ isOn, onToggle }: ToggleProps) => { | ||
| return ( | ||
| <S.Toggle $isOn={isOn} onClick={onToggle}> | ||
| <S.ToggleCircle | ||
| animate={{ | ||
| x: isOn ? 25 : 0, | ||
| }} | ||
| transition={{ | ||
| type: 'spring', | ||
| stiffness: 500, | ||
| damping: 30, | ||
| }} | ||
| > | ||
| <S.IconWrapper>{isOn ? '✓' : '✕'}</S.IconWrapper> | ||
| </S.ToggleCircle> | ||
| </S.Toggle> | ||
| ); | ||
| }; | ||
|
|
||
| export default Toggle; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| import { motion } from 'framer-motion'; | ||
| import styled from 'styled-components'; | ||
|
|
||
| export const Toggle = styled.div<{ $isOn: boolean }>` | ||
| cursor: pointer; | ||
|
|
||
| display: flex; | ||
| align-items: center; | ||
|
|
||
| width: 5rem; | ||
| height: 2.5rem; | ||
| padding: 0.2rem; | ||
| border-radius: 1.2rem; | ||
|
|
||
| background-color: ${({ theme, $isOn }) => ($isOn ? theme.colors.green : theme.colors.dark[300])}; | ||
| `; | ||
|
|
||
| export const ToggleCircle = styled(motion.div)` | ||
| width: 2rem; | ||
| height: 2rem; | ||
| border-radius: 50%; | ||
| background-color: ${({ theme }) => theme.colors.white}; | ||
| `; | ||
|
|
||
| export const IconWrapper = styled.div` | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
|
|
||
| width: 100%; | ||
| height: 100%; | ||
|
|
||
| color: ${({ theme }) => theme.colors.dark[500]}; | ||
| `; |
49 changes: 49 additions & 0 deletions
49
src/frontend/src/components/guild/CategoriesList/index.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| import { BiHash } from 'react-icons/bi'; | ||
| import { BsFillMicFill } from 'react-icons/bs'; | ||
| import { TbPlus } from 'react-icons/tb'; | ||
|
|
||
| import { useGuildInfoStore } from '@/stores/guildInfo'; | ||
| import useModalStore from '@/stores/modalStore'; | ||
| import { BodyMediumText, BodyRegularText } from '@/styles/Typography'; | ||
| import { CategoryDataResult, ChannelResult } from '@/types/guilds'; | ||
|
|
||
| import CreateChannelModal from '../CreateChannelModal'; | ||
|
|
||
| import * as S from './styles'; | ||
|
|
||
| export interface CategoriesListProps { | ||
| categories?: CategoryDataResult[]; | ||
| channels?: ChannelResult[]; | ||
| } | ||
| const CategoriesList = ({ categories, channels }: CategoriesListProps) => { | ||
| const { openModal } = useModalStore(); | ||
| const { guildId } = useGuildInfoStore(); | ||
| const handleOpenModal = (categoryId: string, guildId: string) => { | ||
| openModal('withFooter', <CreateChannelModal categoryId={categoryId} guildId={guildId} />); | ||
| }; | ||
|
|
||
| return ( | ||
| <S.CategoriesList> | ||
| {categories?.map((category) => ( | ||
| <S.Category key={category.categoryId}> | ||
| <S.CategoryName> | ||
| <BodyMediumText>{category.name}</BodyMediumText> | ||
| <TbPlus size={18} onClick={() => handleOpenModal(category.categoryId, guildId)} /> | ||
| </S.CategoryName> | ||
| <S.Channels> | ||
| {channels | ||
| ?.filter((channel) => category.categoryId === channel.categoryId) | ||
| .map((channel) => ( | ||
| <S.ChannelName key={channel.channelId}> | ||
| {channel.channelType === 'TEXT' ? <BiHash size={18} /> : <BsFillMicFill size={18} />} | ||
| <BodyRegularText>{channel.name}</BodyRegularText> | ||
| </S.ChannelName> | ||
| ))} | ||
| </S.Channels> | ||
| </S.Category> | ||
| ))} | ||
| </S.CategoriesList> | ||
| ); | ||
| }; | ||
|
|
||
| export default CategoriesList; |
47 changes: 47 additions & 0 deletions
47
src/frontend/src/components/guild/CategoriesList/styles.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| import styled from 'styled-components'; | ||
|
|
||
| export const CategoriesList = styled.div` | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 2rem; | ||
|
|
||
| margin-top: 1.5rem; | ||
|
|
||
| color: ${({ theme }) => theme.colors.dark[300]}; | ||
| `; | ||
|
|
||
| export const Category = styled.div` | ||
| display: flex; | ||
| flex-direction: column; | ||
| `; | ||
|
|
||
| export const CategoryName = styled.div` | ||
| cursor: pointer; | ||
|
|
||
| display: flex; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
|
|
||
| width: 100%; | ||
|
|
||
| svg { | ||
| color: ${({ theme }) => theme.colors.dark[300]}; | ||
| } | ||
| `; | ||
|
|
||
| export const Channels = styled.div` | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 0.6rem; | ||
| margin-top: 0.6rem; | ||
| `; | ||
|
|
||
| export const ChannelName = styled.div` | ||
| cursor: pointer; | ||
|
|
||
| display: flex; | ||
| gap: 0.5rem; | ||
| align-items: center; | ||
|
|
||
| padding-left: 1rem; | ||
| `; |
111 changes: 111 additions & 0 deletions
111
src/frontend/src/components/guild/CreateChannelModal/index.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| import { useQueryClient } from '@tanstack/react-query'; | ||
| import { useState } from 'react'; | ||
| import { BiHash, BiSolidLock } from 'react-icons/bi'; | ||
| import { BsFillMicFill } from 'react-icons/bs'; | ||
|
|
||
| import { createGuildChannel } from '@/api/guild'; | ||
| import Modal from '@/components/common/Modal'; | ||
| import Toggle from '@/components/common/Toggle'; | ||
| import useModalStore from '@/stores/modalStore'; | ||
| import { BodyMediumText, ChipText, SmallText, TitleText2 } from '@/styles/Typography'; | ||
| import { ChannelType, CreateChannelRequest } from '@/types/guilds'; | ||
|
|
||
| import * as S from './styles'; | ||
|
|
||
| interface CreateChannelModalProps { | ||
| guildId: string; | ||
| categoryId: string; | ||
| } | ||
|
|
||
| const CreateChannelModal = ({ categoryId, guildId }: CreateChannelModalProps) => { | ||
| const { closeAllModal } = useModalStore(); | ||
| const [isPublicChannel, setIsPublicChannel] = useState(false); | ||
| const [channelName, setChannelName] = useState(''); | ||
| const [isSelectedType, setIsSelectedType] = useState<ChannelType>(null); | ||
| const queryClient = useQueryClient(); | ||
|
|
||
| const handleChannelNameChange = (event: React.ChangeEvent<HTMLInputElement>) => { | ||
| setChannelName(event.target.value); | ||
| }; | ||
|
|
||
| const handleChangeTextType = () => { | ||
| setIsSelectedType('TEXT'); | ||
| }; | ||
|
|
||
| const handleChangeVoiceType = () => { | ||
| setIsSelectedType('VOICE'); | ||
| }; | ||
|
|
||
| const handleSubmit = async () => { | ||
| try { | ||
| const requestData: CreateChannelRequest = { | ||
| name: channelName, | ||
| guildId, | ||
| categoryId, | ||
| channelType: isSelectedType, | ||
| private: isPublicChannel, | ||
| }; | ||
|
|
||
| await createGuildChannel(requestData); | ||
|
|
||
| await queryClient.invalidateQueries({ queryKey: ['guildInfo', guildId] }); | ||
|
|
||
| closeAllModal(); | ||
| } catch (error) { | ||
| console.error('카테고리 생성 중 오류가 발생했어요', error); | ||
| } | ||
| }; | ||
|
|
||
| return ( | ||
| <Modal name="withFooter"> | ||
| <Modal.Header> | ||
| <TitleText2>채널 만들기</TitleText2> | ||
| </Modal.Header> | ||
| <Modal.Content> | ||
| <S.ChannelType> | ||
| <ChipText>채널 유형</ChipText> | ||
| <S.ChannelTypeContent $isSelectedType={isSelectedType === 'TEXT'} onClick={handleChangeTextType}> | ||
| <BiHash size={24} /> | ||
| <S.TypeInfo> | ||
| <BodyMediumText>텍스트</BodyMediumText> | ||
| <SmallText>메시지, 이미지, GIF, 의견, 농담을 전송하세요</SmallText> | ||
| </S.TypeInfo> | ||
| </S.ChannelTypeContent> | ||
| <S.ChannelTypeContent $isSelectedType={isSelectedType === 'VOICE'} onClick={handleChangeVoiceType}> | ||
| <BsFillMicFill size={24} /> | ||
| <S.TypeInfo> | ||
| <BodyMediumText>음성</BodyMediumText> | ||
| <SmallText>음성, 영상, 화면 공유로 함께 어울리세요</SmallText> | ||
| </S.TypeInfo> | ||
| </S.ChannelTypeContent> | ||
| </S.ChannelType> | ||
| <ChipText>채널 이름</ChipText> | ||
| <S.ChannelNameInput | ||
| onChange={handleChannelNameChange} | ||
| value={channelName} | ||
| placeholder="채널 이름을 입력해주세요" | ||
| /> | ||
| <S.PrivateSetting> | ||
| <S.SettingText> | ||
| <BiSolidLock size={24} /> | ||
| <BodyMediumText>비공개 채널</BodyMediumText> | ||
| </S.SettingText> | ||
| <Toggle isOn={isPublicChannel} onToggle={() => setIsPublicChannel((prev) => !prev)} /> | ||
| </S.PrivateSetting> | ||
| <S.ChannelInfoText>선택한 멤버들과 역할만 이 채널을 볼 수 있어요</S.ChannelInfoText> | ||
| </Modal.Content> | ||
| <S.FooterContainer> | ||
| <S.CancelButton onClick={closeAllModal}>취소</S.CancelButton> | ||
| <S.CreateButton | ||
| disabled={!channelName.trim()} | ||
| $disabled={!isSelectedType || !channelName.trim()} | ||
| onClick={handleSubmit} | ||
| > | ||
| 채널 만들기 | ||
| </S.CreateButton> | ||
| </S.FooterContainer> | ||
| </Modal> | ||
| ); | ||
| }; | ||
|
|
||
| export default CreateChannelModal; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
애니메이션 동작까지 좋아요 👍
컴포넌트 이름을 ToggleButton으로 두는 것은 어떤가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Toggle,ToggleSwitch,ToggleButton등 다양하게 불리는 것 같은데 전Toggle이 깔끔해보여서 요건 그대로 유지해둘게요! 의견 감사합니당 :)