feat(space): secondary weekly review teaser 추가

This commit is contained in:
2026-03-14 20:00:38 +09:00
parent 5d3a5ac8ac
commit de95505d2f
7 changed files with 105 additions and 5 deletions

View File

@@ -6,14 +6,17 @@ import {
preloadAssetImage,
useMediaCatalog,
} from "@/entities/media";
import { usePlanTier } from "@/entities/plan";
import { getSceneById, SCENE_THEMES } from "@/entities/scene";
import { GOAL_CHIPS, SOUND_PRESETS, useThoughtInbox } from "@/entities/session";
import { useFocusSessionEngine } from "@/features/focus-session";
import { useFocusStats } from "@/features/stats";
import {
useSoundPlayback,
useSoundPresetSelection,
} from "@/features/sound-preset";
import { useHudStatusLine } from "@/shared/lib/useHudStatusLine";
import { copy } from "@/shared/i18n";
import { SpaceFocusHudWidget } from "@/widgets/space-focus-hud";
import { SpaceSetupDrawerWidget } from "@/widgets/space-setup-drawer";
import { SpaceToolsDockWidget } from "@/widgets/space-tools-dock";
@@ -63,6 +66,8 @@ export const SpaceWorkspaceWidget = () => {
usedFallbackManifest,
hasResolvedManifest,
} = useMediaCatalog();
const { isPro } = usePlanTier();
const { review, summary: weeklySummary } = useFocusStats();
const initialSceneId = useMemo(
() => resolveInitialSceneId(sceneQuery, undefined),
@@ -97,6 +102,7 @@ export const SpaceWorkspaceWidget = () => {
>("paused");
const [pendingSessionEntryPoint, setPendingSessionEntryPoint] =
useState<SessionEntryPoint>("space-setup");
const [showReviewTeaserAfterComplete, setShowReviewTeaserAfterComplete] = useState(false);
const {
selectedPresetId,
@@ -208,6 +214,27 @@ export const SpaceWorkspaceWidget = () => {
isBootstrapping,
syncCurrentSession,
});
const hasEnoughWeeklyData =
weeklySummary.last7Days.startedSessions >= 3 &&
(weeklySummary.last7Days.completedSessions >= 2 ||
review.recoveryQuality.availability === "ready");
const shouldShowSecondaryReviewTeaser =
workspaceMode === "setup" &&
showReviewTeaserAfterComplete &&
hasEnoughWeeklyData;
const secondaryReviewTeaser = shouldShowSecondaryReviewTeaser
? {
title: isPro
? copy.space.setup.reviewTeaserTitlePro
: copy.space.setup.reviewTeaserTitle,
summary: isPro
? review.carryForward.keepDoing
: copy.space.setup.reviewTeaserHelper,
ctaHref: "/stats?review=weekly&origin=space-complete",
ctaLabel: copy.space.setup.reviewTeaserCta,
onDismiss: () => setShowReviewTeaserAfterComplete(false),
}
: undefined;
useEffect(() => {
if (!isBootstrapping && !currentSession && !hasQueryOverrides) {
@@ -270,12 +297,17 @@ export const SpaceWorkspaceWidget = () => {
}
onGoalChange={selection.handleGoalChange}
onGoalChipSelect={selection.handleGoalChipSelect}
onStart={controls.handleSetupFocusOpen}
onStart={() => {
setShowReviewTeaserAfterComplete(false);
controls.handleSetupFocusOpen();
}}
reviewTeaser={secondaryReviewTeaser}
resumeHint={
selection.showResumePrompt && selection.resumeGoal
? {
goal: selection.resumeGoal,
onResume: () => {
setShowReviewTeaserAfterComplete(false);
selection.setGoalInput(selection.resumeGoal);
selection.setSelectedGoalId(null);
selection.setShowResumePrompt(false);
@@ -285,6 +317,7 @@ export const SpaceWorkspaceWidget = () => {
);
},
onStartFresh: () => {
setShowReviewTeaserAfterComplete(false);
selection.setGoalInput("");
selection.setSelectedGoalId(null);
selection.setShowResumePrompt(false);
@@ -319,7 +352,15 @@ export const SpaceWorkspaceWidget = () => {
onDismissReturnPrompt={awayReturnRecovery.dismissReturnPrompt}
onStatusMessage={pushStatusLine}
onIntentUpdate={controls.handleIntentUpdate}
onGoalFinish={controls.handleGoalComplete}
onGoalFinish={async () => {
const didFinish = await controls.handleGoalComplete();
if (didFinish) {
setShowReviewTeaserAfterComplete(true);
}
return didFinish;
}}
onGoalUpdate={controls.handleGoalAdvance}
/>
) : null}