refactor(control-center): Quick Controls 재디자인 및 플랜/잠금 결제 동선 정리
This commit is contained in:
@@ -3,10 +3,12 @@ import { cn } from '@/shared/lib/cn';
|
||||
|
||||
interface InboxListProps {
|
||||
thoughts: RecentThought[];
|
||||
onCompleteThought: (thought: RecentThought) => void;
|
||||
onDeleteThought: (thought: RecentThought) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const InboxList = ({ thoughts, className }: InboxListProps) => {
|
||||
export const InboxList = ({ thoughts, onCompleteThought, onDeleteThought, className }: InboxListProps) => {
|
||||
if (thoughts.length === 0) {
|
||||
return (
|
||||
<p
|
||||
@@ -25,12 +27,40 @@ export const InboxList = ({ thoughts, className }: InboxListProps) => {
|
||||
{thoughts.slice(0, 10).map((thought) => (
|
||||
<li
|
||||
key={thought.id}
|
||||
className="rounded-2xl border border-white/14 bg-white/7 px-3.5 py-3"
|
||||
className="group relative rounded-2xl border border-white/14 bg-white/7 px-3.5 py-3 pr-24"
|
||||
>
|
||||
<p className="text-sm leading-relaxed text-white/88">{thought.text}</p>
|
||||
<p
|
||||
className={cn(
|
||||
'text-sm leading-relaxed',
|
||||
thought.isCompleted ? 'text-white/58 line-through' : 'text-white/88',
|
||||
)}
|
||||
>
|
||||
{thought.text}
|
||||
</p>
|
||||
<p className="mt-1.5 text-[11px] text-white/54">
|
||||
{thought.roomName} · {thought.capturedAt}
|
||||
</p>
|
||||
<div className="absolute right-2 top-2 flex items-center gap-1 opacity-100 transition-opacity sm:opacity-0 sm:group-hover:opacity-100 sm:group-focus-within:opacity-100">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onCompleteThought(thought)}
|
||||
className={cn(
|
||||
'inline-flex h-6 items-center rounded-full border px-2 text-[10px] transition-colors',
|
||||
thought.isCompleted
|
||||
? 'border-emerald-200/36 bg-emerald-200/18 text-emerald-100'
|
||||
: 'border-white/20 bg-white/8 text-white/76 hover:bg-white/14',
|
||||
)}
|
||||
>
|
||||
{thought.isCompleted ? '완료됨' : '완료'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onDeleteThought(thought)}
|
||||
className="inline-flex h-6 items-center rounded-full border border-rose-200/30 bg-rose-200/10 px-2 text-[10px] text-rose-100/88 transition-colors hover:bg-rose-200/18"
|
||||
>
|
||||
삭제
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
2
src/features/paywall-sheet/index.ts
Normal file
2
src/features/paywall-sheet/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './ui/ManagePlanSheetContent';
|
||||
export * from './ui/PaywallSheetContent';
|
||||
47
src/features/paywall-sheet/ui/ManagePlanSheetContent.tsx
Normal file
47
src/features/paywall-sheet/ui/ManagePlanSheetContent.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
interface ManagePlanSheetContentProps {
|
||||
onClose: () => void;
|
||||
onManage: () => void;
|
||||
onRestore: () => void;
|
||||
}
|
||||
|
||||
export const ManagePlanSheetContent = ({
|
||||
onClose,
|
||||
onManage,
|
||||
onRestore,
|
||||
}: ManagePlanSheetContentProps) => {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<header className="space-y-1">
|
||||
<h3 className="text-lg font-semibold tracking-tight text-white">PRO 관리</h3>
|
||||
<p className="text-xs text-white/62">결제/복원은 더미 동작이며 실제 연동은 하지 않아요.</p>
|
||||
</header>
|
||||
|
||||
<div className="space-y-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onManage}
|
||||
className="w-full rounded-xl border border-white/18 bg-white/[0.04] px-3 py-2 text-left text-sm text-white/84 transition-colors hover:bg-white/[0.1]"
|
||||
>
|
||||
구독 관리 열기
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onRestore}
|
||||
className="w-full rounded-xl border border-white/18 bg-white/[0.04] px-3 py-2 text-left text-sm text-white/84 transition-colors hover:bg-white/[0.1]"
|
||||
>
|
||||
구매 복원
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="rounded-full border border-white/18 bg-white/[0.05] px-3 py-1.5 text-xs text-white/74 transition-colors hover:bg-white/[0.11]"
|
||||
>
|
||||
닫기
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
86
src/features/paywall-sheet/ui/PaywallSheetContent.tsx
Normal file
86
src/features/paywall-sheet/ui/PaywallSheetContent.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { cn } from '@/shared/lib/cn';
|
||||
|
||||
interface PaywallSheetContentProps {
|
||||
onStartPro: () => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
type BillingCycle = 'monthly' | 'yearly';
|
||||
|
||||
const BILLING_OPTIONS: Array<{ id: BillingCycle; label: string; caption: string }> = [
|
||||
{ id: 'monthly', label: '월간', caption: '월 9,900원 (더미)' },
|
||||
{ id: 'yearly', label: '연간', caption: '연 79,000원 (더미)' },
|
||||
];
|
||||
|
||||
const VALUE_POINTS = [
|
||||
'더 많은 공간 / 고화질 배경',
|
||||
'작업용 BGM / 사운드 확장',
|
||||
'프리셋 팩 / 고급 타이머',
|
||||
];
|
||||
|
||||
export const PaywallSheetContent = ({ onStartPro, onClose }: PaywallSheetContentProps) => {
|
||||
const [cycle, setCycle] = useState<BillingCycle>('monthly');
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<header className="space-y-1">
|
||||
<h3 className="text-lg font-semibold tracking-tight text-white">PRO로 더 깊게</h3>
|
||||
<p className="text-xs text-white/62">필요할 때만 잠금 해제하고, 무대는 그대로 유지해요.</p>
|
||||
</header>
|
||||
|
||||
<ul className="space-y-2">
|
||||
{VALUE_POINTS.map((point) => (
|
||||
<li key={point} className="rounded-xl border border-white/14 bg-white/[0.04] px-3 py-2 text-sm text-white/86">
|
||||
{point}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<section className="rounded-2xl border border-white/14 bg-white/[0.04] p-3">
|
||||
<p className="text-xs text-white/60">가격</p>
|
||||
<div className="mt-2 grid grid-cols-2 gap-2">
|
||||
{BILLING_OPTIONS.map((option) => {
|
||||
const selected = option.id === cycle;
|
||||
|
||||
return (
|
||||
<button
|
||||
key={option.id}
|
||||
type="button"
|
||||
onClick={() => setCycle(option.id)}
|
||||
className={cn(
|
||||
'rounded-xl border px-2.5 py-2 text-left text-xs transition-colors',
|
||||
selected
|
||||
? 'border-sky-200/40 bg-sky-200/14 text-white'
|
||||
: 'border-white/16 bg-white/[0.03] text-white/72 hover:bg-white/[0.08]',
|
||||
)}
|
||||
>
|
||||
<p className="font-medium">{option.label}</p>
|
||||
<p className="mt-0.5 text-[10px] text-white/56">{option.caption}</p>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="rounded-full border border-white/18 bg-white/[0.05] px-3 py-1.5 text-xs text-white/74 transition-colors hover:bg-white/[0.11]"
|
||||
>
|
||||
나중에
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onStartPro}
|
||||
className="rounded-full border border-sky-200/44 bg-sky-300/84 px-3.5 py-1.5 text-xs font-semibold text-slate-900 transition-colors hover:bg-sky-300"
|
||||
>
|
||||
PRO 시작하기
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
1
src/features/plan-pill/index.ts
Normal file
1
src/features/plan-pill/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './ui/PlanPill';
|
||||
26
src/features/plan-pill/ui/PlanPill.tsx
Normal file
26
src/features/plan-pill/ui/PlanPill.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import type { PlanTier } from '@/entities/plan';
|
||||
import { cn } from '@/shared/lib/cn';
|
||||
|
||||
interface PlanPillProps {
|
||||
plan: PlanTier;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
export const PlanPill = ({ plan, onClick }: PlanPillProps) => {
|
||||
const isPro = plan === 'pro';
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
className={cn(
|
||||
'inline-flex items-center rounded-full border px-2.5 py-1 text-[11px] font-medium tracking-[0.08em] uppercase transition-colors',
|
||||
isPro
|
||||
? 'border-amber-200/46 bg-amber-200/14 text-amber-100 hover:bg-amber-200/24'
|
||||
: 'border-white/20 bg-white/8 text-white/82 hover:bg-white/14',
|
||||
)}
|
||||
>
|
||||
{isPro ? 'PRO' : 'Normal'}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user