Skip to main content
The starter template uses Zustand for state management and supports both local and Firebase authentication.

State Management

Zustand Store

The app uses Zustand with localStorage persistence (lib/store.ts):
import { useAppStore } from "@/lib/store";

function MyComponent() {
  // User data
  const user = useAppStore((s) => s.user);

  // Assessment sessions
  const assessmentSessions = useAppStore((s) => s.assessmentSessions);
  const addAssessmentSession = useAppStore((s) => s.addAssessmentSession);

  // Coach sessions
  const coachSessions = useAppStore((s) => s.coachSessions);

  // Session details (keyed by session ID)
  const sessionDetails = useAppStore((s) => s.sessionDetails);
  const updateSessionDetails = useAppStore((s) => s.updateSessionDetails);

  // Personality data (from finalized assessment)
  const personalityType = useAppStore((s) => s.userPersonalityType);
  const personalityAssessment = useAppStore((s) => s.userPersonalityAssessment);
}

Store State Shape

interface AppState {
  // User
  user: User | null;
  setUser: (user: User | null) => void;

  // Assessment sessions
  assessmentSessions: string[];
  addAssessmentSession: (sessionId: string) => void;

  // Coach sessions
  coachSessions: string[];
  addCoachSession: (sessionId: string) => void;

  // Session details
  sessionDetails: Record<string, SessionDetails>;
  updateSessionDetails: (id: string, details: Partial<SessionDetails>) => void;

  // Finalized personality
  userPersonalityType: string | null;
  userPersonalityAssessment: string | null;
  setPersonality: (type: string, assessment: string) => void;

  // Reset
  clearAllData: () => void;
}

Using Selectors

Access specific state slices to avoid unnecessary re-renders:
// βœ… Good - only re-renders when assessmentSessions changes
const sessions = useAppStore((s) => s.assessmentSessions);

// ❌ Bad - re-renders on any state change
const store = useAppStore();
const sessions = store.assessmentSessions;

Storage Key

All data persists under the localStorage key: ttai-app-store

Authentication

The template supports two authentication modes:

1. Local User Auth (No Setup Required)

Simple auth for development or apps that don’t need full authentication:
const { signInAsLocalUser, currentUser, logout } = useAuth();

// Sign in with just a name
signInAsLocalUser("John");

// User object
// { id: "local-user", displayName: "John", email: null, isLocal: true }

2. Firebase Auth (Google OAuth)

Full authentication with Firebase:
const { signInWithGoogle, signIn, signUp, currentUser, logout } = useAuth();

// Google OAuth
await signInWithGoogle();

// Email/password
await signIn(email, password);
await signUp(email, password);

AuthContext Provider

The AuthContext wraps your app in app/layout.tsx:
import { AuthProvider } from "@/app/auth/AuthContext";

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <AuthProvider>{children}</AuthProvider>
      </body>
    </html>
  );
}

Using Auth in Components

"use client";
import { useAuth } from "@/app/auth/AuthContext";

export default function ProfilePage() {
  const { currentUser, loading, logout } = useAuth();

  if (loading) return <p>Loading...</p>;
  if (!currentUser) return <p>Please sign in</p>;

  return (
    <div>
      <p>Welcome, {currentUser.displayName || currentUser.email}!</p>
      <button onClick={logout}>Sign Out</button>
    </div>
  );
}

Protecting Routes

Use the AuthGuard component:
import { AuthGuard } from "@/components/auth";

export default function ProtectedPage() {
  return (
    <AuthGuard title="Sign In Required" description="Create an account to continue">
      <YourProtectedContent />
    </AuthGuard>
  );
}

Firebase Setup

1

Create Firebase Project

Go to Firebase Console and create a new project.
2

Enable Authentication

Navigate to Authentication β†’ Sign-in method and enable:
  • Email/Password
  • Google
3

Get Config

Go to Project Settings β†’ General β†’ Your apps β†’ Web app. Copy the configuration object.
4

Add Environment Variables

.env.local
NEXT_PUBLIC_FIREBASE_API_KEY=your_api_key
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your-project.firebaseapp.com
NEXT_PUBLIC_FIREBASE_PROJECT_ID=your-project-id
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your-project.appspot.com
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_sender_id
NEXT_PUBLIC_FIREBASE_APP_ID=your_app_id
All Firebase config variables must be prefixed with NEXT_PUBLIC_ to be accessible in the browser. Never commit .env.local to version control!

Integrating Auth with ToughTongue

Pass user info to scenarios:
const { currentUser } = useAuth();

const testUrl = buildPersonalityTestUrl({
  userName: currentUser?.displayName || "",
  userEmail: currentUser?.email || "",
});
Store sessions with user context:
const saveSession = (sessionId: string, analysis: any) => {
  updateSessionDetails(sessionId, {
    ...analysis,
    userId: currentUser?.uid,
    completedAt: new Date().toISOString(),
  });
};

Troubleshooting

Solution:
  • Double-check all Firebase config values in .env.local
  • Ensure all 6 Firebase variables are set
  • Restart dev server after changes
Solution: - Verify Google provider is enabled in Firebase Console - Add your domain to authorized domains in Firebase Console β†’ Authentication β†’ Settings - For localhost, it should be authorized by default
Cause: Zustand store uses localStorage (per-browser, per-domain).Notes:
  • Clearing browser data clears saved results
  • Data doesn’t sync across browsers/devices
  • For production, consider syncing to Firestore

Next Steps

Deployment

Deploy to production

API Reference

Full API documentation