refactor(control-center): Quick Controls 재디자인 및 플랜/잠금 결제 동선 정리
This commit is contained in:
31
src/widgets/space-tools-dock/model/applyQuickPack.ts
Normal file
31
src/widgets/space-tools-dock/model/applyQuickPack.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
interface ApplyQuickPackParams {
|
||||
packId: 'balanced' | 'deep-work' | 'gentle';
|
||||
onTimerSelect: (timerLabel: string) => void;
|
||||
onSelectPreset: (presetId: string) => void;
|
||||
pushToast: (payload: { title: string; description?: string }) => void;
|
||||
}
|
||||
|
||||
export const applyQuickPack = ({
|
||||
packId,
|
||||
onTimerSelect,
|
||||
onSelectPreset,
|
||||
pushToast,
|
||||
}: ApplyQuickPackParams) => {
|
||||
if (packId === 'balanced') {
|
||||
onTimerSelect('25/5');
|
||||
onSelectPreset('rain-focus');
|
||||
pushToast({ title: 'Balanced 팩을 적용했어요.' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (packId === 'deep-work') {
|
||||
onTimerSelect('50/10');
|
||||
onSelectPreset('deep-white');
|
||||
pushToast({ title: 'Deep Work 팩을 적용했어요.' });
|
||||
return;
|
||||
}
|
||||
|
||||
onTimerSelect('25/5');
|
||||
onSelectPreset('silent');
|
||||
pushToast({ title: 'Gentle 팩을 적용했어요.' });
|
||||
};
|
||||
@@ -1,2 +1,2 @@
|
||||
export type SpaceAnchorPopoverId = 'sound' | 'notes';
|
||||
export type SpaceUtilityPanelId = 'settings' | 'inbox' | 'stats';
|
||||
export type SpaceUtilityPanelId = 'control-center' | 'inbox' | 'manage-plan' | 'paywall';
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useEffect, useMemo, useState, type KeyboardEvent as ReactKeyboardEvent } from 'react';
|
||||
import type { PlanTier } from '@/entities/plan';
|
||||
import type { RoomTheme } from '@/entities/room';
|
||||
import { SOUND_PRESETS, type RecentThought, type TimerPreset } from '@/entities/session';
|
||||
import { ExitHoldButton } from '@/features/exit-hold';
|
||||
import { ManagePlanSheetContent, PaywallSheetContent } from '@/features/paywall-sheet';
|
||||
import { PlanPill } from '@/features/plan-pill';
|
||||
import { useToast } from '@/shared/ui';
|
||||
import { cn } from '@/shared/lib/cn';
|
||||
import { ControlCenterSheetWidget } from '@/widgets/control-center-sheet';
|
||||
import { SpaceSideSheet } from '@/widgets/space-sheet-shell';
|
||||
import type { SpaceAnchorPopoverId, SpaceUtilityPanelId } from '../model/types';
|
||||
import { applyQuickPack } from '../model/applyQuickPack';
|
||||
import { ANCHOR_ICON, formatThoughtCount, RAIL_ICON, UTILITY_PANEL_TITLE } from './constants';
|
||||
import { InboxToolPanel } from './panels/InboxToolPanel';
|
||||
import { SettingsToolPanel } from './panels/SettingsToolPanel';
|
||||
import { StatsToolPanel } from './panels/StatsToolPanel';
|
||||
|
||||
interface SpaceToolsDockWidgetProps {
|
||||
isFocusMode: boolean;
|
||||
rooms: RoomTheme[];
|
||||
@@ -24,61 +26,15 @@ interface SpaceToolsDockWidgetProps {
|
||||
onRoomSelect: (roomId: string) => void;
|
||||
onTimerSelect: (timerLabel: string) => void;
|
||||
onSelectPreset: (presetId: string) => void;
|
||||
onCaptureThought: (note: string) => void;
|
||||
onClearInbox: () => void;
|
||||
onCaptureThought: (note: string) => RecentThought | null;
|
||||
onDeleteThought: (thoughtId: string) => RecentThought | null;
|
||||
onSetThoughtCompleted: (thoughtId: string, isCompleted: boolean) => RecentThought | null;
|
||||
onRestoreThought: (thought: RecentThought) => void;
|
||||
onRestoreThoughts: (thoughts: RecentThought[]) => void;
|
||||
onClearInbox: () => RecentThought[];
|
||||
onExitRequested: () => void;
|
||||
}
|
||||
|
||||
const ANCHOR_ICON = {
|
||||
sound: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className="h-4 w-4"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.8"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M4 13V11a2 2 0 0 1 2-2h2l3-3h2v12h-2l-3-3H6a2 2 0 0 1-2-2Z" />
|
||||
<path d="M16 9a4 4 0 0 1 0 6" />
|
||||
</svg>
|
||||
),
|
||||
notes: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className="h-4 w-4"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.8"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M6 4h9l3 3v13H6z" />
|
||||
<path d="M15 4v4h4" />
|
||||
<path d="M9 12h6M9 16h4" />
|
||||
</svg>
|
||||
),
|
||||
};
|
||||
|
||||
const UTILITY_PANEL_TITLE: Record<SpaceUtilityPanelId, string> = {
|
||||
inbox: '인박스',
|
||||
stats: '집중 요약',
|
||||
settings: '설정',
|
||||
};
|
||||
|
||||
const formatThoughtCount = (count: number) => {
|
||||
if (count < 1) {
|
||||
return '0';
|
||||
}
|
||||
|
||||
if (count > 9) {
|
||||
return '9+';
|
||||
}
|
||||
|
||||
return String(count);
|
||||
};
|
||||
|
||||
export const SpaceToolsDockWidget = ({
|
||||
isFocusMode,
|
||||
rooms,
|
||||
@@ -92,6 +48,10 @@ export const SpaceToolsDockWidget = ({
|
||||
onTimerSelect,
|
||||
onSelectPreset,
|
||||
onCaptureThought,
|
||||
onDeleteThought,
|
||||
onSetThoughtCompleted,
|
||||
onRestoreThought,
|
||||
onRestoreThoughts,
|
||||
onClearInbox,
|
||||
onExitRequested,
|
||||
}: SpaceToolsDockWidgetProps) => {
|
||||
@@ -99,6 +59,7 @@ export const SpaceToolsDockWidget = ({
|
||||
const [openPopover, setOpenPopover] = useState<SpaceAnchorPopoverId | null>(null);
|
||||
const [utilityPanel, setUtilityPanel] = useState<SpaceUtilityPanelId | null>(null);
|
||||
const [noteDraft, setNoteDraft] = useState('');
|
||||
const [plan, setPlan] = useState<PlanTier>('normal');
|
||||
const [isIdle, setIdle] = useState(false);
|
||||
|
||||
const selectedSoundLabel = useMemo(() => {
|
||||
@@ -112,7 +73,7 @@ export const SpaceToolsDockWidget = ({
|
||||
return;
|
||||
}
|
||||
|
||||
const handleEscape = (event: KeyboardEvent) => {
|
||||
const handleEscape = (event: globalThis.KeyboardEvent) => {
|
||||
if (event.key === 'Escape') {
|
||||
setOpenPopover(null);
|
||||
}
|
||||
@@ -176,11 +137,126 @@ export const SpaceToolsDockWidget = ({
|
||||
return;
|
||||
}
|
||||
|
||||
onCaptureThought(trimmedNote);
|
||||
const addedThought = onCaptureThought(trimmedNote);
|
||||
|
||||
if (!addedThought) {
|
||||
return;
|
||||
}
|
||||
|
||||
setNoteDraft('');
|
||||
pushToast({ title: '메모를 잠깐 주차했어요.' });
|
||||
pushToast({
|
||||
title: '인박스에 저장됨',
|
||||
durationMs: 4200,
|
||||
action: {
|
||||
label: '실행취소',
|
||||
onClick: () => {
|
||||
const removed = onDeleteThought(addedThought.id);
|
||||
|
||||
if (!removed) {
|
||||
return;
|
||||
}
|
||||
|
||||
pushToast({ title: '저장 취소됨' });
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleNoteKeyDown = (event: ReactKeyboardEvent<HTMLInputElement>) => {
|
||||
if (event.key !== 'Enter') {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
handleNoteSubmit();
|
||||
};
|
||||
|
||||
const handleInboxComplete = (thought: RecentThought) => {
|
||||
const previousThought = onSetThoughtCompleted(thought.id, !thought.isCompleted);
|
||||
|
||||
if (!previousThought) {
|
||||
return;
|
||||
}
|
||||
|
||||
const willBeCompleted = !thought.isCompleted;
|
||||
|
||||
pushToast({
|
||||
title: willBeCompleted ? '완료 처리됨' : '완료 해제됨',
|
||||
durationMs: 4200,
|
||||
action: {
|
||||
label: '실행취소',
|
||||
onClick: () => {
|
||||
onRestoreThought(previousThought);
|
||||
pushToast({ title: willBeCompleted ? '완료 처리를 취소했어요.' : '완료 해제를 취소했어요.' });
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleInboxDelete = (thought: RecentThought) => {
|
||||
const removedThought = onDeleteThought(thought.id);
|
||||
|
||||
if (!removedThought) {
|
||||
return;
|
||||
}
|
||||
|
||||
pushToast({
|
||||
title: '삭제됨',
|
||||
durationMs: 4200,
|
||||
action: {
|
||||
label: '실행취소',
|
||||
onClick: () => {
|
||||
onRestoreThought(removedThought);
|
||||
pushToast({ title: '삭제를 취소했어요.' });
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleInboxClear = () => {
|
||||
const snapshot = onClearInbox();
|
||||
|
||||
if (snapshot.length === 0) {
|
||||
pushToast({ title: '인박스가 비어 있어요.' });
|
||||
return;
|
||||
}
|
||||
|
||||
pushToast({
|
||||
title: '모두 비워짐',
|
||||
durationMs: 4200,
|
||||
action: {
|
||||
label: '실행취소',
|
||||
onClick: () => {
|
||||
onRestoreThoughts(snapshot);
|
||||
pushToast({ title: '인박스를 복구했어요.' });
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handlePlanPillClick = () => {
|
||||
if (plan === 'pro') {
|
||||
openUtilityPanel('manage-plan');
|
||||
return;
|
||||
}
|
||||
|
||||
openUtilityPanel('paywall');
|
||||
};
|
||||
|
||||
const handleLockedClick = (source: string) => {
|
||||
pushToast({ title: `${source}은(는) PRO 기능이에요.` });
|
||||
openUtilityPanel('paywall');
|
||||
};
|
||||
|
||||
const handleStartPro = () => {
|
||||
setPlan('pro');
|
||||
pushToast({ title: '결제(더미)' });
|
||||
openUtilityPanel('control-center');
|
||||
};
|
||||
|
||||
const handleApplyPack = (packId: 'balanced' | 'deep-work' | 'gentle') =>
|
||||
applyQuickPack({ packId, onTimerSelect, onSelectPreset, pushToast });
|
||||
|
||||
return (
|
||||
<>
|
||||
{openPopover ? (
|
||||
@@ -206,6 +282,41 @@ export const SpaceToolsDockWidget = ({
|
||||
|
||||
{isFocusMode ? (
|
||||
<>
|
||||
<div
|
||||
className={cn(
|
||||
'fixed z-30 transition-opacity right-[calc(env(safe-area-inset-right,0px)+0.75rem)] top-1/2 -translate-y-1/2',
|
||||
isIdle ? 'opacity-34' : 'opacity-78',
|
||||
)}
|
||||
>
|
||||
<div className="rounded-2xl border border-white/14 bg-black/22 p-1.5 backdrop-blur-md">
|
||||
<div className="flex flex-col gap-1">
|
||||
<button
|
||||
type="button"
|
||||
aria-label="인박스 열기"
|
||||
title="인박스"
|
||||
onClick={() => openUtilityPanel('inbox')}
|
||||
className="relative inline-flex h-8 w-8 items-center justify-center rounded-xl border border-white/12 bg-white/[0.03] text-white/82 transition-colors hover:bg-white/10"
|
||||
>
|
||||
{RAIL_ICON.inbox}
|
||||
{thoughtCount > 0 ? (
|
||||
<span className="absolute -right-1 -top-1 inline-flex min-w-[0.95rem] items-center justify-center rounded-full bg-sky-200/28 px-1 py-0.5 text-[8px] font-semibold text-sky-50">
|
||||
{formatThoughtCount(thoughtCount)}
|
||||
</span>
|
||||
) : null}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Quick Controls 열기"
|
||||
title="Quick Controls"
|
||||
onClick={() => openUtilityPanel('control-center')}
|
||||
className="inline-flex h-8 w-8 items-center justify-center rounded-xl border border-white/12 bg-white/[0.03] text-white/82 transition-colors hover:bg-white/10"
|
||||
>
|
||||
{RAIL_ICON.controlCenter}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
'fixed z-30 transition-opacity left-[calc(env(safe-area-inset-left,0px)+0.75rem)] bottom-[calc(env(safe-area-inset-bottom,0px)+5.25rem)] sm:bottom-[calc(env(safe-area-inset-bottom,0px)+0.75rem)]',
|
||||
@@ -237,7 +348,8 @@ export const SpaceToolsDockWidget = ({
|
||||
<input
|
||||
value={noteDraft}
|
||||
onChange={(event) => setNoteDraft(event.target.value)}
|
||||
placeholder="한 줄 메모"
|
||||
onKeyDown={handleNoteKeyDown}
|
||||
placeholder="떠오른 생각을 잠깐 주차…"
|
||||
className="h-8 min-w-0 flex-1 rounded-lg border border-white/14 bg-white/[0.04] px-2.5 text-xs text-white placeholder:text-white/38 focus:border-sky-200/42 focus:outline-none"
|
||||
/>
|
||||
<button
|
||||
@@ -248,44 +360,7 @@ export const SpaceToolsDockWidget = ({
|
||||
저장
|
||||
</button>
|
||||
</div>
|
||||
<ul className="mt-2 space-y-1.5">
|
||||
{thoughts.slice(0, 3).map((thought) => (
|
||||
<li
|
||||
key={thought.id}
|
||||
className="truncate rounded-lg border border-white/10 bg-white/[0.03] px-2 py-1.5 text-[11px] text-white/74"
|
||||
>
|
||||
{thought.text}
|
||||
</li>
|
||||
))}
|
||||
{thoughts.length === 0 ? (
|
||||
<li className="rounded-lg border border-white/10 bg-white/[0.03] px-2 py-1.5 text-[11px] text-white/56">
|
||||
아직 메모가 없어요.
|
||||
</li>
|
||||
) : null}
|
||||
</ul>
|
||||
<div className="mt-2 flex items-center justify-end gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => openUtilityPanel('inbox')}
|
||||
className="text-[11px] text-white/62 transition-colors hover:text-white/88"
|
||||
>
|
||||
인박스
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => openUtilityPanel('stats')}
|
||||
className="text-[11px] text-white/62 transition-colors hover:text-white/88"
|
||||
>
|
||||
통계
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => openUtilityPanel('settings')}
|
||||
className="text-[11px] text-white/62 transition-colors hover:text-white/88"
|
||||
>
|
||||
설정
|
||||
</button>
|
||||
</div>
|
||||
<p className="mt-2 text-[11px] text-white/52">나중에 인박스에서 정리해요.</p>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
@@ -345,10 +420,10 @@ export const SpaceToolsDockWidget = ({
|
||||
<div className="mt-2 flex justify-end">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => openUtilityPanel('settings')}
|
||||
onClick={() => openUtilityPanel('control-center')}
|
||||
className="text-[11px] text-white/62 transition-colors hover:text-white/88"
|
||||
>
|
||||
고급 옵션
|
||||
Control Center
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -361,25 +436,23 @@ export const SpaceToolsDockWidget = ({
|
||||
<SpaceSideSheet
|
||||
open={utilityPanel !== null}
|
||||
title={utilityPanel ? UTILITY_PANEL_TITLE[utilityPanel] : ''}
|
||||
subtitle={utilityPanel === 'control-center' ? '배경 · 타이머 · 사운드를 그 자리에서 바꿔요.' : undefined}
|
||||
headerAction={
|
||||
utilityPanel === 'control-center' ? (
|
||||
<PlanPill plan={plan} onClick={handlePlanPillClick} />
|
||||
) : undefined
|
||||
}
|
||||
onClose={() => setUtilityPanel(null)}
|
||||
>
|
||||
{utilityPanel === 'inbox' ? (
|
||||
<InboxToolPanel
|
||||
thoughts={thoughts}
|
||||
onClear={() => {
|
||||
onClearInbox();
|
||||
pushToast({ title: '인박스를 비웠어요 (더미)' });
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{utilityPanel === 'stats' ? <StatsToolPanel /> : null}
|
||||
{utilityPanel === 'settings' ? (
|
||||
<SettingsToolPanel
|
||||
{utilityPanel === 'control-center' ? (
|
||||
<ControlCenterSheetWidget
|
||||
plan={plan}
|
||||
rooms={rooms}
|
||||
selectedRoomId={selectedRoomId}
|
||||
selectedTimerLabel={selectedTimerLabel}
|
||||
selectedSoundPresetId={selectedPresetId}
|
||||
timerPresets={timerPresets}
|
||||
soundPresets={SOUND_PRESETS}
|
||||
onSelectRoom={(roomId) => {
|
||||
onRoomSelect(roomId);
|
||||
pushToast({ title: '공간을 바꿨어요.' });
|
||||
@@ -388,6 +461,36 @@ export const SpaceToolsDockWidget = ({
|
||||
onTimerSelect(label);
|
||||
pushToast({ title: `타이머를 ${label}로 바꿨어요.` });
|
||||
}}
|
||||
onSelectSound={(presetId) => {
|
||||
onSelectPreset(presetId);
|
||||
pushToast({ title: '사운드를 바꿨어요.' });
|
||||
}}
|
||||
onApplyPack={handleApplyPack}
|
||||
onLockedClick={handleLockedClick}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{utilityPanel === 'inbox' ? (
|
||||
<InboxToolPanel
|
||||
thoughts={thoughts}
|
||||
onCompleteThought={handleInboxComplete}
|
||||
onDeleteThought={handleInboxDelete}
|
||||
onClear={handleInboxClear}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{utilityPanel === 'paywall' ? (
|
||||
<PaywallSheetContent
|
||||
onStartPro={handleStartPro}
|
||||
onClose={() => setUtilityPanel(null)}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{utilityPanel === 'manage-plan' ? (
|
||||
<ManagePlanSheetContent
|
||||
onClose={() => setUtilityPanel(null)}
|
||||
onManage={() => pushToast({ title: '구독 관리(더미)' })}
|
||||
onRestore={() => pushToast({ title: '구매 복원(더미)' })}
|
||||
/>
|
||||
) : null}
|
||||
</SpaceSideSheet>
|
||||
|
||||
85
src/widgets/space-tools-dock/ui/constants.tsx
Normal file
85
src/widgets/space-tools-dock/ui/constants.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import type { SpaceUtilityPanelId } from '../model/types';
|
||||
|
||||
export const ANCHOR_ICON = {
|
||||
sound: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className="h-4 w-4"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.8"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M4 13V11a2 2 0 0 1 2-2h2l3-3h2v12h-2l-3-3H6a2 2 0 0 1-2-2Z" />
|
||||
<path d="M16 9a4 4 0 0 1 0 6" />
|
||||
</svg>
|
||||
),
|
||||
notes: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className="h-4 w-4"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.8"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M6 4h9l3 3v13H6z" />
|
||||
<path d="M15 4v4h4" />
|
||||
<path d="M9 12h6M9 16h4" />
|
||||
</svg>
|
||||
),
|
||||
};
|
||||
|
||||
export const RAIL_ICON = {
|
||||
controlCenter: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className="h-4 w-4"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.8"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M4 7h16M4 12h16M4 17h16" />
|
||||
<circle cx="8" cy="7" r="1.2" fill="currentColor" stroke="none" />
|
||||
<circle cx="16" cy="12" r="1.2" fill="currentColor" stroke="none" />
|
||||
<circle cx="11" cy="17" r="1.2" fill="currentColor" stroke="none" />
|
||||
</svg>
|
||||
),
|
||||
inbox: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className="h-4 w-4"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.8"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<rect x="4" y="6" width="16" height="12" rx="2.5" />
|
||||
<path d="m5 8 7 5 7-5" />
|
||||
</svg>
|
||||
),
|
||||
};
|
||||
|
||||
export const UTILITY_PANEL_TITLE: Record<SpaceUtilityPanelId, string> = {
|
||||
'control-center': 'Quick Controls',
|
||||
inbox: '인박스',
|
||||
paywall: 'PRO',
|
||||
'manage-plan': '플랜 관리',
|
||||
};
|
||||
|
||||
export const formatThoughtCount = (count: number) => {
|
||||
if (count < 1) {
|
||||
return '0';
|
||||
}
|
||||
|
||||
if (count > 9) {
|
||||
return '9+';
|
||||
}
|
||||
|
||||
return String(count);
|
||||
};
|
||||
@@ -1,25 +1,67 @@
|
||||
import { useState } from 'react';
|
||||
import type { RecentThought } from '@/entities/session';
|
||||
import { InboxList } from '@/features/inbox';
|
||||
|
||||
interface InboxToolPanelProps {
|
||||
thoughts: RecentThought[];
|
||||
onCompleteThought: (thought: RecentThought) => void;
|
||||
onDeleteThought: (thought: RecentThought) => void;
|
||||
onClear: () => void;
|
||||
}
|
||||
|
||||
export const InboxToolPanel = ({ thoughts, onClear }: InboxToolPanelProps) => {
|
||||
export const InboxToolPanel = ({
|
||||
thoughts,
|
||||
onCompleteThought,
|
||||
onDeleteThought,
|
||||
onClear,
|
||||
}: InboxToolPanelProps) => {
|
||||
const [confirmOpen, setConfirmOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="space-y-3.5">
|
||||
<div className="relative space-y-3.5">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<p className="text-xs text-white/58">나중에 모아보는 읽기 전용 인박스</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClear}
|
||||
onClick={() => setConfirmOpen(true)}
|
||||
className="rounded-full border border-white/20 bg-white/8 px-2.5 py-1 text-[11px] text-white/74 transition-colors hover:bg-white/14 hover:text-white"
|
||||
>
|
||||
모두 비우기
|
||||
</button>
|
||||
</div>
|
||||
<InboxList thoughts={thoughts} />
|
||||
<InboxList
|
||||
thoughts={thoughts}
|
||||
onCompleteThought={onCompleteThought}
|
||||
onDeleteThought={onDeleteThought}
|
||||
/>
|
||||
|
||||
{confirmOpen ? (
|
||||
<div className="absolute inset-0 z-10 flex items-center justify-center rounded-2xl bg-slate-950/70 px-3 backdrop-blur-sm">
|
||||
<div className="w-full max-w-[272px] rounded-2xl border border-white/14 bg-slate-900/92 p-3.5 shadow-xl shadow-slate-950/45">
|
||||
<p className="text-sm font-medium text-white/92">정말 인박스를 비울까요?</p>
|
||||
<p className="mt-1 text-[11px] text-white/60">실수라면 토스트에서 실행취소할 수 있어요.</p>
|
||||
<div className="mt-3 flex justify-end gap-1.5">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setConfirmOpen(false)}
|
||||
className="rounded-full border border-white/20 bg-white/8 px-2.5 py-1 text-[11px] text-white/74 transition-colors hover:bg-white/14 hover:text-white"
|
||||
>
|
||||
취소
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
onClear();
|
||||
setConfirmOpen(false);
|
||||
}}
|
||||
className="rounded-full border border-rose-200/34 bg-rose-200/16 px-2.5 py-1 text-[11px] text-rose-100/92 transition-colors hover:bg-rose-200/24"
|
||||
>
|
||||
비우기
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user