Consolidate settings and add familyName to FamilyProvider

- Settings: Merge Family Settings + Family + Family Members into one Family section
- Add familyName to FamilyProvider from session API
- Show family name, plan, members count, children count in one place
- Show members list with roles
- Remove redundant sections

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Manohar Gupta 2026-05-11 00:48:00 +05:30
parent add108c920
commit 31ac12b418
2 changed files with 56 additions and 66 deletions

View file

@ -13,6 +13,7 @@ interface Child {
interface FamilyContextType { interface FamilyContextType {
familyId: string | null; familyId: string | null;
familyName: string | null;
childId: string | null; childId: string | null;
child: Child | null; child: Child | null;
children: Child[]; children: Child[];
@ -23,6 +24,7 @@ interface FamilyContextType {
const FamilyContext = createContext<FamilyContextType>({ const FamilyContext = createContext<FamilyContextType>({
familyId: null, familyId: null,
familyName: null,
childId: null, childId: null,
child: null, child: null,
children: [], children: [],
@ -38,6 +40,7 @@ export function useFamily() {
export function FamilyProvider({ children: providerChildren }: { children: ReactNode }) { export function FamilyProvider({ children: providerChildren }: { children: ReactNode }) {
const router = useRouter(); const router = useRouter();
const [familyId, setFamilyId] = useState<string | null>(null); const [familyId, setFamilyId] = useState<string | null>(null);
const [familyName, setFamilyName] = useState<string | null>(null);
const [childId, setChildId] = useState<string | null>(null); const [childId, setChildId] = useState<string | null>(null);
const [child, setChild] = useState<Child | null>(null); const [child, setChild] = useState<Child | null>(null);
const [children, setChildren] = useState<Child[]>([]); const [children, setChildren] = useState<Child[]>([]);
@ -96,6 +99,7 @@ export function FamilyProvider({ children: providerChildren }: { children: React
} }
setFamilyId(familyId); setFamilyId(familyId);
setFamilyName(sessionData.familyName || "My Family");
setTier(sessionData.tier || "free"); setTier(sessionData.tier || "free");
setMemberCount(2); setMemberCount(2);
} catch (err) { } catch (err) {
@ -112,6 +116,7 @@ export function FamilyProvider({ children: providerChildren }: { children: React
<FamilyContext.Provider <FamilyContext.Provider
value={{ value={{
familyId, familyId,
familyName,
childId, childId,
child, child,
children, children,

View file

@ -26,22 +26,22 @@ interface Invite {
export default function SettingsPage() { export default function SettingsPage() {
const router = useRouter(); const router = useRouter();
const { theme, mode, setMode } = useTheme(); const { theme, mode, setMode } = useTheme();
const { tier, memberCount, familyId } = useFamily(); const { tier, memberCount, familyId, children, familyName: providerFamilyName } = useFamily();
const [themeOpen, setThemeOpen] = useState(false); const [themeOpen, setThemeOpen] = useState(false);
const [inviteOpen, setInviteOpen] = useState(false); const [inviteOpen, setInviteOpen] = useState(false);
const [membersOpen, setMembersOpen] = useState(false); const [familyOpen, setFamilyOpen] = useState(true);
const [familyOpen, setFamilyOpen] = useState(false);
const [members, setMembers] = useState<Member[]>([]); const [members, setMembers] = useState<Member[]>([]);
const [invites, setInvites] = useState<Invite[]>([]); const [invites, setInvites] = useState<Invite[]>([]);
const [showAddInvite, setShowAddInvite] = useState(false);
const [inviteEmail, setInviteEmail] = useState(""); const [inviteEmail, setInviteEmail] = useState("");
const [inviteRole, setInviteRole] = useState("caregiver"); const [inviteRole, setInviteRole] = useState("caregiver");
const [inviteLoading, setInviteLoading] = useState(false); const [inviteLoading, setInviteLoading] = useState(false);
const [familyName, setFamilyName] = useState("");
// Check if can invite more members // Check if can invite more members
const canInvite = tier === "pro" || memberCount < 2; const canInvite = tier === "pro" || memberCount < 2;
// Family name from provider or fallback
const familyName = providerFamilyName || "My Family";
const themeOptions = [ const themeOptions = [
{ value: "light", label: "Light" }, { value: "light", label: "Light" },
{ value: "dark", label: "Dark" }, { value: "dark", label: "Dark" },
@ -50,8 +50,10 @@ export default function SettingsPage() {
] as const; ] as const;
useEffect(() => { useEffect(() => {
fetchInvites(); if (familyId) {
fetchMembers(); fetchMembers();
fetchInvites();
}
}, [familyId]); }, [familyId]);
const fetchMembers = async () => { const fetchMembers = async () => {
@ -129,7 +131,7 @@ export default function SettingsPage() {
<span className="text-gray-400"></span> <span className="text-gray-400"></span>
</Link> </Link>
{/* Family - Collapsible */} {/* Family - Single consolidated section */}
<div className="bg-white dark:bg-gray-800 rounded-xl overflow-hidden"> <div className="bg-white dark:bg-gray-800 rounded-xl overflow-hidden">
<button <button
onClick={() => setFamilyOpen(!familyOpen)} onClick={() => setFamilyOpen(!familyOpen)}
@ -137,38 +139,58 @@ export default function SettingsPage() {
> >
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<span className="text-xl">🏠</span> <span className="text-xl">🏠</span>
<div className="font-medium">Family Settings</div> <div className="font-medium">Family</div>
</div> </div>
<span className={`text-gray-400 transition-transform ${familyOpen ? "rotate-180" : ""}`}></span> <span className={`text-gray-400 transition-transform ${familyOpen ? "rotate-180" : ""}`}></span>
</button> </button>
{familyOpen && ( {familyOpen && (
<div className="px-4 pb-4 space-y-3"> <div className="px-4 pb-4 space-y-4">
<input {/* Family Info */}
type="text"
value={familyName}
onChange={(e) => setFamilyName(e.target.value)}
placeholder="Family name"
className="w-full p-2 border rounded-lg text-sm"
/>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span className="text-sm text-gray-500">Plan: {tier}</span> <div>
<div className="font-medium text-lg">{familyName}</div>
<div className="text-sm text-gray-500">
{tier === "pro" ? "Pro Plan" : "Free Plan"} · {members.length} member{members.length !== 1 ? "s" : ""} · {children?.length || 0} child{children?.length !== 1 ? "ren" : ""}
</div>
</div>
{tier === "free" && ( {tier === "free" && (
<button className="text-sm text-rose-500">Upgrade to Pro</button> <button className="text-sm px-3 py-1 bg-rose-400 text-white rounded-full">Upgrade</button>
)} )}
</div> </div>
{/* Family Members */}
<div>
<div className="text-sm font-medium text-gray-500 mb-2">Family Members</div>
{members.length > 0 ? (
<div className="space-y-2">
{members.map((member) => (
<div key={member.id} className="flex items-center justify-between p-2 bg-gray-50 dark:bg-gray-700 rounded">
<div>
<div className="font-medium text-sm">{member.displayName || member.name || member.email}</div>
<div className="text-xs text-gray-400">{member.email}</div>
</div> </div>
<span className={`text-xs px-2 py-1 rounded ${member.role === "admin" ? "bg-rose-100 text-rose-600" : "bg-gray-100 dark:bg-gray-600"}`}>
{member.role}
</span>
</div>
))}
</div>
) : (
<p className="text-gray-400 text-sm">No family members</p>
)} )}
</div> </div>
<Link href="/family" className="flex items-center justify-between p-4 bg-white dark:bg-gray-800 rounded-xl">
<div className="flex items-center gap-3"> {/* Manage Children */}
<span className="text-xl">👨👩👧</span> <Link href="/family" className="flex items-center justify-between p-3 border border-dashed border-gray-300 dark:border-gray-600 rounded-lg">
<div className="font-medium">Family</div> <span className="text-sm">Manage Children</span>
</div>
<span className="text-gray-400"></span> <span className="text-gray-400"></span>
</Link> </Link>
</div>
)}
</div>
{/* Invite Members - Collapsible */} {/* Invite Members */}
<div className="bg-white dark:bg-gray-800 rounded-xl overflow-hidden"> <div className="bg-white dark:bg-gray-800 rounded-xl overflow-hidden">
<button <button
onClick={() => setInviteOpen(!inviteOpen)} onClick={() => setInviteOpen(!inviteOpen)}
@ -178,7 +200,7 @@ export default function SettingsPage() {
<span className="text-xl"></span> <span className="text-xl"></span>
<div className="font-medium">Invite Members</div> <div className="font-medium">Invite Members</div>
{tier === "free" && ( {tier === "free" && (
<span className="text-xs px-2 py-0.5 bg-rose-100 text-rose-600 rounded-full">Free: {memberCount}/2</span> <span className="text-xs px-2 py-0.5 bg-rose-100 text-rose-600 rounded-full">{memberCount}/2</span>
)} )}
</div> </div>
<span className={`text-gray-400 transition-transform ${inviteOpen ? "rotate-180" : ""}`}></span> <span className={`text-gray-400 transition-transform ${inviteOpen ? "rotate-180" : ""}`}></span>
@ -237,44 +259,7 @@ export default function SettingsPage() {
)} )}
</div> </div>
{/* Family Members - Collapsible */} {/* Theme */}
<div className="bg-white dark:bg-gray-800 rounded-xl overflow-hidden">
<button
onClick={() => setMembersOpen(!membersOpen)}
className="w-full flex items-center justify-between p-4"
>
<div className="flex items-center gap-3">
<span className="text-xl">👥</span>
<div className="font-medium">Family Members</div>
<span className="text-xs text-gray-400">({members.length})</span>
</div>
<span className={`text-gray-400 transition-transform ${membersOpen ? "rotate-180" : ""}`}></span>
</button>
{membersOpen && (
<div className="px-4 pb-4">
{members.length > 0 ? (
<div className="space-y-2">
{members.map((member) => (
<div key={member.id} className="flex items-center justify-between p-2 bg-gray-50 rounded">
<div>
<div className="font-medium text-sm">{member.displayName || member.name}</div>
<div className="text-xs text-gray-400">{member.email}</div>
</div>
<span className={`text-xs px-2 py-1 rounded ${member.role === "admin" ? "bg-rose-100 text-rose-600" : "bg-gray-100"}`}>
{member.role}
</span>
</div>
))}
</div>
) : (
<p className="text-gray-500 text-sm">No members yet</p>
)}
</div>
)}
</div>
{/* Theme - Collapsible */}
<div className="bg-white dark:bg-gray-800 rounded-xl overflow-hidden"> <div className="bg-white dark:bg-gray-800 rounded-xl overflow-hidden">
<button <button
onClick={() => setThemeOpen(!themeOpen)} onClick={() => setThemeOpen(!themeOpen)}