Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { ReactNode } from 'react';
import { ReactNode, useRef } from 'react';

import Dropdown from '@/shared/components/Dropdown/Dropdown';
import FaviconImage from '@/shared/components/FaviconImage/FaviconImage';
import ModalWrapper, { ModalWrapperRef } from '@/shared/components/ModalWrapper/ModalWrapper';
import Spacer from '@/shared/components/Spacer/Spacer';

import { AllowedServiceGroupDetailSiteType } from '@/shared/types/allowedService';

import MinusBtn from '@/shared/assets/svgs/minus_btn.svg?react';
import MeatBallDefaultIcon from '@/shared/assets/svgs/common/ic_meatball_default.svg?react';

import ModalContentsAlert from '@/pages/AllowedServicePage/ModalContentsAlert/ModalContentsAlert';
import { usePostMergeToParentDomain } from '@/shared/apisV2/allowedService/allowedService.mutations';

export interface AllowedServiceGroupDetailContentProps {
children: ReactNode;
Expand Down Expand Up @@ -53,12 +58,35 @@ export const AllowedServiceGroupDetailContentTable = ({

export interface AllowedServiceGroupDetailContentRootTableRowProps extends AllowedServiceGroupDetailSiteType {
onDeleteAllowedSite: () => void;
activeGroupId: number;
}

export const AllowedServiceGroupDetailContentTableRow = ({
onDeleteAllowedSite,
activeGroupId,
...allowedSiteData
}: AllowedServiceGroupDetailContentRootTableRowProps) => {
const domainAllowModalRef = useRef<ModalWrapperRef>(null);
const confirmDeleteModalRef = useRef<ModalWrapperRef>(null);

const handleOpenDomainAllowModal = () => {
domainAllowModalRef.current?.open();
};

const handleCloseDomainAllowModal = () => {
domainAllowModalRef.current?.close();
};

const handleOpenConfirmDeleteModal = () => {
confirmDeleteModalRef.current?.open();
};

const handleCloseConfirmDeleteModal = () => {
confirmDeleteModalRef.current?.close();
};

const allowToMergeParentDomain = usePostMergeToParentDomain();

return (
<div className="flex h-[5rem] items-center border-b-[0.1rem] border-gray-bg-04 px-[1rem]">
<div className="flex w-[24rem] flex-shrink-0 items-center gap-x-[0.5rem] truncate pr-[1rem] text-left text-white body-med-16">
Expand All @@ -77,11 +105,49 @@ export const AllowedServiceGroupDetailContentTableRow = ({
</div>
<div>
<div className="pr-[2.05rem]">
<button type="button" onClick={onDeleteAllowedSite}>
<MinusBtn className="fill-gray-bg-07 hover:fill-error-01 active:fill-error-03" />
</button>
<Dropdown>
<Dropdown.Trigger>
<MeatBallDefaultIcon className="cursor-pointer hover:rounded-full hover:bg-gray-bg-05" />
</Dropdown.Trigger>
<Dropdown.Content className="absolute right-0 top-[2.4rem] w-[16.7rem]">
<Dropdown.Item label="상위 도메인 허용" onClick={handleOpenDomainAllowModal} />
<Dropdown.Item
label="허용 사이트 삭제"
textColor="red"
onClick={() => {
handleOpenConfirmDeleteModal();
}}
/>
</Dropdown.Content>
</Dropdown>
</div>
</div>
<ModalWrapper ref={domainAllowModalRef} backdrop>
{() => (
<ModalContentsAlert.DomainAllowConfirm
siteName={allowedSiteData.siteName}
onConfirm={() => {
allowToMergeParentDomain.mutate({
allowedGroupId: activeGroupId,
siteUrl: allowedSiteData.siteUrl,
});
handleCloseDomainAllowModal();
}}
onCancel={handleCloseDomainAllowModal}
/>
)}
</ModalWrapper>
<ModalWrapper ref={confirmDeleteModalRef} backdrop>
{() => (
<ModalContentsAlert.ConfirmDelete
onClick={() => {
handleCloseConfirmDeleteModal();
onDeleteAllowedSite();
}}
pageName={allowedSiteData.pageName}
/>
)}
</ModalWrapper>
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,29 +63,26 @@ const AllowedServiceListContent = ({ children }: { children: ReactNode }) => {
};

interface AllowedServiceListItemProps extends AllowedServiceGroupType {
index: number;
activeGroupId: number | null;
activeGroupTitleInput: string;
onSelectActiveGroup: (activeGroupId: number | null) => void;
onDeleteAllowedServiceGroup: (groupId: number, isActive: boolean, currentIndex: number) => void;
onDeleteAllowedServiceGroup: (groupId: number) => void;
isEditingTitle: boolean;
}

const AllowedServiceListItem = ({
index,
activeGroupId,
activeGroupTitleInput,
onSelectActiveGroup,
onDeleteAllowedServiceGroup,
isEditingTitle,

...allowedServiceGroupData
}: AllowedServiceListItemProps) => {
const isActive = activeGroupId === allowedServiceGroupData.id;

const handleDeleteAllowedServiceGroup = (e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
onDeleteAllowedServiceGroup(allowedServiceGroupData.id, isActive, index);
onDeleteAllowedServiceGroup(allowedServiceGroupData.id);
};

const handleSelectActiveGroupId = () => {
Expand Down
69 changes: 38 additions & 31 deletions src/pages/AllowedServicePage/AllowedServicePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import TextField from '@/shared/components/TextField/TextField';
import { isUrlValid } from '@/shared/utils/validation';

import { ColorPaletteType } from '@/shared/types/allowedService';
import { GetAllowedServiceListRes } from '@/shared/types/api/allowedService';

import BellIcon from '@/shared/assets/svgs/bell.svg?react';
import FriendSettingIcon from '@/shared/assets/svgs/friend_setting.svg?react';

import ModalContentsAlert from '@/pages/AllowedServicePage/ModalContentsAlert/ModalContentsAlert';
import { allowedServiceKeys } from '@/shared/apisV2/allowedService/allowedService.keys';
import {
useDeleteAllowedService,
Expand Down Expand Up @@ -47,6 +47,7 @@ const AllowedServicePage = () => {
const queryClient = useQueryClient();

const friendsModalRef = useRef<ModalWrapperRef>(null);
const requireTitleModalRef = useRef<ModalWrapperRef>(null);

const handleChangeTitleInput = (e: ChangeEvent<HTMLInputElement>) => {
setTitleInput(e.target.value);
Expand Down Expand Up @@ -150,35 +151,24 @@ const AllowedServicePage = () => {
}
};

const handleDeleteAllowedServiceGroup = (groupId: number, isActive: boolean, currentIndex: number) => {
const handleDeleteAllowedServiceGroup = (groupId: number) => {
deleteAllowedServiceGroup(
{ allowedGroupId: groupId },
{
onSuccess: () => {
queryClient.setQueryData(
allowedServiceKeys.allowedServiceList({ connectType: currentTap }),
(oldData: GetAllowedServiceListRes) => {
if (!oldData) return oldData;
return {
...oldData,
data: oldData.data.filter((group) => group.id !== groupId),
};
},
);

if (isActive) {
if (allowedServiceList && allowedServiceList.data.length > 1) {
setActiveGroupId(allowedServiceList.data[currentIndex + 1].id);
} else {
handleEnableAddingAllowedServiceGroup();
}
}
queryClient.invalidateQueries({
queryKey: allowedServiceKeys.allowedServiceList({ connectType: currentTap }),
});
},
},
);
};

const handleAddAllowedService = (urlInput: string, activeGroupId: number | null) => {
if (!activeGroupId) {
handleOpenRequireTitleModal();
return;
}
if (activeGroupId && !isPending) {
postAddAllowedService(
{
Expand Down Expand Up @@ -216,14 +206,23 @@ const AllowedServicePage = () => {

const handleKeyDownUrlInput = (e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter' && !e.nativeEvent.isComposing) {
e.preventDefault();
handleAddAllowedService(urlInput, activeGroupId);
}
};

// NOTE: 첫 렌더링 시 api를 통해 받은 첫번째 allowed service group id를 activeGroupId로 설정
// 리스트 삭제 후, 현재 active 그룹이 리스트에 없는 경우 첫 번째 그룹으로 설정
useEffect(() => {
if (activeGroupId === null && allowedServiceList && allowedServiceList?.data.length > 0) {
setActiveGroupId(allowedServiceList.data[0].id);
if (allowedServiceList && allowedServiceList.data.length > 0) {
const activeGroupExists = activeGroupId && allowedServiceList.data.some((group) => group.id === activeGroupId);

if (!activeGroupId || !activeGroupExists) {
setActiveGroupId(allowedServiceList.data[0].id);
}
} else if (allowedServiceList && allowedServiceList.data.length === 0 && activeGroupId !== null) {
// 리스트가 비어있고 선택된 그룹이 있으면 입력 모드로 전환
handleEnableAddingAllowedServiceGroup();
}
}, [allowedServiceList]);

Expand All @@ -239,6 +238,14 @@ const AllowedServicePage = () => {
friendsModalRef.current?.open();
};

const handleOpenRequireTitleModal = () => {
requireTitleModalRef.current?.open();
};

const handleCloseRequireTitleModal = () => {
requireTitleModalRef.current?.close();
};

return (
<AutoFixedGrid type="allowedService" className="gap-[3rem] bg-gray-bg-01 px-[3.6rem] py-[4.2rem]">
<div className="absolute right-[4.2rem] top-[5.4rem] z-50 flex gap-[0.8rem]">
Expand All @@ -261,10 +268,9 @@ const AllowedServicePage = () => {
{activeGroupId === null && (
<AllowedServiceList.ItemInput titleInput={titleInput} selectedColor={selectedColor} />
)}
{allowedServiceList?.data.map((allowedServiceGroupData, index) => (
{allowedServiceList?.data.map((allowedServiceGroupData) => (
<AllowedServiceList.Item
key={allowedServiceGroupData.id}
index={index}
activeGroupId={activeGroupId}
activeGroupTitleInput={titleInput}
onSelectActiveGroup={handleSelectActiveGroupId}
Expand Down Expand Up @@ -300,9 +306,6 @@ const AllowedServicePage = () => {
<AllowedServiceGroupDetail.TabButton onClick={() => setCurrentTap('WEB')} isActive={currentTap === 'WEB'}>
웹사이트
</AllowedServiceGroupDetail.TabButton>
<AllowedServiceGroupDetail.TabButton disabled isActive={currentTap === 'DESKTOP'}>
</AllowedServiceGroupDetail.TabButton>
</AllowedServiceGroupDetail.Tabs>

<AllowedServiceGroupDetail.Content>
Expand All @@ -322,15 +325,16 @@ const AllowedServicePage = () => {
사이트 등록하기
</TextField.ConfirmButton>
</TextField>

<AllowedServiceGroupDetail.Table totalLength={allowedServiceGroupDetail?.data.allowedSites.length || 0}>
{allowedServiceGroupDetail &&
activeGroupId &&
allowedServiceGroupDetail.data.allowedSites.map((allowedSiteData, index) => (
<AllowedServiceGroupDetail.TableRow
key={`${index}-${allowedSiteData.id}`}
onDeleteAllowedSite={() =>
handleDeleteAllowedService(allowedSiteData.id, allowedSiteData.siteUrl)
}
activeGroupId={activeGroupId}
onDeleteAllowedSite={() => {
handleDeleteAllowedService(allowedSiteData.id, allowedSiteData.siteUrl);
}}
{...allowedSiteData}
/>
))}
Expand All @@ -352,6 +356,9 @@ const AllowedServicePage = () => {
<ModalWrapper ref={friendsModalRef} backdrop>
{({ isModalOpen }) => <ModalContentsFriends isModalOpen={isModalOpen} />}
</ModalWrapper>
<ModalWrapper ref={requireTitleModalRef} backdrop>
{() => <ModalContentsAlert.RequireTitle onClick={handleCloseRequireTitleModal} />}
</ModalWrapper>
</AutoFixedGrid>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { forwardRef } from 'react';

import ButtonRadius5 from '@/shared/components/ButtonRadius5/ButtonRadius5';

interface RequireTitleProps {
onClick: () => void;
}

const RequireTitle = forwardRef<HTMLDivElement, RequireTitleProps>(({ onClick }, ref) => {
return (
<div
ref={ref}
className="flex w-[47.2rem] flex-col justify-center gap-[3rem] whitespace-pre-line rounded-[8px] bg-gray-bg-04 p-[3rem] text-center text-white subhead-bold-22"
>
<p>
허용서비스 리스트의 이름을
<br />
먼저 입력해주세요.
</p>
<ButtonRadius5.Md color="gray" onClick={onClick} className="h-[4.6rem] text-white outline-none">
확인
</ButtonRadius5.Md>
</div>
);
});

interface ConfirmDeleteProps {
onClick: () => void;
pageName: string;
}

const ConfirmDelete = forwardRef<HTMLDivElement, ConfirmDeleteProps>(({ onClick, pageName }, ref) => {
return (
<div
ref={ref}
className="flex w-[47.2rem] flex-col justify-center gap-[3rem] whitespace-pre-line rounded-[8px] bg-gray-bg-04 p-[3rem] text-center text-white subhead-bold-22"
>
<p>
&apos;
<span className="inline-block max-w-[22rem] overflow-hidden text-ellipsis whitespace-nowrap align-middle">
{pageName}
</span>
&apos; 허용 사이트가
<br />
삭제되었습니다.
</p>
<ButtonRadius5.Md color="gray" onClick={onClick} className="h-[4.6rem] text-white outline-none">
확인
</ButtonRadius5.Md>
</div>
);
});

interface DomainAllowConfirmProps {
onConfirm: () => void;
onCancel: () => void;
siteName?: string;
}

const DomainAllowConfirm = forwardRef<HTMLDivElement, DomainAllowConfirmProps>(
({ onConfirm, onCancel, siteName }, ref) => {
return (
<div
ref={ref}
className="flex w-[47.2rem] flex-col justify-center whitespace-pre-line rounded-[8px] bg-gray-bg-04 p-[3rem] text-center text-white subhead-bold-22"
>
<p className="pb-[1rem] text-center text-white subhead-bold-22">
&apos;
<span className="inline-block max-w-[22rem] overflow-hidden text-ellipsis whitespace-nowrap align-middle">
{siteName}
</span>
&apos;의 <br />
상위 도메인을 허용할까요?
</p>
<p className="pb-[3rem] text-gray-05 subhead-med-18">해당 사이트 이름을 가진 링크들이 하나로 통합돼요.</p>
<div className="flex w-full justify-center gap-[1rem]">
<ButtonRadius5.Md color="main" onClick={onConfirm} className="h-[4.6rem] w-[19.3rem] text-black outline-none">
허용
</ButtonRadius5.Md>
<ButtonRadius5.Md color="gray" onClick={onCancel} className="h-[4.6rem] w-[19.3rem] text-white outline-none">
취소하기
</ButtonRadius5.Md>
</div>
</div>
);
},
);

RequireTitle.displayName = 'RequireTitle';
ConfirmDelete.displayName = 'ConfirmDelete';
DomainAllowConfirm.displayName = 'DomainAllowConfirm';

const ModalContentsAlert = {
RequireTitle,
DomainAllowConfirm,
ConfirmDelete,
};

export default ModalContentsAlert;
Loading