|
|
|
@@ -1,12 +1,52 @@
|
|
|
|
'use client';
|
|
|
|
'use client';
|
|
|
|
|
|
|
|
|
|
|
|
import Link from 'next/link';
|
|
|
|
import Link from 'next/link';
|
|
|
|
|
|
|
|
import { useMemo } from 'react';
|
|
|
|
|
|
|
|
import { useMediaCatalog, getSceneStageBackgroundStyle } from '@/entities/media';
|
|
|
|
import { usePlanTier } from '@/entities/plan';
|
|
|
|
import { usePlanTier } from '@/entities/plan';
|
|
|
|
|
|
|
|
import { getSceneById, SCENE_THEMES } from '@/entities/scene';
|
|
|
|
import { useFocusStats } from '@/features/stats';
|
|
|
|
import { useFocusStats } from '@/features/stats';
|
|
|
|
import { copy } from '@/shared/i18n';
|
|
|
|
import { copy } from '@/shared/i18n';
|
|
|
|
import { cn } from '@/shared/lib/cn';
|
|
|
|
import { cn } from '@/shared/lib/cn';
|
|
|
|
|
|
|
|
|
|
|
|
const ReviewMetric = ({
|
|
|
|
const glassPanelClass =
|
|
|
|
|
|
|
|
'rounded-[2rem] border border-white/10 bg-[linear-gradient(160deg,rgba(8,12,18,0.46)_0%,rgba(8,12,18,0.2)_58%,rgba(8,12,18,0.52)_100%)] shadow-[0_24px_80px_rgba(3,7,18,0.28)] backdrop-blur-[24px]';
|
|
|
|
|
|
|
|
const metricTileClass =
|
|
|
|
|
|
|
|
'rounded-[1.45rem] border border-white/10 bg-[linear-gradient(145deg,rgba(255,255,255,0.08)_0%,rgba(255,255,255,0.04)_100%)] px-4 py-4 backdrop-blur-xl';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const DEFAULT_STATS_SCENE_ID = getSceneById('forest')?.id ?? SCENE_THEMES[0].id;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const reviewStageSceneByPreset = (presetId: string) => {
|
|
|
|
|
|
|
|
if (presetId.startsWith('forest')) {
|
|
|
|
|
|
|
|
return getSceneById('forest') ?? SCENE_THEMES[0];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return getSceneById(DEFAULT_STATS_SCENE_ID) ?? SCENE_THEMES[0];
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const StatusAccessory = ({
|
|
|
|
|
|
|
|
label,
|
|
|
|
|
|
|
|
subtle = false,
|
|
|
|
|
|
|
|
}: {
|
|
|
|
|
|
|
|
label: string;
|
|
|
|
|
|
|
|
subtle?: boolean;
|
|
|
|
|
|
|
|
}) => {
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
|
|
<span
|
|
|
|
|
|
|
|
className={cn(
|
|
|
|
|
|
|
|
'inline-flex items-center gap-2 rounded-full border px-3 py-1.5 text-[10px] font-semibold uppercase tracking-[0.2em] backdrop-blur-md',
|
|
|
|
|
|
|
|
subtle
|
|
|
|
|
|
|
|
? 'border-white/8 bg-white/[0.05] text-white/44'
|
|
|
|
|
|
|
|
: 'border-white/12 bg-white/[0.07] text-white/64',
|
|
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<span className="h-1.5 w-1.5 rounded-full bg-white/50" />
|
|
|
|
|
|
|
|
{label}
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const SnapshotMetric = ({
|
|
|
|
label,
|
|
|
|
label,
|
|
|
|
value,
|
|
|
|
value,
|
|
|
|
hint,
|
|
|
|
hint,
|
|
|
|
@@ -16,10 +56,10 @@ const ReviewMetric = ({
|
|
|
|
hint: string;
|
|
|
|
hint: string;
|
|
|
|
}) => {
|
|
|
|
}) => {
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<div className="rounded-2xl border border-brand-dark/8 bg-white/70 px-4 py-3">
|
|
|
|
<div className={metricTileClass}>
|
|
|
|
<p className="text-[11px] font-medium tracking-[0.08em] text-brand-dark/44">{label}</p>
|
|
|
|
<p className="text-[10px] font-medium uppercase tracking-[0.18em] text-white/42">{label}</p>
|
|
|
|
<p className="mt-2 text-[1.05rem] font-semibold text-brand-dark">{value}</p>
|
|
|
|
<p className="mt-3 text-[1.05rem] font-medium tracking-[-0.03em] text-white/92">{value}</p>
|
|
|
|
<p className="mt-1 text-[12px] leading-[1.45] text-brand-dark/52">{hint}</p>
|
|
|
|
<p className="mt-2 text-[12px] leading-[1.58] text-white/54">{hint}</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
@@ -30,35 +70,36 @@ const ReviewSection = ({
|
|
|
|
metrics,
|
|
|
|
metrics,
|
|
|
|
availability,
|
|
|
|
availability,
|
|
|
|
note,
|
|
|
|
note,
|
|
|
|
toneClass,
|
|
|
|
accentClass,
|
|
|
|
}: {
|
|
|
|
}: {
|
|
|
|
title: string;
|
|
|
|
title: string;
|
|
|
|
summary: string;
|
|
|
|
summary: string;
|
|
|
|
metrics: Array<{ id: string; label: string; value: string; hint: string }>;
|
|
|
|
metrics: Array<{ id: string; label: string; value: string; hint: string }>;
|
|
|
|
availability: 'ready' | 'limited';
|
|
|
|
availability: 'ready' | 'limited';
|
|
|
|
note?: string;
|
|
|
|
note?: string;
|
|
|
|
toneClass: string;
|
|
|
|
accentClass: string;
|
|
|
|
}) => {
|
|
|
|
}) => {
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<section className="overflow-hidden rounded-[28px] border border-brand-dark/10 bg-white/76 backdrop-blur-md">
|
|
|
|
<section className={cn(glassPanelClass, 'relative overflow-hidden p-5 sm:p-6')}>
|
|
|
|
<div className={cn('h-1.5 w-full', toneClass)} />
|
|
|
|
<div className={cn('pointer-events-none absolute inset-x-0 top-0 h-20 opacity-90', accentClass)} />
|
|
|
|
<div className="px-5 py-5 sm:px-6">
|
|
|
|
<div className="relative">
|
|
|
|
<div className="flex items-start justify-between gap-4">
|
|
|
|
<div className="flex items-start justify-between gap-4">
|
|
|
|
<div className="max-w-2xl">
|
|
|
|
<div className="max-w-[34rem]">
|
|
|
|
<h2 className="text-[1.05rem] font-semibold tracking-tight text-brand-dark">{title}</h2>
|
|
|
|
<p className="text-[10px] font-medium uppercase tracking-[0.2em] text-white/40">
|
|
|
|
<p className="mt-2 text-[14px] leading-[1.65] text-brand-dark/68">{summary}</p>
|
|
|
|
Weekly Review
|
|
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<h2 className="mt-3 text-[1.2rem] font-medium tracking-[-0.04em] text-white">
|
|
|
|
|
|
|
|
{title}
|
|
|
|
|
|
|
|
</h2>
|
|
|
|
|
|
|
|
<p className="mt-3 text-[13px] leading-[1.68] text-white/64">{summary}</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{availability === 'limited' ? (
|
|
|
|
{availability === 'limited' ? <StatusAccessory label="Limited" subtle /> : null}
|
|
|
|
<span className="shrink-0 rounded-full border border-brand-dark/10 bg-white/70 px-3 py-1 text-[11px] font-medium tracking-[0.08em] text-brand-dark/46">
|
|
|
|
|
|
|
|
Limited
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
) : null}
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{metrics.length > 0 ? (
|
|
|
|
{metrics.length > 0 ? (
|
|
|
|
<div className="mt-5 grid gap-3 sm:grid-cols-3">
|
|
|
|
<div className="mt-5 grid gap-3 sm:grid-cols-3">
|
|
|
|
{metrics.map((metric) => (
|
|
|
|
{metrics.map((metric) => (
|
|
|
|
<ReviewMetric
|
|
|
|
<SnapshotMetric
|
|
|
|
key={metric.id}
|
|
|
|
key={metric.id}
|
|
|
|
label={metric.label}
|
|
|
|
label={metric.label}
|
|
|
|
value={metric.value}
|
|
|
|
value={metric.value}
|
|
|
|
@@ -69,7 +110,7 @@ const ReviewSection = ({
|
|
|
|
) : null}
|
|
|
|
) : null}
|
|
|
|
|
|
|
|
|
|
|
|
{note ? (
|
|
|
|
{note ? (
|
|
|
|
<p className="mt-5 rounded-2xl border border-brand-dark/8 bg-white/56 px-4 py-3 text-[13px] leading-[1.6] text-brand-dark/54">
|
|
|
|
<p className="mt-5 rounded-[1.35rem] border border-white/8 bg-white/[0.05] px-4 py-3 text-[12px] leading-[1.62] text-white/52">
|
|
|
|
{note}
|
|
|
|
{note}
|
|
|
|
</p>
|
|
|
|
</p>
|
|
|
|
) : null}
|
|
|
|
) : null}
|
|
|
|
@@ -81,20 +122,37 @@ const ReviewSection = ({
|
|
|
|
export const StatsOverviewWidget = () => {
|
|
|
|
export const StatsOverviewWidget = () => {
|
|
|
|
const { stats } = copy;
|
|
|
|
const { stats } = copy;
|
|
|
|
const { isPro } = usePlanTier();
|
|
|
|
const { isPro } = usePlanTier();
|
|
|
|
|
|
|
|
const { sceneAssetMap } = useMediaCatalog();
|
|
|
|
const { review, isLoading, error, source, refetch } = useFocusStats();
|
|
|
|
const { review, isLoading, error, source, refetch } = useFocusStats();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const activeScene = useMemo(
|
|
|
|
|
|
|
|
() => reviewStageSceneByPreset(review.carryForward.presetId),
|
|
|
|
|
|
|
|
[review.carryForward.presetId],
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
const sourceLabel = source === 'api' ? stats.sourceApi : stats.sourceMock;
|
|
|
|
|
|
|
|
const syncLabel = error ? error : isLoading ? stats.loading : stats.synced;
|
|
|
|
const carryForwardCtaLabel = isPro ? stats.reviewCarryCtaPro : review.carryForward.ctaLabel;
|
|
|
|
const carryForwardCtaLabel = isPro ? stats.reviewCarryCtaPro : review.carryForward.ctaLabel;
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<div className="min-h-screen bg-[radial-gradient(circle_at_14%_0%,rgba(198,219,244,0.52),transparent_42%),radial-gradient(circle_at_88%_12%,rgba(232,240,249,0.8),transparent_34%),linear-gradient(180deg,#f8fbff_0%,#f2f7fc_46%,#edf3f9_100%)] text-brand-dark">
|
|
|
|
<div className="relative min-h-dvh overflow-hidden bg-black text-white selection:bg-white/20">
|
|
|
|
<div className="mx-auto w-full max-w-6xl px-4 pb-12 pt-6 sm:px-6 lg:px-8">
|
|
|
|
<div
|
|
|
|
<header className="mb-6 flex flex-wrap items-center justify-between gap-3 rounded-[24px] border border-brand-dark/10 bg-white/74 px-4 py-3 backdrop-blur-md sm:px-5">
|
|
|
|
className="absolute inset-0 bg-cover bg-center"
|
|
|
|
<div>
|
|
|
|
style={getSceneStageBackgroundStyle(activeScene, sceneAssetMap?.[activeScene.id])}
|
|
|
|
<p className="text-[11px] font-medium tracking-[0.14em] text-brand-dark/42">
|
|
|
|
/>
|
|
|
|
{review.periodLabel}
|
|
|
|
<div className="absolute inset-0 bg-[radial-gradient(circle_at_center,transparent_0%,rgba(0,0,0,0.62)_100%)] mix-blend-multiply pointer-events-none" />
|
|
|
|
|
|
|
|
<div className="absolute inset-0 bg-black/18 pointer-events-none" />
|
|
|
|
|
|
|
|
<div className="absolute inset-0 bg-[linear-gradient(180deg,rgba(2,6,23,0.12)_0%,rgba(2,6,23,0.32)_44%,rgba(2,6,23,0.62)_100%)] pointer-events-none" />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<header className="absolute inset-x-0 top-0 z-40 flex items-center justify-between px-5 py-5 md:px-8 md:py-8">
|
|
|
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
|
|
|
<p className="text-[11px] font-bold uppercase tracking-[0.4em] text-white/54">
|
|
|
|
|
|
|
|
Weekly Review
|
|
|
|
</p>
|
|
|
|
</p>
|
|
|
|
<h1 className="mt-1 text-[1.2rem] font-semibold tracking-tight text-brand-dark">
|
|
|
|
{isPro ? (
|
|
|
|
{review.snapshotTitle}
|
|
|
|
<span className="rounded-full bg-white/10 px-2 py-0.5 text-[9px] font-bold uppercase tracking-[0.18em] text-white/60 backdrop-blur-md">
|
|
|
|
</h1>
|
|
|
|
PRO
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
) : null}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
@@ -103,55 +161,58 @@ export const StatsOverviewWidget = () => {
|
|
|
|
onClick={() => {
|
|
|
|
onClick={() => {
|
|
|
|
void refetch();
|
|
|
|
void refetch();
|
|
|
|
}}
|
|
|
|
}}
|
|
|
|
className="rounded-full border border-brand-dark/12 bg-white/74 px-3.5 py-2 text-[12px] font-medium text-brand-dark/78 transition hover:bg-white/94"
|
|
|
|
className="rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-[10px] font-semibold uppercase tracking-[0.18em] text-white/62 backdrop-blur-md transition hover:bg-white/[0.1] hover:text-white"
|
|
|
|
>
|
|
|
|
>
|
|
|
|
{stats.refresh}
|
|
|
|
{stats.refresh}
|
|
|
|
</button>
|
|
|
|
</button>
|
|
|
|
<Link
|
|
|
|
<Link
|
|
|
|
href="/app"
|
|
|
|
href="/app"
|
|
|
|
className="rounded-full border border-brand-dark/12 bg-white/74 px-3.5 py-2 text-[12px] font-medium text-brand-dark/78 transition hover:bg-white/94"
|
|
|
|
className="rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-[10px] font-semibold uppercase tracking-[0.18em] text-white/62 backdrop-blur-md transition hover:bg-white/[0.1] hover:text-white"
|
|
|
|
>
|
|
|
|
>
|
|
|
|
{copy.common.hub}
|
|
|
|
{copy.common.hub}
|
|
|
|
</Link>
|
|
|
|
</Link>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</header>
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
<main className="relative z-10 mx-auto flex min-h-dvh w-full max-w-[90rem] flex-col px-4 pb-10 pt-28 md:px-8 md:pb-12 md:pt-32">
|
|
|
|
<section className="overflow-hidden rounded-[32px] border border-brand-dark/10 bg-[linear-gradient(180deg,rgba(255,255,255,0.84),rgba(255,255,255,0.7))] px-5 py-5 shadow-[0_20px_56px_rgba(148,163,184,0.12)] backdrop-blur-md sm:px-6 sm:py-6">
|
|
|
|
<div className="flex flex-col items-center text-center">
|
|
|
|
<div className="flex flex-wrap items-start justify-between gap-4">
|
|
|
|
<div className="flex flex-wrap items-center justify-center gap-2">
|
|
|
|
<div className="max-w-3xl">
|
|
|
|
<StatusAccessory label={review.periodLabel} />
|
|
|
|
<p className="text-[11px] font-medium tracking-[0.14em] text-brand-dark/42">
|
|
|
|
<StatusAccessory label={sourceLabel} subtle />
|
|
|
|
{source === 'api' ? stats.sourceApi : stats.sourceMock}
|
|
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<p className="mt-3 text-[1.55rem] font-semibold leading-[1.3] tracking-tight text-brand-dark sm:text-[1.9rem]">
|
|
|
|
|
|
|
|
{review.snapshotSummary}
|
|
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<p className="mt-3 text-[13px] leading-[1.6] text-brand-dark/54">
|
|
|
|
|
|
|
|
{error ? error : isLoading ? stats.loading : stats.synced}
|
|
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="grid w-full gap-3 sm:grid-cols-2 xl:w-[26rem]">
|
|
|
|
<div className="mt-8 max-w-[58rem] space-y-4">
|
|
|
|
|
|
|
|
<p className="text-[12px] font-medium uppercase tracking-[0.22em] text-white/42">
|
|
|
|
|
|
|
|
{review.snapshotTitle}
|
|
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<h1 className="text-[2.5rem] font-light leading-[0.98] tracking-[-0.06em] text-white md:text-[4rem]">
|
|
|
|
|
|
|
|
{review.snapshotSummary}
|
|
|
|
|
|
|
|
</h1>
|
|
|
|
|
|
|
|
<p className="mx-auto max-w-[38rem] text-[13px] leading-[1.7] text-white/54 md:text-[14px]">
|
|
|
|
|
|
|
|
{syncLabel}
|
|
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<section className="mt-10 grid gap-3 md:grid-cols-2 xl:grid-cols-4">
|
|
|
|
{review.snapshotMetrics.map((metric) => (
|
|
|
|
{review.snapshotMetrics.map((metric) => (
|
|
|
|
<ReviewMetric
|
|
|
|
<SnapshotMetric
|
|
|
|
key={metric.id}
|
|
|
|
key={metric.id}
|
|
|
|
label={metric.label}
|
|
|
|
label={metric.label}
|
|
|
|
value={metric.value}
|
|
|
|
value={metric.value}
|
|
|
|
hint={metric.hint}
|
|
|
|
hint={metric.hint}
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
))}
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="grid gap-5 xl:grid-cols-[1.08fr_0.92fr]">
|
|
|
|
<section className="mt-12 grid gap-5 xl:grid-cols-[1.08fr_0.92fr]">
|
|
|
|
<ReviewSection
|
|
|
|
<ReviewSection
|
|
|
|
title={review.startQuality.title}
|
|
|
|
title={review.startQuality.title}
|
|
|
|
summary={review.startQuality.summary}
|
|
|
|
summary={review.startQuality.summary}
|
|
|
|
metrics={review.startQuality.metrics}
|
|
|
|
metrics={review.startQuality.metrics}
|
|
|
|
availability={review.startQuality.availability}
|
|
|
|
availability={review.startQuality.availability}
|
|
|
|
note={review.startQuality.note}
|
|
|
|
note={review.startQuality.note}
|
|
|
|
toneClass="bg-[linear-gradient(90deg,rgba(96,165,250,0.68),rgba(191,219,254,0.22))]"
|
|
|
|
accentClass="bg-[radial-gradient(circle_at_top_left,rgba(96,165,250,0.34),rgba(96,165,250,0)_62%)]"
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<ReviewSection
|
|
|
|
<ReviewSection
|
|
|
|
@@ -160,64 +221,80 @@ export const StatsOverviewWidget = () => {
|
|
|
|
metrics={review.recoveryQuality.metrics}
|
|
|
|
metrics={review.recoveryQuality.metrics}
|
|
|
|
availability={review.recoveryQuality.availability}
|
|
|
|
availability={review.recoveryQuality.availability}
|
|
|
|
note={review.recoveryQuality.note}
|
|
|
|
note={review.recoveryQuality.note}
|
|
|
|
toneClass="bg-[linear-gradient(90deg,rgba(20,184,166,0.68),rgba(153,246,228,0.22))]"
|
|
|
|
accentClass="bg-[radial-gradient(circle_at_top_left,rgba(20,184,166,0.32),rgba(20,184,166,0)_62%)]"
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="grid gap-5 xl:grid-cols-[1fr_0.88fr]">
|
|
|
|
<section className="mt-5 grid gap-5 xl:grid-cols-[0.94fr_1.06fr]">
|
|
|
|
<ReviewSection
|
|
|
|
<ReviewSection
|
|
|
|
title={review.completionQuality.title}
|
|
|
|
title={review.completionQuality.title}
|
|
|
|
summary={review.completionQuality.summary}
|
|
|
|
summary={review.completionQuality.summary}
|
|
|
|
metrics={review.completionQuality.metrics}
|
|
|
|
metrics={review.completionQuality.metrics}
|
|
|
|
availability={review.completionQuality.availability}
|
|
|
|
availability={review.completionQuality.availability}
|
|
|
|
note={review.completionQuality.note}
|
|
|
|
note={review.completionQuality.note}
|
|
|
|
toneClass="bg-[linear-gradient(90deg,rgba(251,191,36,0.72),rgba(253,230,138,0.22))]"
|
|
|
|
accentClass="bg-[radial-gradient(circle_at_top_left,rgba(245,158,11,0.34),rgba(245,158,11,0)_62%)]"
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<section className="overflow-hidden rounded-[28px] border border-brand-dark/10 bg-white/78 backdrop-blur-md">
|
|
|
|
<section className={cn(glassPanelClass, 'relative overflow-hidden p-6 sm:p-7')}>
|
|
|
|
<div className="h-1.5 w-full bg-[linear-gradient(90deg,rgba(139,92,246,0.72),rgba(221,214,254,0.2))]" />
|
|
|
|
<div className="pointer-events-none absolute inset-x-0 top-0 h-24 bg-[radial-gradient(circle_at_top_left,rgba(168,85,247,0.32),rgba(168,85,247,0)_62%)]" />
|
|
|
|
<div className="px-5 py-5 sm:px-6">
|
|
|
|
|
|
|
|
<p className="text-[11px] font-medium tracking-[0.14em] text-brand-dark/42">
|
|
|
|
<div className="relative">
|
|
|
|
{review.periodLabel}
|
|
|
|
<div className="flex flex-wrap items-start justify-between gap-4">
|
|
|
|
|
|
|
|
<div className="max-w-[34rem]">
|
|
|
|
|
|
|
|
<p className="text-[10px] font-medium uppercase tracking-[0.2em] text-white/40">
|
|
|
|
|
|
|
|
Carry Forward
|
|
|
|
</p>
|
|
|
|
</p>
|
|
|
|
<h2 className="mt-2 text-[1.05rem] font-semibold tracking-tight text-brand-dark">
|
|
|
|
<h2 className="mt-3 text-[1.45rem] font-medium tracking-[-0.04em] text-white md:text-[1.8rem]">
|
|
|
|
{stats.reviewCarryKeepTitle}
|
|
|
|
다음 세션에 그대로 가져갈 흐름
|
|
|
|
</h2>
|
|
|
|
</h2>
|
|
|
|
<p className="mt-2 text-[14px] leading-[1.65] text-brand-dark/68">
|
|
|
|
<p className="mt-3 text-[14px] leading-[1.7] text-white/64">
|
|
|
|
{review.carryForward.keepDoing}
|
|
|
|
{review.carryForward.keepDoing}
|
|
|
|
</p>
|
|
|
|
</p>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="mt-5 border-t border-brand-dark/8 pt-5">
|
|
|
|
{isPro ? <StatusAccessory label="Recommended Ritual" subtle /> : null}
|
|
|
|
<h3 className="text-[1.02rem] font-semibold tracking-tight text-brand-dark">
|
|
|
|
</div>
|
|
|
|
{stats.reviewCarryTryTitle}
|
|
|
|
|
|
|
|
</h3>
|
|
|
|
<div className="mt-6 grid gap-4 lg:grid-cols-[minmax(0,1fr)_minmax(15rem,0.78fr)]">
|
|
|
|
<p className="mt-2 text-[14px] leading-[1.65] text-brand-dark/68">
|
|
|
|
<div className={metricTileClass}>
|
|
|
|
|
|
|
|
<p className="text-[10px] font-medium uppercase tracking-[0.18em] text-white/42">
|
|
|
|
|
|
|
|
다음 주에 바꿔볼 것
|
|
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<p className="mt-3 text-[14px] leading-[1.7] text-white/72">
|
|
|
|
{review.carryForward.tryNext}
|
|
|
|
{review.carryForward.tryNext}
|
|
|
|
</p>
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{isPro ? (
|
|
|
|
<div className={metricTileClass}>
|
|
|
|
<div className="mt-5 rounded-2xl border border-brand-dark/8 bg-white/56 px-4 py-3">
|
|
|
|
<p className="text-[10px] font-medium uppercase tracking-[0.18em] text-white/42">
|
|
|
|
<p className="text-[11px] font-medium tracking-[0.08em] text-brand-dark/42">
|
|
|
|
Atmosphere
|
|
|
|
{stats.reviewCarryPresetLabel}
|
|
|
|
|
|
|
|
</p>
|
|
|
|
</p>
|
|
|
|
<p className="mt-2 text-[13px] font-medium text-brand-dark/82">
|
|
|
|
<p className="mt-3 text-[14px] font-medium tracking-[-0.03em] text-white/88">
|
|
|
|
{review.carryForward.presetLabel}
|
|
|
|
{review.carryForward.presetLabel}
|
|
|
|
</p>
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<p className="mt-2 text-[12px] leading-[1.58] text-white/52">
|
|
|
|
|
|
|
|
지금 가장 무리 없이 다시 들어갈 수 있는 기본 흐름입니다.
|
|
|
|
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
) : null}
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div className="mt-6 flex flex-wrap items-center justify-between gap-4 border-t border-white/10 pt-5">
|
|
|
|
|
|
|
|
<p className="max-w-[30rem] text-[12px] leading-[1.65] text-white/44">
|
|
|
|
|
|
|
|
review는 지난 시간을 요약하는 화면이 아니라, 다음 세션을 더 가볍게 열기 위한
|
|
|
|
|
|
|
|
출발점입니다.
|
|
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
|
|
<Link
|
|
|
|
<Link
|
|
|
|
href={review.carryForward.ctaHref}
|
|
|
|
href={review.carryForward.ctaHref}
|
|
|
|
className="mt-6 inline-flex rounded-full border border-brand-dark/14 bg-brand-dark px-4 py-2.5 text-[12px] font-medium tracking-[0.04em] text-white transition hover:bg-brand-dark/92"
|
|
|
|
className="inline-flex min-h-[3.4rem] items-center justify-center rounded-full border border-white/14 bg-white/[0.14] px-6 text-[13px] font-medium tracking-[0.02em] text-white shadow-[0_12px_28px_rgba(3,7,18,0.22)] backdrop-blur-md transition hover:bg-white/[0.18] active:scale-[0.99]"
|
|
|
|
>
|
|
|
|
>
|
|
|
|
{carryForwardCtaLabel}
|
|
|
|
{carryForwardCtaLabel}
|
|
|
|
</Link>
|
|
|
|
</Link>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
</div>
|
|
|
|
</section>
|
|
|
|
</div>
|
|
|
|
</main>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|