Audit: Fix FamilyProvider, add signout, remove hardcoded defaults
This commit is contained in:
parent
35895d226f
commit
a95f55967d
7 changed files with 44 additions and 15 deletions
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import { useState, useEffect, createContext, useContext } from "react";
|
import { useState, useEffect, createContext, useContext } from "react";
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
interface Child {
|
interface Child {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -35,6 +36,7 @@ export function useFamily() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FamilyProvider({ children: providerChildren }: { children: ReactNode }) {
|
export function FamilyProvider({ children: providerChildren }: { children: ReactNode }) {
|
||||||
|
const router = useRouter();
|
||||||
const [familyId, setFamilyId] = useState<string | null>(null);
|
const [familyId, setFamilyId] = 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);
|
||||||
|
|
@ -50,17 +52,22 @@ export function FamilyProvider({ children: providerChildren }: { children: React
|
||||||
const sessionRes = await fetch("/api/auth/signin");
|
const sessionRes = await fetch("/api/auth/signin");
|
||||||
const sessionData = await sessionRes.json();
|
const sessionData = await sessionRes.json();
|
||||||
|
|
||||||
|
// Not authenticated - redirect to login
|
||||||
if (!sessionData.authenticated) {
|
if (!sessionData.authenticated) {
|
||||||
// Not logged in, use default
|
router.push("/login");
|
||||||
setFamilyId("default");
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sessionFamilyId = sessionData.familyId || "default";
|
// Authenticated but no family - go to onboarding
|
||||||
|
if (!sessionData.familyId) {
|
||||||
|
router.push("/onboarding");
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch children for this family
|
// Fetch children for this family
|
||||||
const res = await fetch(`/api/children?familyId=${sessionFamilyId}`);
|
const res = await fetch(`/api/children?familyId=${sessionData.familyId}`);
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
|
|
||||||
if (data.children?.length > 0) {
|
if (data.children?.length > 0) {
|
||||||
|
|
@ -74,9 +81,12 @@ export function FamilyProvider({ children: providerChildren }: { children: React
|
||||||
setChildren(childList);
|
setChildren(childList);
|
||||||
setChild(childList[0]);
|
setChild(childList[0]);
|
||||||
setChildId(childList[0].id);
|
setChildId(childList[0].id);
|
||||||
|
} else {
|
||||||
|
// No children - go to onboarding
|
||||||
|
router.push("/onboarding");
|
||||||
}
|
}
|
||||||
|
|
||||||
setFamilyId(sessionFamilyId);
|
setFamilyId(sessionData.familyId);
|
||||||
setTier(sessionData.tier || "free");
|
setTier(sessionData.tier || "free");
|
||||||
setMemberCount(2);
|
setMemberCount(2);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
@ -87,7 +97,7 @@ export function FamilyProvider({ children: providerChildren }: { children: React
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchFamilyData();
|
fetchFamilyData();
|
||||||
}, []);
|
}, [router]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FamilyContext.Provider
|
<FamilyContext.Provider
|
||||||
|
|
|
||||||
|
|
@ -5,18 +5,16 @@ import { cookies } from "next/headers";
|
||||||
export async function POST() {
|
export async function POST() {
|
||||||
try {
|
try {
|
||||||
const cookieStore = await cookies();
|
const cookieStore = await cookies();
|
||||||
const sessionToken = cookieStore.get("session")?.value;
|
const sessionToken = cookieStore.get("tia_session")?.value;
|
||||||
|
|
||||||
if (sessionToken) {
|
if (sessionToken) {
|
||||||
// Delete session from database
|
|
||||||
await sql`
|
await sql`
|
||||||
DELETE FROM sessions WHERE session_token = ${sessionToken}
|
DELETE FROM sessions WHERE session_token = ${sessionToken}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear cookie
|
|
||||||
const response = NextResponse.json({ success: true });
|
const response = NextResponse.json({ success: true });
|
||||||
response.cookies.set("session", "", {
|
response.cookies.set("tia_session", "", {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
secure: process.env.NODE_ENV === "production",
|
secure: process.env.NODE_ENV === "production",
|
||||||
sameSite: "lax",
|
sameSite: "lax",
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { sql } from "@/db";
|
||||||
// GET - list children
|
// GET - list children
|
||||||
export async function GET(request: Request) {
|
export async function GET(request: Request) {
|
||||||
const { searchParams } = new URL(request.url);
|
const { searchParams } = new URL(request.url);
|
||||||
const familyId = searchParams.get("familyId") || "default";
|
const familyId = searchParams.get("familyId") || null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const children = await sql.unsafe(
|
const children = await sql.unsafe(
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { sql } from "@/db";
|
||||||
// GET - list family members
|
// GET - list family members
|
||||||
export async function GET(request: Request) {
|
export async function GET(request: Request) {
|
||||||
const { searchParams } = new URL(request.url);
|
const { searchParams } = new URL(request.url);
|
||||||
const familyId = searchParams.get("familyId") || "default";
|
const familyId = searchParams.get("familyId") || null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const members = await sql.unsafe(
|
const members = await sql.unsafe(
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { sql } from "@/db";
|
||||||
// GET - get family details
|
// GET - get family details
|
||||||
export async function GET(request: Request) {
|
export async function GET(request: Request) {
|
||||||
const { searchParams } = new URL(request.url);
|
const { searchParams } = new URL(request.url);
|
||||||
const familyId = searchParams.get("familyId") || "default";
|
const familyId = searchParams.get("familyId");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const family = await sql.unsafe(
|
const family = await sql.unsafe(
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { randomBytes } from "crypto";
|
||||||
// GET - list invites for a family
|
// GET - list invites for a family
|
||||||
export async function GET(request: Request) {
|
export async function GET(request: Request) {
|
||||||
const { searchParams } = new URL(request.url);
|
const { searchParams } = new URL(request.url);
|
||||||
const familyId = searchParams.get("familyId") || "default";
|
const familyId = searchParams.get("familyId") || null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const invites = await sql.unsafe(
|
const invites = await sql.unsafe(
|
||||||
|
|
|
||||||
|
|
@ -38,11 +38,23 @@ export default function SettingsPage() {
|
||||||
const [inviteRole, setInviteRole] = useState("caregiver");
|
const [inviteRole, setInviteRole] = useState("caregiver");
|
||||||
const [inviteLoading, setInviteLoading] = useState(false);
|
const [inviteLoading, setInviteLoading] = useState(false);
|
||||||
const [familyName, setFamilyName] = useState("");
|
const [familyName, setFamilyName] = useState("");
|
||||||
const [pediatricianPhone, setPediatricianPhone] = useState("");
|
const [signingOut, setSigningOut] = useState(false);
|
||||||
|
|
||||||
// Check if can invite more members
|
// Check if can invite more members
|
||||||
const canInvite = tier === "pro" || memberCount < 2;
|
const canInvite = tier === "pro" || memberCount < 2;
|
||||||
|
|
||||||
|
const handleSignOut = async () => {
|
||||||
|
if (!confirm("Are you sure you want to sign out?")) return;
|
||||||
|
setSigningOut(true);
|
||||||
|
try {
|
||||||
|
await fetch("/api/auth/signout", { method: "POST" });
|
||||||
|
router.push("/login");
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Sign out failed:", err);
|
||||||
|
}
|
||||||
|
setSigningOut(false);
|
||||||
|
};
|
||||||
|
|
||||||
const themeOptions = [
|
const themeOptions = [
|
||||||
{ value: "light", label: "Light" },
|
{ value: "light", label: "Light" },
|
||||||
{ value: "dark", label: "Dark" },
|
{ value: "dark", label: "Dark" },
|
||||||
|
|
@ -317,6 +329,15 @@ export default function SettingsPage() {
|
||||||
<div className="font-medium">App Version</div>
|
<div className="font-medium">App Version</div>
|
||||||
<div className="text-sm text-gray-500">Tia v1.0.0</div>
|
<div className="text-sm text-gray-500">Tia v1.0.0</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Sign Out */}
|
||||||
|
<button
|
||||||
|
onClick={handleSignOut}
|
||||||
|
disabled={signingOut}
|
||||||
|
className="w-full p-4 mt-4 bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 rounded-xl font-medium disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{signingOut ? "Signing out..." : "Sign Out"}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue