152 lines
4.3 KiB
TypeScript
152 lines
4.3 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useRef, type CSSProperties } from "react";
|
|
import Link from "next/link";
|
|
import { Gift, Heart, Sparkles } from "lucide-react";
|
|
|
|
import { CharacterAvatar } from "@/app/_components";
|
|
import { MobileShell } from "@/app/_components/core";
|
|
|
|
import { TipProductImage } from "./tip-product-image";
|
|
import styles from "./tip-success-view.module.css";
|
|
|
|
const GENERIC_TIP_SUCCESS_MESSAGE =
|
|
"Thank you. Your gift made me smile.";
|
|
|
|
export interface TipSuccessViewProps {
|
|
characterName: string;
|
|
characterAvatar: string;
|
|
characterCover: string;
|
|
giftImageSources: readonly string[];
|
|
giftName: string;
|
|
isMessageLoading: boolean;
|
|
message: string | null;
|
|
messageError: string | null;
|
|
splashHref: string;
|
|
onRetryMessage: () => void;
|
|
onSendAgain: () => void;
|
|
}
|
|
|
|
export function TipSuccessView({
|
|
characterName,
|
|
characterAvatar,
|
|
characterCover,
|
|
giftImageSources,
|
|
giftName,
|
|
isMessageLoading,
|
|
message,
|
|
messageError,
|
|
splashHref,
|
|
onRetryMessage,
|
|
onSendAgain,
|
|
}: TipSuccessViewProps) {
|
|
const titleRef = useRef<HTMLHeadingElement>(null);
|
|
|
|
useEffect(() => {
|
|
titleRef.current?.focus({ preventScroll: true });
|
|
}, []);
|
|
|
|
return (
|
|
<MobileShell background="#fff7f0">
|
|
<main
|
|
className={styles.shell}
|
|
style={
|
|
{
|
|
"--tip-success-cover": `url("${characterCover}")`,
|
|
} as CSSProperties
|
|
}
|
|
>
|
|
<div className={styles.coverWash} aria-hidden="true" />
|
|
<div className={styles.glowOne} aria-hidden="true" />
|
|
<div className={styles.glowTwo} aria-hidden="true" />
|
|
|
|
<section
|
|
className={styles.card}
|
|
aria-live="polite"
|
|
aria-labelledby="tip-success-title"
|
|
>
|
|
<div className={styles.visual}>
|
|
<span className={styles.sparkleOne} aria-hidden="true">
|
|
<Sparkles size={22} strokeWidth={1.8} />
|
|
</span>
|
|
<span className={styles.sparkleTwo} aria-hidden="true">
|
|
<Heart size={17} fill="currentColor" strokeWidth={1.8} />
|
|
</span>
|
|
<div className={styles.avatarFrame}>
|
|
<CharacterAvatar
|
|
src={characterAvatar}
|
|
alt={characterName}
|
|
size="100%"
|
|
imageSize={112}
|
|
priority
|
|
/>
|
|
</div>
|
|
<div className={styles.coffeeFrame}>
|
|
<TipProductImage
|
|
key={giftName}
|
|
sources={giftImageSources}
|
|
alt={giftName}
|
|
className={styles.coffeeImage}
|
|
priority
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<p className={styles.eyebrow}>
|
|
<Gift size={15} aria-hidden="true" />
|
|
Gift received
|
|
</p>
|
|
<h1
|
|
ref={titleRef}
|
|
id="tip-success-title"
|
|
className={styles.title}
|
|
tabIndex={-1}
|
|
>
|
|
You made {characterName} smile.
|
|
</h1>
|
|
<div className={styles.copy}>
|
|
<p>
|
|
{message ??
|
|
(isMessageLoading
|
|
? "Your gift arrived. A thank-you note is on its way."
|
|
: GENERIC_TIP_SUCCESS_MESSAGE)}
|
|
</p>
|
|
</div>
|
|
|
|
{messageError ? (
|
|
<div className={styles.messageRetry}>
|
|
<p>We could not load the personal thank-you note.</p>
|
|
<button
|
|
type="button"
|
|
className={styles.retryAction}
|
|
disabled={isMessageLoading}
|
|
onClick={onRetryMessage}
|
|
>
|
|
{isMessageLoading ? "Retrying..." : "Retry message"}
|
|
</button>
|
|
</div>
|
|
) : null}
|
|
|
|
<div className={styles.actions}>
|
|
<button
|
|
type="button"
|
|
className={styles.primaryAction}
|
|
data-analytics-key="tip.send_again"
|
|
onClick={onSendAgain}
|
|
>
|
|
Send another gift
|
|
</button>
|
|
<Link
|
|
href={splashHref}
|
|
className={styles.secondaryAction}
|
|
data-analytics-key="tip.success_back_to_splash"
|
|
>
|
|
Back to {characterName}
|
|
</Link>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
</MobileShell>
|
|
);
|
|
}
|