This commit is contained in:
KS Jannette
2026-03-29 00:02:38 -04:00
parent 33ec729631
commit 3a52e7afb3
5 changed files with 86 additions and 13 deletions

View File

@@ -1,4 +1,5 @@
Backend startup quickstarat:
Backend startup quickstart:
-----------------------------> BEST
## 1. brew services start postgresql@15

View File

@@ -0,0 +1,48 @@
import { createContext, useContext, useReducer, useEffect } from "react";
import {
userPropertiesReducer,
initialState,
ACTION_TYPES,
} from "./userPropertiesReducer";
const STORAGE_KEY = "kp_onboard_user";
function hydrateState() {
try {
const raw = sessionStorage.getItem(STORAGE_KEY);
if (raw) return { ...initialState, ...JSON.parse(raw) };
} catch {
/* corrupt data — fall through */
}
return initialState;
}
const UserPropertiesContext = createContext(initialState);
export function useUserProperties() {
const ctx = useContext(UserPropertiesContext);
if (!ctx) {
throw new Error(
"useUserProperties must be used within UserPropertiesProvider",
);
}
return ctx;
}
export function UserPropertiesProvider({ children }) {
const [state, dispatch] = useReducer(userPropertiesReducer, null, hydrateState);
useEffect(() => {
if (state.email || state.password) {
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(state));
} else {
sessionStorage.removeItem(STORAGE_KEY);
}
}, [state]);
return (
<UserPropertiesContext.Provider value={{ state, dispatch, ACTION_TYPES }}>
{children}
</UserPropertiesContext.Provider>
);
}

View File

@@ -0,0 +1,20 @@
export const initialState = {
email: "",
password: "",
};
export const ACTION_TYPES = {
SET_USER_PROPERTIES: "SET_USER_PROPERTIES",
CLEAR_USER_PROPERTIES: "CLEAR_USER_PROPERTIES",
};
export function userPropertiesReducer(state, action) {
switch (action.type) {
case ACTION_TYPES.SET_USER_PROPERTIES:
return { ...state, ...action.payload };
case ACTION_TYPES.CLEAR_USER_PROPERTIES:
return { ...initialState };
default:
return state;
}
}

View File

@@ -2,15 +2,18 @@ import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import { AuthProvider } from "./contexts/AuthContext";
import { UserPropertiesProvider } from "./contexts/UserPropertiesContext";
import App from "./App";
import "./index.css";
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<BrowserRouter>
<UserPropertiesProvider>
<AuthProvider>
<App />
</AuthProvider>
</UserPropertiesProvider>
</BrowserRouter>
</React.StrictMode>,
);

View File

@@ -1,6 +1,7 @@
import { useState, useEffect } from "react";
import { useNavigate, useSearchParams } from "react-router-dom";
import { useAuth } from "../../contexts/AuthContext";
import { useUserProperties } from "../../contexts/UserPropertiesContext";
import {
createOnboardingCheckout,
verifyCheckoutSession,
@@ -15,6 +16,7 @@ const STEPS = ["Create Account", "Choose Plan"];
export default function Subscribe() {
const { currentUser, signup } = useAuth();
const { state: userProps, dispatch, ACTION_TYPES } = useUserProperties();
const navigate = useNavigate();
const [searchParams, setSearchParams] = useSearchParams();
@@ -51,9 +53,7 @@ export default function Subscribe() {
// Phase 1: no account yet — create it. Auth state change remounts the
// component (App.jsx swaps route trees) and Phase 2 runs on the next mount.
if (!currentUser) {
const savedEmail = sessionStorage.getItem("kp_onboard_email");
const savedPw = sessionStorage.getItem("kp_onboard_pw");
if (!savedEmail || !savedPw) {
if (!userProps.email || !userProps.password) {
setSearchParams({}, { replace: true });
setError("Session expired. Please start the signup process again.");
setStep(1);
@@ -61,7 +61,7 @@ export default function Subscribe() {
return;
}
setLoading(true);
signup(savedEmail, savedPw).catch((err) => {
signup(userProps.email, userProps.password).catch((err) => {
setSearchParams({}, { replace: true });
setError("Account creation failed: " + err.message);
setStep(1);
@@ -73,8 +73,7 @@ export default function Subscribe() {
// Phase 2: authenticated — verify the checkout and go to the dashboard.
setSearchParams({}, { replace: true });
setLoading(true);
sessionStorage.removeItem("kp_onboard_email");
sessionStorage.removeItem("kp_onboard_pw");
dispatch({ type: ACTION_TYPES.CLEAR_USER_PROPERTIES });
verifyCheckoutSession(sessionId)
.then(() => {
@@ -85,7 +84,7 @@ export default function Subscribe() {
setStep(2);
})
.finally(() => setLoading(false));
}, [currentUser, searchParams, setSearchParams, signup, navigate]);
}, [currentUser, searchParams, setSearchParams, signup, navigate, userProps, dispatch, ACTION_TYPES]);
// ── Step handlers ─────────────────────────────────────────────────────────
@@ -124,8 +123,10 @@ export default function Subscribe() {
return;
}
sessionStorage.setItem("kp_onboard_email", data.email);
sessionStorage.setItem("kp_onboard_pw", data.password);
dispatch({
type: ACTION_TYPES.SET_USER_PROPERTIES,
payload: { email: data.email, password: data.password },
});
const { url } = await createOnboardingCheckout(
data.email,
data.selectedTier,