feat(settings/profile): move share to per-product, profile share on card header

- Each product row now has an ↗ share button (product URL shared, not profile)
  WhatsApp message: "Found this for our baby — [title]: [url]"
- Only one product share sheet open at a time; closes on backdrop tap
- Profile page share (↗) moved to the profile card header row where it
  contextually belongs — shares the /m/slug link, not a product
- Removed the share button that was on the Products section header

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Manohar Gupta 2026-05-23 19:40:51 +05:30
parent 8598259fb1
commit fb402e9898

View file

@ -34,7 +34,8 @@ export default function ProfileSettingsPage() {
const [saving, setSaving] = useState(false);
const [saveMsg, setSaveMsg] = useState("");
const [profileExpanded, setProfileExpanded] = useState(true);
const [showShareSheet, setShowShareSheet] = useState(false);
const [shareProductId, setShareProductId] = useState<string | null>(null);
const [profileShareOpen, setProfileShareOpen] = useState(false);
const [copied, setCopied] = useState(false);
const [slug, setSlug] = useState("");
@ -89,21 +90,20 @@ export default function ProfileSettingsPage() {
setSaving(false);
}
async function copyLink() {
if (!profileUrl) return;
await navigator.clipboard.writeText(profileUrl);
async function copyLink(url: string) {
await navigator.clipboard.writeText(url);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
function shareViaWhatsApp() {
const text = encodeURIComponent(`Check out my baby product recommendations! ${profileUrl}`);
window.open(`https://wa.me/?text=${text}`, "_blank");
function shareViaWhatsApp(url: string, text: string) {
const msg = encodeURIComponent(`${text} ${url}`);
window.open(`https://wa.me/?text=${msg}`, "_blank");
}
async function shareNative() {
async function shareNative(title: string, url: string) {
if (navigator.share) {
await navigator.share({ title: `${displayName}'s baby picks`, url: profileUrl });
await navigator.share({ title, url });
}
}
@ -190,13 +190,13 @@ export default function ProfileSettingsPage() {
{/* Profile section — collapsible */}
<div className="bg-white dark:bg-gray-800 rounded-2xl shadow-sm overflow-hidden">
{/* Always-visible header row */}
<div className="flex items-center justify-between px-4 py-3">
<button
onClick={() => setProfileExpanded(v => !v)}
className="w-full flex items-center justify-between p-4"
className="flex items-center gap-3 flex-1 text-left"
>
<div className="flex items-center gap-3">
<span className="text-xl">👤</span>
<div className="text-left">
<div>
<p className="font-semibold text-gray-800 dark:text-white text-sm">
{displayName || "Set up your profile"}
</p>
@ -204,9 +204,50 @@ export default function ProfileSettingsPage() {
<p className="text-xs text-gray-400">{baseUrl}/m/{slug}</p>
)}
</div>
</div>
<span className={`text-gray-400 text-sm transition-transform ${profileExpanded ? "rotate-180" : ""}`}></span>
</button>
<div className="flex items-center gap-2">
{/* Profile page share — only when slug is set */}
{slug && slugValid && (
<div className="relative">
<button
onClick={e => { e.stopPropagation(); setProfileShareOpen(v => !v); }}
className="p-2 rounded-xl bg-rose-50 dark:bg-rose-900/20 text-rose-500 text-sm"
title="Share your profile page"
>
</button>
{profileShareOpen && (
<>
<div className="fixed inset-0 z-30" onClick={() => setProfileShareOpen(false)} />
<div className="absolute right-0 top-9 z-40 bg-white dark:bg-gray-800 rounded-2xl shadow-xl border border-gray-100 dark:border-gray-700 p-3 w-52 space-y-1">
<p className="text-xs text-gray-400 px-2 pb-1">Share your profile page</p>
<button onClick={() => { copyLink(profileUrl); setProfileShareOpen(false); }}
className="w-full flex items-center gap-3 px-3 py-2.5 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-700 text-sm text-gray-700 dark:text-gray-200">
<span className="text-lg">{copied ? "✅" : "📋"}</span>
{copied ? "Copied!" : "Copy link"}
</button>
<button onClick={() => { shareViaWhatsApp(profileUrl, "Check out my baby product recommendations!"); setProfileShareOpen(false); }}
className="w-full flex items-center gap-3 px-3 py-2.5 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-700 text-sm text-gray-700 dark:text-gray-200">
<span className="text-lg">💬</span>
WhatsApp
</button>
{typeof navigator !== "undefined" && "share" in navigator && (
<button onClick={() => { shareNative(`${displayName}'s baby picks`, profileUrl); setProfileShareOpen(false); }}
className="w-full flex items-center gap-3 px-3 py-2.5 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-700 text-sm text-gray-700 dark:text-gray-200">
<span className="text-lg">📤</span>
More options
</button>
)}
</div>
</>
)}
</div>
)}
<button onClick={() => setProfileExpanded(v => !v)} className="p-2 text-gray-400 text-sm">
<span className={`inline-block transition-transform ${profileExpanded ? "rotate-180" : ""}`}></span>
</button>
</div>
</div>
{/* Expanded form */}
{profileExpanded && (
@ -281,51 +322,6 @@ export default function ProfileSettingsPage() {
<div className="bg-white dark:bg-gray-800 rounded-2xl p-4 shadow-sm">
<div className="flex items-center justify-between mb-4">
<h2 className="font-semibold text-gray-800 dark:text-white">Product Recommendations</h2>
<div className="flex items-center gap-2">
{/* Share button — only visible when profile has a slug */}
{slug && slugValid && (
<div className="relative">
<button
onClick={() => setShowShareSheet(v => !v)}
className="flex items-center gap-1 px-2.5 py-1.5 rounded-xl bg-rose-50 dark:bg-rose-900/20 text-rose-500 text-sm font-medium"
>
<span></span>
<span>Share</span>
</button>
{showShareSheet && (
<>
<div className="fixed inset-0 z-30" onClick={() => setShowShareSheet(false)} />
<div className="absolute right-0 top-9 z-40 bg-white dark:bg-gray-800 rounded-2xl shadow-xl border border-gray-100 dark:border-gray-700 p-3 w-52 space-y-1">
<p className="text-xs text-gray-400 px-2 pb-1">Share your page</p>
<button
onClick={() => { copyLink(); setShowShareSheet(false); }}
className="w-full flex items-center gap-3 px-3 py-2.5 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-700 text-sm text-gray-700 dark:text-gray-200"
>
<span className="text-lg">{copied ? "✅" : "📋"}</span>
{copied ? "Copied!" : "Copy link"}
</button>
<button
onClick={() => { shareViaWhatsApp(); setShowShareSheet(false); }}
className="w-full flex items-center gap-3 px-3 py-2.5 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-700 text-sm text-gray-700 dark:text-gray-200"
>
<span className="text-lg">💬</span>
WhatsApp
</button>
{typeof navigator !== "undefined" && "share" in navigator && (
<button
onClick={() => { shareNative(); setShowShareSheet(false); }}
className="w-full flex items-center gap-3 px-3 py-2.5 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-700 text-sm text-gray-700 dark:text-gray-200"
>
<span className="text-lg">📤</span>
More options
</button>
)}
</div>
</>
)}
</div>
)}
<button
onClick={() => { resetProductForm(); setShowAddProduct(true); }}
className="text-sm text-rose-500 font-medium px-2.5 py-1.5"
@ -333,7 +329,6 @@ export default function ProfileSettingsPage() {
+ Add
</button>
</div>
</div>
{/* Add/Edit form */}
{showAddProduct && (
@ -373,7 +368,8 @@ export default function ProfileSettingsPage() {
<div className="space-y-2">
{products.map((p, i) => (
<div key={p.id} className="flex items-center gap-3 p-3 rounded-xl bg-gray-50 dark:bg-gray-700/50">
<div key={p.id} className="rounded-xl bg-gray-50 dark:bg-gray-700/50 overflow-visible">
<div className="flex items-center gap-3 p-3">
<div className="text-2xl">{CATEGORY_EMOJI[p.category] || "🛍️"}</div>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-gray-800 dark:text-white truncate">{p.title}</p>
@ -385,9 +381,45 @@ export default function ProfileSettingsPage() {
<button onClick={() => moveProduct(p.id, "down")} disabled={i === products.length - 1}
className="text-gray-400 disabled:opacity-30 text-xs px-1.5 py-1"></button>
<button onClick={() => startEdit(p)} className="text-rose-400 text-xs px-1.5 py-1">Edit</button>
{/* Per-product share */}
<div className="relative">
<button
onClick={() => setShareProductId(id => id === p.id ? null : p.id)}
className="text-rose-400 text-xs px-1.5 py-1"
title="Share this product"
>
</button>
{shareProductId === p.id && (
<>
<div className="fixed inset-0 z-30" onClick={() => setShareProductId(null)} />
<div className="absolute right-0 bottom-8 z-40 bg-white dark:bg-gray-800 rounded-2xl shadow-xl border border-gray-100 dark:border-gray-700 p-3 w-52 space-y-1">
<p className="text-xs text-gray-400 px-2 pb-1 truncate">{p.title}</p>
<button onClick={() => { copyLink(p.url); setShareProductId(null); }}
className="w-full flex items-center gap-3 px-3 py-2.5 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-700 text-sm text-gray-700 dark:text-gray-200">
<span className="text-lg">{copied ? "✅" : "📋"}</span>
{copied ? "Copied!" : "Copy link"}
</button>
<button onClick={() => { shareViaWhatsApp(p.url, `Found this for our baby — ${p.title}:`); setShareProductId(null); }}
className="w-full flex items-center gap-3 px-3 py-2.5 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-700 text-sm text-gray-700 dark:text-gray-200">
<span className="text-lg">💬</span>
WhatsApp
</button>
{typeof navigator !== "undefined" && "share" in navigator && (
<button onClick={() => { shareNative(p.title, p.url); setShareProductId(null); }}
className="w-full flex items-center gap-3 px-3 py-2.5 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-700 text-sm text-gray-700 dark:text-gray-200">
<span className="text-lg">📤</span>
More options
</button>
)}
</div>
</>
)}
</div>
<button onClick={() => deleteProduct(p.id)} className="text-gray-400 text-xs px-1.5 py-1"></button>
</div>
</div>
</div>
))}
</div>
</div>