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
16 changes: 16 additions & 0 deletions public/icons/teamficial-keyword.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/app/(main)/project/[id]/_components/InfoItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const InfoItem = ({ label, value, className = '' }: InfoItemProps) => {
href={value}
target="_blank"
rel="noopener noreferrer"
className="body-4 text-primary-900 line-clamp-1 break-all underline"
className="desktop:body-4 body-8 text-primary-900 line-clamp-1 break-all underline"
title={value}
>
{value}
Expand Down
116 changes: 71 additions & 45 deletions src/app/(main)/teampsylog/_components/KeywordBar.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useGetKeyword } from '@/hooks/queries/useKeyword';
import React from 'react';
import React, { useEffect, useState } from 'react';
import KeywordItem from './KeywordItem';
import Image from 'next/image';
import ProfileDropdown from './ProfileDropdown';
import { ResponseProfile } from '@/types/profile';
import { useToast } from '@/contexts/ToastContext';
import KeywordGuideBalloon from './KeywordGuideBalloon';

const KeywordBar = ({
profileId,
Expand All @@ -29,6 +30,7 @@ const KeywordBar = ({
}) => {
const { data } = useGetKeyword({ profileId });
const { addToast } = useToast();
const [showGuide, setShowGuide] = useState(false);

const headKeywords = data?.headKeywords || [];
const desktopDisplayKeywords = [
Expand Down Expand Up @@ -61,45 +63,63 @@ const KeywordBar = ({
addToast({ message: '링크가 복사되었어요' });
};

useEffect(() => {
// 편집 모드이고 아직 슬롯이 선택되지 않았을 때만 가이드 표시
if (isEditMode && selectedSlot === null) {
setShowGuide(true);
} else {
setShowGuide(false);
}
}, [isEditMode, selectedSlot]);

if (!isShareMode) return null;

return (
<>
{/* desktop */}
<div className="desktop:flex hidden items-center justify-between rounded-lg bg-gray-100 px-5 py-4">
{/* 대표키워드 및 프로필 드롭다운 */}
<div className="flex items-center gap-2">
<ProfileDropdown
profiles={profiles}
selectedProfileId={selectedProfileId}
onSelectProfile={onSelectProfile}
/>
{desktopDisplayKeywords.map((keyword, index) => (
<KeywordItem
key={`${keyword}-${index}`}
keyword={keyword}
isEditMode={isEditMode}
isSelected={selectedSlot === index}
isPlaceholder={index >= headKeywords.length}
onClick={() => isEditMode && onSelectSlot(index)}
/>
))}
</div>
{isShareMode && (
<div className="flex gap-4">
<button onClick={onToggleEditMode} className="cursor-pointer">
<Image
src={isEditMode ? '/icons/edit-selected.svg' : '/icons/edit.svg'}
alt="수정하기"
width={28}
height={28}
<div className="desktop:block hidden">
<div className="relative">
{isEditMode && showGuide && (
<KeywordGuideBalloon position="top" onClose={() => setShowGuide(false)} />
)}
<div className="flex items-center justify-between rounded-lg bg-gray-100 px-5 py-4">
{/* 대표키워드 및 프로필 드롭다운 */}
<div className="flex items-center gap-2">
<ProfileDropdown
profiles={profiles}
selectedProfileId={selectedProfileId}
onSelectProfile={onSelectProfile}
/>
</button>
<button onClick={handleShare} className="cursor-pointer">
<Image src="/icons/share.svg" alt="공유하기" width={28} height={28} />
</button>
{desktopDisplayKeywords.map((keyword, index) => (
<KeywordItem
key={`${keyword}-${index}`}
keyword={keyword}
isEditMode={isEditMode}
isSelected={selectedSlot === index}
isPlaceholder={index >= headKeywords.length}
onClick={() => isEditMode && onSelectSlot(index)}
/>
))}
</div>
{isShareMode && (
<div className="flex gap-4">
<button onClick={onToggleEditMode} className="cursor-pointer">
<Image
src={isEditMode ? '/icons/edit-selected.svg' : '/icons/edit.svg'}
alt="수정하기"
width={28}
height={28}
/>
</button>
<button onClick={handleShare} className="cursor-pointer">
<Image src="/icons/share.svg" alt="공유하기" width={28} height={28} />
</button>
</div>
)}
</div>
)}
</div>
</div>

{/* mobile */}
<div className="desktop:hidden z-5 flex flex-col gap-4 py-5">
{/* 수정 버튼 및 프로필 드롭다운 */}
Expand All @@ -123,18 +143,24 @@ const KeywordBar = ({
</button>
</div>
</div>
<div className="flex items-center gap-1">
{mobileDisplayKeywords.map((keyword, index) => (
<KeywordItem
key={`${keyword}-${index}`}
keyword={keyword}
isEditMode={isEditMode}
isSelected={selectedSlot === index}
isPlaceholder={index >= headKeywords.length}
onClick={() => isEditMode && onSelectSlot(index)}
isMobileDevice={true}
/>
))}

<div className="relative">
{isEditMode && showGuide && (
<KeywordGuideBalloon position="bottom" onClose={() => setShowGuide(false)} />
)}
<div className="flex items-center gap-1">
{mobileDisplayKeywords.map((keyword, index) => (
<KeywordItem
key={`${keyword}-${index}`}
keyword={keyword}
isEditMode={isEditMode}
isSelected={selectedSlot === index}
isPlaceholder={index >= headKeywords.length}
onClick={() => isEditMode && onSelectSlot(index)}
isMobileDevice={true}
/>
))}
</div>
</div>
</div>
</>
Expand Down
41 changes: 41 additions & 0 deletions src/app/(main)/teampsylog/_components/KeywordGuideBalloon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react';

interface KeywordGuideBalloonProps {
position: 'top' | 'bottom';
onClose: () => void;
text?: string;
}

const KeywordGuideBalloon: React.FC<KeywordGuideBalloonProps> = ({
position,
onClose,
text = '변경할 대표 키워드를\n먼저 선택하세요',
}) => {
return (
<div
className={`absolute left-1/2 z-50 flex -translate-x-1/2 items-center rounded bg-gray-800 px-4 py-2 text-white shadow-lg ${
position === 'top' ? 'bottom-full mb-1.5' : 'top-full mt-1.5'
}`}
>
<div className="relative flex min-h-6 items-center justify-between gap-2">
<span className="body-10 desktop:body-8 text-start whitespace-pre-line">{text}</span>
<button
onClick={onClose}
className="flex h-4 w-4 flex-shrink-0 items-center justify-center p-0 text-base text-white/70 hover:text-white focus:outline-none"
>
</button>
</div>
{/* 꼬리 */}
<div
className={`absolute left-1/2 h-0 w-0 -translate-x-1/2 border-x-[6px] border-x-transparent ${
position === 'top'
? 'top-full border-t-[9px] border-t-gray-800'
: 'bottom-full border-b-[9px] border-b-gray-800'
}`}
/>
</div>
);
};

export default KeywordGuideBalloon;
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const KeywordGuideOverlay = ({ onClose }: Props) => {
</label>
</div>
{/* mobile */}
<div className="desktop:hidden relative mx-auto flex min-h-screen w-full flex-col">
<div className="desktop:hidden relative mx-auto flex min-h-[100dvh] w-full flex-col">
<div className="mt-[25px] flex justify-center">
<Swiper
spaceBetween={0}
Expand Down
22 changes: 13 additions & 9 deletions src/app/(main)/teampsylog/_components/KeywordItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,19 @@ const KeywordItem = ({
<div
onClick={onClick}
className={`desktop:body-5 body-9 desktop:px-5 desktop:py-3 box-border rounded-lg px-3 py-2 transition-all ${
isEditMode && isSelected && isPlaceholder
? 'border-primary-900 bg-primary-50 text-primary-900 cursor-pointer border-2 border-dashed'
: isEditMode && isSelected
? 'border-primary-900 bg-primary-50 text-primary-900 cursor-pointer border'
: isEditMode && isPlaceholder
? 'cursor-pointer border-2 border-dashed border-gray-400 bg-gray-50 text-gray-600'
: isEditMode
? 'cursor-pointer border border-gray-300 bg-gray-50 text-gray-800'
: `border border-gray-300 bg-gray-50 ${isPlaceholder ? 'text-gray-600' : 'text-gray-800'}`
isEditMode && isSelected
? 'border-primary-900 border border-dashed'
: isEditMode
? 'border border-dashed border-gray-400'
: 'border border-gray-300'
} ${
isEditMode && isSelected
? 'bg-primary-50 text-primary-900 cursor-pointer'
: isEditMode && isPlaceholder
? 'cursor-pointer bg-gray-50 text-gray-600'
: isEditMode
? 'cursor-pointer bg-gray-50 text-gray-800'
: `bg-gray-50 ${isPlaceholder ? 'text-gray-600' : 'text-gray-800'}`
} `}
>
<span
Expand Down
Loading