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:
parent
add108c920
commit
31ac12b418
2 changed files with 56 additions and 66 deletions
|
|
@ -13,6 +13,7 @@ interface Child {
|
|||
|
||||
interface FamilyContextType {
|
||||
familyId: string | null;
|
||||
familyName: string | null;
|
||||
childId: string | null;
|
||||
child: Child | null;
|
||||
children: Child[];
|
||||
|
|
@ -23,6 +24,7 @@ interface FamilyContextType {
|
|||
|
||||
const FamilyContext = createContext<FamilyContextType>({
|
||||
familyId: null,
|
||||
familyName: null,
|
||||
childId: null,
|
||||
child: null,
|
||||
children: [],
|
||||
|
|
@ -38,6 +40,7 @@ export function useFamily() {
|
|||
export function FamilyProvider({ children: providerChildren }: { children: ReactNode }) {
|
||||
const router = useRouter();
|
||||
const [familyId, setFamilyId] = useState<string | null>(null);
|
||||
const [familyName, setFamilyName] = useState<string | null>(null);
|
||||
const [childId, setChildId] = useState<string | null>(null);
|
||||
const [child, setChild] = useState<Child | null>(null);
|
||||
const [children, setChildren] = useState<Child[]>([]);
|
||||
|
|
@ -96,6 +99,7 @@ export function FamilyProvider({ children: providerChildren }: { children: React
|
|||
}
|
||||
|
||||
setFamilyId(familyId);
|
||||
setFamilyName(sessionData.familyName || "My Family");
|
||||
setTier(sessionData.tier || "free");
|
||||
setMemberCount(2);
|
||||
} catch (err) {
|
||||
|
|
@ -112,6 +116,7 @@ export function FamilyProvider({ children: providerChildren }: { children: React
|
|||
<FamilyContext.Provider
|
||||
value={{
|
||||
familyId,
|
||||
familyName,
|
||||
childId,
|
||||
child,
|
||||
children,
|
||||
|
|
|
|||
|
|
@ -26,22 +26,22 @@ interface Invite {
|
|||
export default function SettingsPage() {
|
||||
const router = useRouter();
|
||||
const { theme, mode, setMode } = useTheme();
|
||||
const { tier, memberCount, familyId } = useFamily();
|
||||
const { tier, memberCount, familyId, children, familyName: providerFamilyName } = useFamily();
|
||||
const [themeOpen, setThemeOpen] = useState(false);
|
||||
const [inviteOpen, setInviteOpen] = useState(false);
|
||||
const [membersOpen, setMembersOpen] = useState(false);
|
||||
const [familyOpen, setFamilyOpen] = useState(false);
|
||||
const [familyOpen, setFamilyOpen] = useState(true);
|
||||
const [members, setMembers] = useState<Member[]>([]);
|
||||
const [invites, setInvites] = useState<Invite[]>([]);
|
||||
const [showAddInvite, setShowAddInvite] = useState(false);
|
||||
const [inviteEmail, setInviteEmail] = useState("");
|
||||
const [inviteRole, setInviteRole] = useState("caregiver");
|
||||
const [inviteLoading, setInviteLoading] = useState(false);
|
||||
const [familyName, setFamilyName] = useState("");
|
||||
|
||||
// Check if can invite more members
|
||||
const canInvite = tier === "pro" || memberCount < 2;
|
||||
|
||||
// Family name from provider or fallback
|
||||
const familyName = providerFamilyName || "My Family";
|
||||
|
||||
const themeOptions = [
|
||||
{ value: "light", label: "Light" },
|
||||
{ value: "dark", label: "Dark" },
|
||||
|
|
@ -50,8 +50,10 @@ export default function SettingsPage() {
|
|||
] as const;
|
||||
|
||||
useEffect(() => {
|
||||
fetchInvites();
|
||||
if (familyId) {
|
||||
fetchMembers();
|
||||
fetchInvites();
|
||||
}
|
||||
}, [familyId]);
|
||||
|
||||
const fetchMembers = async () => {
|
||||
|
|
@ -129,7 +131,7 @@ export default function SettingsPage() {
|
|||
<span className="text-gray-400">→</span>
|
||||
</Link>
|
||||
|
||||
{/* Family - Collapsible */}
|
||||
{/* Family - Single consolidated section */}
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl overflow-hidden">
|
||||
<button
|
||||
onClick={() => setFamilyOpen(!familyOpen)}
|
||||
|
|
@ -137,38 +139,58 @@ export default function SettingsPage() {
|
|||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xl">🏠</span>
|
||||
<div className="font-medium">Family Settings</div>
|
||||
<div className="font-medium">Family</div>
|
||||
</div>
|
||||
<span className={`text-gray-400 transition-transform ${familyOpen ? "rotate-180" : ""}`}>▼</span>
|
||||
</button>
|
||||
|
||||
{familyOpen && (
|
||||
<div className="px-4 pb-4 space-y-3">
|
||||
<input
|
||||
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="px-4 pb-4 space-y-4">
|
||||
{/* Family Info */}
|
||||
<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" && (
|
||||
<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>
|
||||
|
||||
{/* 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>
|
||||
<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>
|
||||
<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">
|
||||
<span className="text-xl">👨👩👧</span>
|
||||
<div className="font-medium">Family</div>
|
||||
</div>
|
||||
|
||||
{/* Manage Children */}
|
||||
<Link href="/family" className="flex items-center justify-between p-3 border border-dashed border-gray-300 dark:border-gray-600 rounded-lg">
|
||||
<span className="text-sm">Manage Children</span>
|
||||
<span className="text-gray-400">→</span>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Invite Members - Collapsible */}
|
||||
{/* Invite Members */}
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl overflow-hidden">
|
||||
<button
|
||||
onClick={() => setInviteOpen(!inviteOpen)}
|
||||
|
|
@ -178,7 +200,7 @@ export default function SettingsPage() {
|
|||
<span className="text-xl">✉️</span>
|
||||
<div className="font-medium">Invite Members</div>
|
||||
{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>
|
||||
<span className={`text-gray-400 transition-transform ${inviteOpen ? "rotate-180" : ""}`}>▼</span>
|
||||
|
|
@ -237,44 +259,7 @@ export default function SettingsPage() {
|
|||
)}
|
||||
</div>
|
||||
|
||||
{/* Family Members - Collapsible */}
|
||||
<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 */}
|
||||
{/* Theme */}
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl overflow-hidden">
|
||||
<button
|
||||
onClick={() => setThemeOpen(!themeOpen)}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue