fix(settings/profile): align My Profile page with app theme

- Background: flat gray → rose-to-amber gradient (matches all other pages)
- Header: add back button with rounded-xl pill style + xl font title
- Buttons: bg-pink-500 → bg-rose-400 throughout
- Loading: spinner → branded emoji bounce (consistent with home screen)
- Toggle: checkbox → styled toggle switch matching app UI language
- Layout: remove desktop max-w-lg wrapper; full-width mobile layout
- Input styling: unified inputClass with focus ring + consistent padding
- Empty state: plain text → emoji + two-line message
- Product rows: border div → bg-gray-50 pill card (matches wardrobe style)
- Add pb-24 so content clears the bottom nav bar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Manohar Gupta 2026-05-23 19:26:38 +05:30
parent 3d0e6ed46c
commit 3b62841bd4

View file

@ -1,5 +1,7 @@
"use client";
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
interface Profile {
id: string;
@ -25,19 +27,18 @@ const CATEGORIES = ["general", "feeding", "sleep", "play", "clothing"] as const;
const CATEGORY_EMOJI: Record<string, string> = { feeding: "🍼", sleep: "💤", play: "🎮", clothing: "👗", general: "🛍️" };
export default function ProfileSettingsPage() {
const router = useRouter();
const [profile, setProfile] = useState<Profile | null>(null);
const [products, setProducts] = useState<Product[]>([]);
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const [saveMsg, setSaveMsg] = useState("");
// form state
const [slug, setSlug] = useState("");
const [displayName, setDisplayName] = useState("");
const [bio, setBio] = useState("");
const [isPublic, setIsPublic] = useState(false);
// product form
const [showAddProduct, setShowAddProduct] = useState(false);
const [editingProduct, setEditingProduct] = useState<Product | null>(null);
const [pTitle, setPTitle] = useState("");
@ -139,36 +140,47 @@ export default function ProfileSettingsPage() {
const slugValid = /^[a-z0-9-]{3,40}$/.test(slug);
const baseUrl = typeof window !== "undefined" ? window.location.origin : "";
const inputClass = "w-full border border-gray-200 dark:border-gray-600 rounded-xl px-3 py-2.5 text-sm bg-white dark:bg-gray-700 dark:text-white focus:outline-none focus:ring-2 focus:ring-rose-300";
if (loading) return (
<div className="flex items-center justify-center min-h-screen">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-pink-400" />
<div className="min-h-screen flex flex-col items-center justify-center gap-4 bg-gradient-to-br from-rose-50 to-amber-50 dark:from-gray-900 dark:to-gray-800">
<div className="flex gap-3 text-4xl">
{["🍼", "😴", "🚼", "👶"].map((e, i) => (
<span key={i} className="animate-bounce" style={{ animationDelay: `${i * 120}ms` }}>{e}</span>
))}
</div>
<p className="text-sm text-gray-400">Loading profile</p>
</div>
);
return (
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 pb-20">
<div className="max-w-lg mx-auto px-4 py-6">
<h1 className="text-2xl font-bold text-gray-900 dark:text-white mb-6">My Profile Page</h1>
<div className="min-h-screen bg-gradient-to-br from-rose-50 to-amber-50 dark:from-gray-900 dark:to-gray-800 pb-24">
{/* Header */}
<div className="flex items-center gap-3 p-4">
<button onClick={() => router.back()} className="p-2 rounded-xl bg-white dark:bg-gray-800 shadow-sm text-xl"></button>
<h1 className="text-xl font-bold">My Profile Page</h1>
</div>
<div className="px-4 space-y-4">
{/* Profile section */}
<div className="bg-white dark:bg-gray-800 rounded-2xl p-5 mb-4 shadow-sm">
<div className="bg-white dark:bg-gray-800 rounded-2xl p-4 shadow-sm">
<h2 className="font-semibold text-gray-800 dark:text-white mb-4">Profile</h2>
<label className="block text-sm text-gray-600 dark:text-gray-400 mb-1">Display Name</label>
<label className="block text-sm text-gray-500 dark:text-gray-400 mb-1">Display Name</label>
<input
value={displayName}
onChange={e => setDisplayName(e.target.value)}
className="w-full border dark:border-gray-600 rounded-xl px-3 py-2 text-sm dark:bg-gray-700 dark:text-white mb-3"
className={`${inputClass} mb-3`}
placeholder="Priya Sharma"
/>
<label className="block text-sm text-gray-600 dark:text-gray-400 mb-1">Your Page URL</label>
<div className="flex items-center gap-1 border dark:border-gray-600 rounded-xl px-3 py-2 text-sm mb-1 dark:bg-gray-700">
<span className="text-gray-400 text-xs">{baseUrl}/m/</span>
<label className="block text-sm text-gray-500 dark:text-gray-400 mb-1">Your Page URL</label>
<div className="flex items-center gap-1 border border-gray-200 dark:border-gray-600 rounded-xl px-3 py-2.5 text-sm mb-1 bg-white dark:bg-gray-700 focus-within:ring-2 focus-within:ring-rose-300">
<span className="text-gray-400 text-xs whitespace-nowrap">{baseUrl}/m/</span>
<input
value={slug}
onChange={e => setSlug(e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, ""))}
className="flex-1 bg-transparent outline-none dark:text-white"
className="flex-1 bg-transparent outline-none dark:text-white text-sm"
placeholder="priya-sharma"
/>
</div>
@ -176,47 +188,49 @@ export default function ProfileSettingsPage() {
<p className="text-xs text-red-500 mb-2">340 chars, lowercase letters, numbers, hyphens only</p>
)}
<label className="block text-sm text-gray-600 dark:text-gray-400 mb-1 mt-3">Bio <span className="text-gray-400">({bio.length}/200)</span></label>
<label className="block text-sm text-gray-500 dark:text-gray-400 mb-1 mt-3">
Bio <span className="text-gray-400">({bio.length}/200)</span>
</label>
<textarea
value={bio}
onChange={e => setBio(e.target.value.slice(0, 200))}
rows={3}
className="w-full border dark:border-gray-600 rounded-xl px-3 py-2 text-sm dark:bg-gray-700 dark:text-white mb-3 resize-none"
className={`${inputClass} mb-3 resize-none`}
placeholder="New mama sharing what works for us 💕"
/>
<label className="flex items-center gap-3 cursor-pointer mb-4">
<input
type="checkbox"
checked={isPublic}
onChange={e => setIsPublic(e.target.checked)}
className="w-4 h-4 accent-pink-500"
/>
<div
onClick={() => setIsPublic(v => !v)}
className={`w-10 h-6 rounded-full transition-colors relative cursor-pointer ${isPublic ? "bg-rose-400" : "bg-gray-300 dark:bg-gray-600"}`}
>
<div className={`absolute top-1 w-4 h-4 bg-white rounded-full shadow transition-transform ${isPublic ? "translate-x-5" : "translate-x-1"}`} />
</div>
<span className="text-sm text-gray-700 dark:text-gray-300">Make my page public</span>
</label>
<button
onClick={saveProfile}
disabled={saving || !displayName || !slugValid}
className="w-full bg-pink-500 text-white rounded-xl py-2.5 font-medium text-sm disabled:opacity-50"
className="w-full bg-rose-400 text-white rounded-xl py-2.5 font-medium text-sm disabled:opacity-50 active:scale-95 transition-transform"
>
{saving ? "Saving..." : "Save Profile"}
{saving ? "Saving" : "Save Profile"}
</button>
{saveMsg && (
<p className={`text-xs mt-2 text-center ${saveMsg.startsWith("Saved") ? "text-green-600" : "text-red-500"}`}>
<p className={`text-xs mt-2 text-center ${saveMsg.startsWith("Saved") ? "text-green-600 dark:text-green-400" : "text-red-500"}`}>
{saveMsg}
</p>
)}
</div>
{/* Products section */}
<div className="bg-white dark:bg-gray-800 rounded-2xl p-5 shadow-sm">
<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>
<button
onClick={() => { resetProductForm(); setShowAddProduct(true); }}
className="text-sm text-pink-500 font-medium"
className="text-sm text-rose-500 font-medium"
>
+ Add
</button>
@ -224,26 +238,26 @@ export default function ProfileSettingsPage() {
{/* Add/Edit form */}
{showAddProduct && (
<div className="bg-gray-50 dark:bg-gray-700/50 rounded-xl p-4 mb-4">
<div className="bg-gray-50 dark:bg-gray-700/50 rounded-xl p-4 mb-4 space-y-2">
<input value={pTitle} onChange={e => setPTitle(e.target.value)}
placeholder="Product title *" className="w-full border dark:border-gray-600 rounded-lg px-3 py-2 text-sm dark:bg-gray-700 dark:text-white mb-2" />
placeholder="Product title *" className={inputClass} />
<input value={pUrl} onChange={e => setPUrl(e.target.value)}
placeholder="Product URL *" className="w-full border dark:border-gray-600 rounded-lg px-3 py-2 text-sm dark:bg-gray-700 dark:text-white mb-2" />
placeholder="Product URL *" className={inputClass} />
<textarea value={pDesc} onChange={e => setPDesc(e.target.value)} rows={2}
placeholder="Description (optional)" className="w-full border dark:border-gray-600 rounded-lg px-3 py-2 text-sm dark:bg-gray-700 dark:text-white mb-2 resize-none" />
placeholder="Description (optional)" className={`${inputClass} resize-none`} />
<input value={pImageUrl} onChange={e => setPImageUrl(e.target.value)}
placeholder="Image URL (optional)" className="w-full border dark:border-gray-600 rounded-lg px-3 py-2 text-sm dark:bg-gray-700 dark:text-white mb-2" />
placeholder="Image URL (optional)" className={inputClass} />
<select value={pCategory} onChange={e => setPCategory(e.target.value)}
className="w-full border dark:border-gray-600 rounded-lg px-3 py-2 text-sm dark:bg-gray-700 dark:text-white mb-3">
className={inputClass}>
{CATEGORIES.map(c => <option key={c} value={c}>{CATEGORY_EMOJI[c]} {c.charAt(0).toUpperCase() + c.slice(1)}</option>)}
</select>
<div className="flex gap-2">
<div className="flex gap-2 pt-1">
<button onClick={saveProduct} disabled={!pTitle || !pUrl}
className="flex-1 bg-pink-500 text-white rounded-lg py-2 text-sm font-medium disabled:opacity-50">
className="flex-1 bg-rose-400 text-white rounded-xl py-2.5 text-sm font-medium disabled:opacity-50">
{editingProduct ? "Update" : "Add Product"}
</button>
<button onClick={resetProductForm}
className="flex-1 border dark:border-gray-600 rounded-lg py-2 text-sm text-gray-600 dark:text-gray-300">
className="flex-1 border border-gray-200 dark:border-gray-600 rounded-xl py-2.5 text-sm text-gray-600 dark:text-gray-300">
Cancel
</button>
</div>
@ -251,12 +265,16 @@ export default function ProfileSettingsPage() {
)}
{products.length === 0 && !showAddProduct && (
<p className="text-sm text-gray-400 text-center py-4">No products yet. Add your first recommendation!</p>
<div className="text-center py-8">
<span className="text-4xl">🛍</span>
<p className="text-sm text-gray-400 mt-2">No products yet.</p>
<p className="text-xs text-gray-400">Add your first recommendation!</p>
</div>
)}
<div className="space-y-2">
{products.map((p, i) => (
<div key={p.id} className="flex items-center gap-3 p-3 rounded-xl border dark:border-gray-700">
<div key={p.id} className="flex items-center gap-3 p-3 rounded-xl bg-gray-50 dark:bg-gray-700/50">
<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>
@ -264,11 +282,11 @@ export default function ProfileSettingsPage() {
</div>
<div className="flex items-center gap-1">
<button onClick={() => moveProduct(p.id, "up")} disabled={i === 0}
className="text-gray-400 hover:text-gray-600 disabled:opacity-30 text-xs px-1"></button>
className="text-gray-400 disabled:opacity-30 text-xs px-1.5 py-1"></button>
<button onClick={() => moveProduct(p.id, "down")} disabled={i === products.length - 1}
className="text-gray-400 hover:text-gray-600 disabled:opacity-30 text-xs px-1"></button>
<button onClick={() => startEdit(p)} className="text-blue-400 hover:text-blue-600 text-xs px-1">Edit</button>
<button onClick={() => deleteProduct(p.id)} className="text-red-400 hover:text-red-600 text-xs px-1"></button>
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>
<button onClick={() => deleteProduct(p.id)} className="text-gray-400 text-xs px-1.5 py-1"></button>
</div>
</div>
))}