fix: 플랜 페이지 전체 섹션 투명 현상 완전 해결 — initial opacity:0 전수 제거
- PlanHeader: motion.div/h1/p의 initial opacity:0 → plain HTML로 교체
(배경 blob 애니메이션은 opacity 무관하여 유지)
- RepurposingProposal: 카드 motion.div → plain div, 확장 accordion도 plain div
- ChannelStrategy, ContentStrategy, AssetCollection, PlanCTA,
BrandingGuide, WorkflowTracker, StrategyAdjustmentSection:
initial={{ opacity: 0, ... }} 라인 전수 제거
- 이 환경의 framer-motion이 animate prop 완료 실패 → opacity:0에서 멈춤
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main
parent
eaf215ea6d
commit
6b3b950ad0
|
|
@ -91,7 +91,6 @@ export default function AssetCollection({ data }: AssetCollectionProps) {
|
|||
key={asset.id}
|
||||
className="rounded-2xl border border-slate-100 bg-white shadow-sm p-5 cursor-pointer hover:shadow-[3px_4px_12px_rgba(0,0,0,0.06)] hover:border-[#D5CDF5] transition-all"
|
||||
onClick={() => setSelectedAsset({ kind: 'asset', data: asset })}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: i * 0.05 }}
|
||||
>
|
||||
|
|
@ -153,7 +152,6 @@ export default function AssetCollection({ data }: AssetCollectionProps) {
|
|||
key={video.title}
|
||||
className="min-w-[280px] rounded-2xl border border-slate-100 bg-white shadow-sm p-5 shrink-0 cursor-pointer hover:shadow-[3px_4px_12px_rgba(0,0,0,0.06)] hover:border-[#D5CDF5] transition-all"
|
||||
onClick={() => setSelectedAsset({ kind: 'youtube', data: video })}
|
||||
initial={{ opacity: 0, x: 20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.4, delay: i * 0.1 }}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -157,7 +157,6 @@ function VisualIdentityTab({ data }: { data: BrandGuide }) {
|
|||
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4 }}
|
||||
className="space-y-8"
|
||||
|
|
@ -268,7 +267,6 @@ function VisualIdentityTab({ data }: { data: BrandGuide }) {
|
|||
function ToneVoiceTab({ tone }: { tone: BrandGuide['toneOfVoice'] }) {
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4 }}
|
||||
className="space-y-6"
|
||||
|
|
@ -339,7 +337,6 @@ function ToneVoiceTab({ tone }: { tone: BrandGuide['toneOfVoice'] }) {
|
|||
function ChannelRulesTab({ channels }: { channels: BrandGuide['channelBranding'] }) {
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4 }}
|
||||
>
|
||||
|
|
@ -396,7 +393,6 @@ function BrandConsistencyTab({ inconsistencies }: { inconsistencies: BrandIncons
|
|||
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4 }}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ export default function ChannelStrategy({ channels }: ChannelStrategyProps) {
|
|||
<motion.div
|
||||
key={ch.channelId}
|
||||
className="bg-white rounded-2xl p-6 shadow-[3px_4px_12px_rgba(0,0,0,0.06)] hover:shadow-[4px_6px_16px_rgba(0,0,0,0.09)] transition-shadow"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -69,7 +69,6 @@ export default function ContentStrategy({ data }: ContentStrategyProps) {
|
|||
{activeTab === 'pillars' && (
|
||||
<motion.div
|
||||
className="grid md:grid-cols-2 gap-6"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4 }}
|
||||
>
|
||||
|
|
@ -78,7 +77,6 @@ export default function ContentStrategy({ data }: ContentStrategyProps) {
|
|||
key={pillar.title}
|
||||
className="rounded-2xl border border-slate-100 bg-white shadow-sm p-6 border-l-4"
|
||||
style={{ borderLeftColor: pillar.color }}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: i * 0.1 }}
|
||||
>
|
||||
|
|
@ -109,7 +107,6 @@ export default function ContentStrategy({ data }: ContentStrategyProps) {
|
|||
{activeTab === 'types' && (
|
||||
<motion.div
|
||||
className="rounded-2xl overflow-hidden border border-slate-100"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4 }}
|
||||
>
|
||||
|
|
@ -123,7 +120,6 @@ export default function ContentStrategy({ data }: ContentStrategyProps) {
|
|||
<motion.div
|
||||
key={row.format}
|
||||
className={`grid grid-cols-4 ${i % 2 === 0 ? 'bg-white' : 'bg-slate-50'}`}
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3, delay: i * 0.04 }}
|
||||
>
|
||||
|
|
@ -151,7 +147,6 @@ export default function ContentStrategy({ data }: ContentStrategyProps) {
|
|||
{activeTab === 'workflow' && (
|
||||
<motion.div
|
||||
className="flex md:flex-row flex-col gap-4 items-stretch"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4 }}
|
||||
>
|
||||
|
|
@ -159,7 +154,6 @@ export default function ContentStrategy({ data }: ContentStrategyProps) {
|
|||
<div key={step.step} className="flex md:flex-row flex-col items-center gap-4 flex-1">
|
||||
<motion.div
|
||||
className="flex-1 w-full rounded-2xl border border-slate-100 bg-white shadow-sm p-5"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: i * 0.1 }}
|
||||
>
|
||||
|
|
@ -191,7 +185,6 @@ export default function ContentStrategy({ data }: ContentStrategyProps) {
|
|||
{/* Tab 4: Repurposing */}
|
||||
{activeTab === 'repurposing' && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4 }}
|
||||
>
|
||||
|
|
@ -214,7 +207,6 @@ export default function ContentStrategy({ data }: ContentStrategyProps) {
|
|||
<motion.div
|
||||
key={`${output.format}-${i}`}
|
||||
className="rounded-xl border border-slate-100 bg-white p-4"
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3, delay: i * 0.05 }}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ export default function PlanCTA() {
|
|||
return (
|
||||
<motion.section
|
||||
className="py-16 md:py-20 px-6"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, ease: 'easeOut' }}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export default function PlanHeader({
|
|||
}: PlanHeaderProps) {
|
||||
return (
|
||||
<section className="relative overflow-hidden bg-[radial-gradient(ellipse_at_top_left,#e0e7ff,transparent_50%),radial-gradient(ellipse_at_bottom_right,#fce7f3,transparent_50%),radial-gradient(ellipse_at_center,#f5f3ff,transparent_60%)] py-20 md:py-28 px-6">
|
||||
{/* Animated blobs */}
|
||||
{/* Animated blobs — position only, no opacity */}
|
||||
<motion.div
|
||||
className="absolute top-10 left-10 w-72 h-72 rounded-full bg-[#6C5CE7]/10 blur-3xl"
|
||||
animate={{ x: [0, 30, 0], y: [0, -20, 0] }}
|
||||
|
|
@ -47,46 +47,21 @@ export default function PlanHeader({
|
|||
|
||||
<div className="relative max-w-7xl mx-auto">
|
||||
<div className="flex flex-col md:flex-row items-center md:items-start justify-between gap-10">
|
||||
{/* Left: Text content */}
|
||||
<motion.div
|
||||
className="flex-1 text-center md:text-left"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
<motion.p
|
||||
className="text-xs font-semibold text-[#6C5CE7] mb-4 tracking-widest uppercase"
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.1 }}
|
||||
>
|
||||
{/* Left: Text content — plain div, no opacity animation */}
|
||||
<div className="flex-1 text-center md:text-left">
|
||||
<p className="text-xs font-semibold text-[#6C5CE7] mb-4 tracking-widest uppercase">
|
||||
Marketing Execution Plan
|
||||
</motion.p>
|
||||
</p>
|
||||
|
||||
<motion.h1
|
||||
className="font-serif text-4xl md:text-5xl font-bold text-[#0A1128] mb-3"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
>
|
||||
<h1 className="font-serif text-4xl md:text-5xl font-bold text-[#0A1128] mb-3">
|
||||
{clinicName}
|
||||
</motion.h1>
|
||||
</h1>
|
||||
|
||||
<motion.p
|
||||
className="text-xl text-slate-600 mb-8"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.3 }}
|
||||
>
|
||||
<p className="text-xl text-slate-600 mb-8">
|
||||
{clinicNameEn}
|
||||
</motion.p>
|
||||
</p>
|
||||
|
||||
<motion.div
|
||||
className="flex flex-wrap gap-3 justify-center md:justify-start"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.4 }}
|
||||
>
|
||||
<div className="flex flex-wrap gap-3 justify-center md:justify-start">
|
||||
<span className="inline-flex items-center gap-2 rounded-full bg-white/60 backdrop-blur-sm border border-white/40 px-3 py-1 text-sm font-medium text-slate-700">
|
||||
<CalendarFilled size={14} className="text-slate-400" />
|
||||
{formatDate(date)}
|
||||
|
|
@ -95,23 +70,18 @@ export default function PlanHeader({
|
|||
<GlobeFilled size={14} className="text-slate-400" />
|
||||
{targetUrl}
|
||||
</span>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right: 90 Days badge */}
|
||||
<motion.div
|
||||
className="shrink-0"
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.6, delay: 0.3 }}
|
||||
>
|
||||
<div className="shrink-0">
|
||||
<div className="w-32 h-32 rounded-full bg-gradient-to-r from-[#4F1DA1] to-[#021341] flex flex-col items-center justify-center shadow-lg">
|
||||
<span className="text-4xl font-bold text-white leading-none">
|
||||
90
|
||||
</span>
|
||||
<span className="text-sm text-white/60">Days</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { useState } from 'react';
|
||||
import { motion } from 'motion/react';
|
||||
import {
|
||||
YoutubeFilled,
|
||||
InstagramFilled,
|
||||
|
|
@ -63,12 +62,9 @@ export default function RepurposingProposal({ proposals }: RepurposingProposalPr
|
|||
const isExpanded = expandedIdx === idx;
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
<div
|
||||
key={item.sourceVideo.title}
|
||||
className="rounded-2xl border border-slate-100 bg-white shadow-[3px_4px_12px_rgba(0,0,0,0.06)] overflow-hidden"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: idx * 0.08 }}
|
||||
>
|
||||
{/* Card Header — click to expand */}
|
||||
<button
|
||||
|
|
@ -120,26 +116,17 @@ export default function RepurposingProposal({ proposals }: RepurposingProposalPr
|
|||
|
||||
{/* Expanded: Repurpose outputs */}
|
||||
{isExpanded && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, height: 0 }}
|
||||
animate={{ opacity: 1, height: 'auto' }}
|
||||
exit={{ opacity: 0, height: 0 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
className="border-t border-slate-100"
|
||||
>
|
||||
<div className="border-t border-slate-100">
|
||||
<div className="px-6 py-4">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<BoltFilled size={14} className="text-[#6C5CE7]" />
|
||||
<p className="text-xs font-semibold text-slate-500 uppercase tracking-widest">REPURPOSE AS</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
{item.outputs.map((output, oIdx) => (
|
||||
<motion.div
|
||||
{item.outputs.map((output) => (
|
||||
<div
|
||||
key={output.format}
|
||||
className="flex items-start gap-3 p-4 rounded-xl bg-slate-50 border border-slate-100"
|
||||
initial={{ opacity: 0, x: -8 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: oIdx * 0.06 }}
|
||||
>
|
||||
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-[#6C5CE7]/10 to-[#4F1DA1]/10 flex items-center justify-center shrink-0">
|
||||
<ChannelIcon channel={output.channel} size={16} />
|
||||
|
|
@ -149,13 +136,13 @@ export default function RepurposingProposal({ proposals }: RepurposingProposalPr
|
|||
<p className="text-xs text-[#6C5CE7] font-medium mb-1">{output.channel}</p>
|
||||
<p className="text-xs text-slate-500 leading-relaxed">{output.description}</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
)}
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -176,7 +176,6 @@ export default function StrategyAdjustmentSection({ clinicId, planId }: Strategy
|
|||
{overallAssessment && (
|
||||
<motion.div
|
||||
className="bg-purple-50 border border-purple-100 rounded-2xl p-4 mb-6"
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
>
|
||||
<p className="text-sm text-purple-800">{overallAssessment}</p>
|
||||
|
|
@ -192,7 +191,6 @@ export default function StrategyAdjustmentSection({ clinicId, planId }: Strategy
|
|||
<motion.div
|
||||
key={i}
|
||||
className="bg-white rounded-xl p-4 border border-slate-100"
|
||||
initial={{ opacity: 0, x: -10 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: i * 0.05 }}
|
||||
>
|
||||
|
|
@ -237,7 +235,6 @@ export default function StrategyAdjustmentSection({ clinicId, planId }: Strategy
|
|||
<motion.div
|
||||
key={i}
|
||||
className={`${colors.bg} rounded-xl p-4 border border-slate-100`}
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: i * 0.08 }}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -101,7 +101,6 @@ function WorkflowCard({ item, onStageChange, onNotesChange }: {
|
|||
<motion.div
|
||||
layout
|
||||
className="bg-white rounded-2xl border border-slate-100 shadow-[3px_4px_12px_rgba(0,0,0,0.06)] overflow-hidden"
|
||||
initial={{ opacity: 0, y: 16 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -8 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
|
|
@ -144,7 +143,6 @@ function WorkflowCard({ item, onStageChange, onNotesChange }: {
|
|||
<AnimatePresence>
|
||||
{expanded && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, height: 0 }}
|
||||
animate={{ opacity: 1, height: 'auto' }}
|
||||
exit={{ opacity: 0, height: 0 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
|
|
@ -341,7 +339,6 @@ export default function WorkflowTracker({ data }: WorkflowTrackerProps) {
|
|||
className={`flex items-center gap-2 px-3 py-1.5 rounded-full border text-xs font-medium transition-all ${color.bg} ${color.text} ${color.border} ${
|
||||
isActive ? 'ring-2 ring-white/40' : ''
|
||||
} ${activeStageFilter && !isActive ? 'opacity-40' : ''}`}
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
>
|
||||
|
|
@ -366,7 +363,6 @@ export default function WorkflowTracker({ data }: WorkflowTrackerProps) {
|
|||
<AnimatePresence mode="popLayout">
|
||||
{filtered.length === 0 ? (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
className="text-center py-10 text-white/30 text-sm"
|
||||
>
|
||||
|
|
|
|||
Loading…
Reference in New Issue