diff --git a/__tests__/app/coaching-sessions/coaching-session-page.test.tsx b/__tests__/app/coaching-sessions/coaching-session-page.test.tsx
index 5d35c8a5..ffd9514f 100644
--- a/__tests__/app/coaching-sessions/coaching-session-page.test.tsx
+++ b/__tests__/app/coaching-sessions/coaching-session-page.test.tsx
@@ -3,6 +3,9 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'
import { useRouter, useParams, useSearchParams } from 'next/navigation'
import CoachingSessionsPage from '@/app/coaching-sessions/[id]/page'
import { TestProviders } from '@/test-utils/providers'
+import { useCurrentCoachingSession } from '@/lib/hooks/use-current-coaching-session'
+import { useCurrentCoachingRelationship } from '@/lib/hooks/use-current-coaching-relationship'
+import { createMockCoachingSession } from '../../factories/coaching-session.factory'
// Mock Next.js navigation hooks
vi.mock('next/navigation', () => ({
@@ -12,24 +15,8 @@ vi.mock('next/navigation', () => ({
}))
// Mock the coaching session hooks
-vi.mock('@/lib/hooks/use-current-coaching-session', () => ({
- useCurrentCoachingSession: vi.fn(() => ({
- currentCoachingSessionId: 'session-123',
- currentCoachingSession: {
- id: 'session-123',
- title: 'Test Session',
- coaching_relationship_id: 'rel-123'
- },
- isError: false,
- }))
-}))
-
-vi.mock('@/lib/hooks/use-current-coaching-relationship', () => ({
- useCurrentCoachingRelationship: vi.fn(() => ({
- currentCoachingRelationshipId: 'rel-123',
- setCurrentCoachingRelationshipId: vi.fn(),
- }))
-}))
+vi.mock('@/lib/hooks/use-current-coaching-session')
+vi.mock('@/lib/hooks/use-current-coaching-relationship')
// Mock auth store
vi.mock('@/lib/providers/auth-store-provider', () => ({
@@ -84,16 +71,39 @@ describe('CoachingSessionsPage URL Parameter Persistence', () => {
const mockRouter = {
push: vi.fn(),
replace: vi.fn(),
- }
+ } as const
const mockParams = {
id: 'session-123'
- }
+ } as const
beforeEach(() => {
vi.clearAllMocks()
;(useRouter as any).mockReturnValue(mockRouter)
;(useParams as any).mockReturnValue(mockParams)
+
+ // Set default mocks for relationship hooks
+ vi.mocked(useCurrentCoachingSession).mockReturnValue({
+ currentCoachingSessionId: 'session-123',
+ currentCoachingSession: createMockCoachingSession({
+ id: 'session-123',
+ coaching_relationship_id: 'rel-123'
+ }),
+ isError: false,
+ isLoading: false,
+ refresh: vi.fn(),
+ })
+
+ vi.mocked(useCurrentCoachingRelationship).mockReturnValue({
+ currentCoachingRelationshipId: 'rel-123',
+ setCurrentCoachingRelationshipId: vi.fn(),
+ currentCoachingRelationship: null,
+ isLoading: false,
+ isError: false,
+ currentOrganizationId: 'org-123',
+ resetCoachingRelationshipState: vi.fn(),
+ refresh: vi.fn(),
+ })
})
/**
@@ -217,4 +227,254 @@ describe('CoachingSessionsPage URL Parameter Persistence', () => {
)
})
})
+})
+
+/**
+ * Test Suite: Relationship Auto-Sync Behavior
+ *
+ * Purpose: Validates that the coaching relationship ID is correctly synced from the current
+ * session data to the store in various navigation scenarios, fixing Bug #228 while preserving
+ * the fix for Issue #79 (new tab support).
+ */
+describe('CoachingSessionsPage - Relationship Auto-Sync', () => {
+ const mockRouter = {
+ push: vi.fn(),
+ replace: vi.fn(),
+ } as const
+
+ const mockParams = {
+ id: 'session-123'
+ } as const
+
+ beforeEach(() => {
+ vi.clearAllMocks()
+ ;(useRouter as any).mockReturnValue(mockRouter)
+ ;(useParams as any).mockReturnValue(mockParams)
+ ;(useSearchParams as any).mockReturnValue(new URLSearchParams())
+ })
+
+ /**
+ * Test: First Load with Empty Store (Issue #79)
+ *
+ * Scenario: User opens a session URL in a new tab/window with empty sessionStorage
+ * Expected: Relationship ID should be synced from session data to store AND refresh called
+ * This ensures Issue #79 (new tab support) continues to work
+ */
+ it('should sync relationship ID on first load with empty store', () => {
+ const mockSetRelationshipId = vi.fn()
+ const mockRefresh = vi.fn()
+
+ // Session has relationship ID, but store is empty (new tab scenario)
+ vi.mocked(useCurrentCoachingSession).mockReturnValue({
+ currentCoachingSessionId: 'session-123',
+ currentCoachingSession: createMockCoachingSession({
+ id: 'session-123',
+ coaching_relationship_id: 'rel-123'
+ }),
+ isError: false,
+ isLoading: false,
+ refresh: vi.fn(),
+ })
+
+ vi.mocked(useCurrentCoachingRelationship).mockReturnValue({
+ currentCoachingRelationshipId: null, // Empty store
+ setCurrentCoachingRelationshipId: mockSetRelationshipId,
+ currentCoachingRelationship: null,
+ isLoading: false,
+ isError: false,
+ currentOrganizationId: 'org-123',
+ resetCoachingRelationshipState: vi.fn(),
+ refresh: mockRefresh,
+ })
+
+ render(
+
+
+
+ )
+
+ // Should call setCurrentCoachingRelationshipId with the session's relationship ID
+ expect(mockSetRelationshipId).toHaveBeenCalledWith('rel-123')
+ // Should call refresh to fetch the relationship data
+ expect(mockRefresh).toHaveBeenCalled()
+ })
+
+ /**
+ * Test: Switching Between Sessions with Different Relationships (Bug #228)
+ *
+ * Scenario: User navigates from Session A (rel-1) to Session B (rel-2)
+ * Expected: Relationship ID should update from rel-1 to rel-2 AND refresh called
+ * This is the primary fix for Bug #228
+ */
+ it('should update relationship ID when switching to session with different relationship', () => {
+ const mockSetRelationshipId = vi.fn()
+ const mockRefresh = vi.fn()
+
+ // Session has relationship ID 'rel-456', but store has stale 'rel-123'
+ vi.mocked(useCurrentCoachingSession).mockReturnValue({
+ currentCoachingSessionId: 'session-456',
+ currentCoachingSession: createMockCoachingSession({
+ id: 'session-456',
+ coaching_relationship_id: 'rel-456' // Different relationship
+ }),
+ isError: false,
+ isLoading: false,
+ refresh: vi.fn(),
+ })
+
+ vi.mocked(useCurrentCoachingRelationship).mockReturnValue({
+ currentCoachingRelationshipId: 'rel-123', // Stale relationship from previous session
+ setCurrentCoachingRelationshipId: mockSetRelationshipId,
+ currentCoachingRelationship: null,
+ isLoading: false,
+ isError: false,
+ currentOrganizationId: 'org-123',
+ resetCoachingRelationshipState: vi.fn(),
+ refresh: mockRefresh,
+ })
+
+ render(
+
+
+
+ )
+
+ // Should call setCurrentCoachingRelationshipId to update to the new relationship
+ expect(mockSetRelationshipId).toHaveBeenCalledWith('rel-456')
+ // Should call refresh to fetch the new relationship data (fixes stale cache bug)
+ expect(mockRefresh).toHaveBeenCalled()
+ })
+
+ /**
+ * Test: Same Relationship, Different Session
+ *
+ * Scenario: User navigates from Session A to Session B, both in the same relationship
+ * Expected: setCurrentCoachingRelationshipId should NOT be called (optimization)
+ * This ensures we don't trigger unnecessary updates
+ */
+ it('should not update relationship ID when switching to session with same relationship', () => {
+ const mockSetRelationshipId = vi.fn()
+
+ // Session and store both have the same relationship ID
+ vi.mocked(useCurrentCoachingSession).mockReturnValue({
+ currentCoachingSessionId: 'session-456',
+ currentCoachingSession: createMockCoachingSession({
+ id: 'session-456',
+ coaching_relationship_id: 'rel-123' // Same relationship
+ }),
+ isError: false,
+ isLoading: false,
+ refresh: vi.fn(),
+ })
+
+ vi.mocked(useCurrentCoachingRelationship).mockReturnValue({
+ currentCoachingRelationshipId: 'rel-123', // Same relationship already in store
+ setCurrentCoachingRelationshipId: mockSetRelationshipId,
+ currentCoachingRelationship: null,
+ isLoading: false,
+ isError: false,
+ currentOrganizationId: 'org-123',
+ resetCoachingRelationshipState: vi.fn(),
+ refresh: vi.fn(),
+ })
+
+ render(
+
+
+
+ )
+
+ // Should NOT call setCurrentCoachingRelationshipId since they match
+ expect(mockSetRelationshipId).not.toHaveBeenCalled()
+ })
+
+ /**
+ * Test: Session Without Relationship ID
+ *
+ * Scenario: Session data is loaded but doesn't have a coaching_relationship_id
+ * Expected: setCurrentCoachingRelationshipId should NOT be called
+ * This handles edge cases where session data might be incomplete
+ */
+ it('should not update relationship ID when session has no relationship', () => {
+ const mockSetRelationshipId = vi.fn()
+
+ // Session without relationship ID
+ vi.mocked(useCurrentCoachingSession).mockReturnValue({
+ currentCoachingSessionId: 'session-123',
+ currentCoachingSession: createMockCoachingSession({
+ id: 'session-123',
+ coaching_relationship_id: undefined as any // No coaching_relationship_id
+ }),
+ isError: false,
+ isLoading: false,
+ refresh: vi.fn(),
+ })
+
+ vi.mocked(useCurrentCoachingRelationship).mockReturnValue({
+ currentCoachingRelationshipId: 'rel-123',
+ setCurrentCoachingRelationshipId: mockSetRelationshipId,
+ currentCoachingRelationship: null,
+ isLoading: false,
+ isError: false,
+ currentOrganizationId: 'org-123',
+ resetCoachingRelationshipState: vi.fn(),
+ refresh: vi.fn(),
+ })
+
+ render(
+
+
+
+ )
+
+ // Should NOT call setCurrentCoachingRelationshipId
+ expect(mockSetRelationshipId).not.toHaveBeenCalled()
+ })
+
+ /**
+ * Test: Direct URL Access with Stale Store
+ *
+ * Scenario: User manually types a session URL while store has a different relationship
+ * Expected: Relationship ID should update to match the session from the URL AND refresh called
+ * This ensures URL is always the source of truth
+ */
+ it('should handle direct URL access with stale relationship ID in store', () => {
+ const mockSetRelationshipId = vi.fn()
+ const mockRefresh = vi.fn()
+
+ // User types URL for session-789 which belongs to rel-789
+ // But store has stale rel-123 from previous browsing
+ vi.mocked(useCurrentCoachingSession).mockReturnValue({
+ currentCoachingSessionId: 'session-789',
+ currentCoachingSession: createMockCoachingSession({
+ id: 'session-789',
+ coaching_relationship_id: 'rel-789'
+ }),
+ isError: false,
+ isLoading: false,
+ refresh: vi.fn(),
+ })
+
+ vi.mocked(useCurrentCoachingRelationship).mockReturnValue({
+ currentCoachingRelationshipId: 'rel-123', // Stale from previous session
+ setCurrentCoachingRelationshipId: mockSetRelationshipId,
+ currentCoachingRelationship: null,
+ isLoading: false,
+ isError: false,
+ currentOrganizationId: 'org-123',
+ resetCoachingRelationshipState: vi.fn(),
+ refresh: mockRefresh,
+ })
+
+ render(
+
+
+
+ )
+
+ // Should update to match the URL-based session
+ expect(mockSetRelationshipId).toHaveBeenCalledWith('rel-789')
+ // Should call refresh to fetch the new relationship data
+ expect(mockRefresh).toHaveBeenCalled()
+ })
})
\ No newline at end of file
diff --git a/__tests__/app/coaching-sessions/relationship-sync.test.ts b/__tests__/app/coaching-sessions/relationship-sync.test.ts
new file mode 100644
index 00000000..7e97e9e7
--- /dev/null
+++ b/__tests__/app/coaching-sessions/relationship-sync.test.ts
@@ -0,0 +1,50 @@
+import { describe, it, expect } from 'vitest'
+import { shouldSyncRelationship } from '@/app/coaching-sessions/[id]/relationship-sync'
+
+describe('shouldSyncRelationship', () => {
+ describe('when session has no relationship ID', () => {
+ it('returns false with null store', () => {
+ expect(shouldSyncRelationship(undefined, null)).toBe(false)
+ })
+
+ it('returns false with populated store', () => {
+ expect(shouldSyncRelationship(undefined, 'rel-123')).toBe(false)
+ })
+ })
+
+ describe('when store is empty', () => {
+ it('returns true (Issue #79: new tab scenario)', () => {
+ expect(shouldSyncRelationship('rel-123', null)).toBe(true)
+ })
+ })
+
+ describe('when relationship IDs differ', () => {
+ it('returns true (Bug #228: switching between sessions)', () => {
+ expect(shouldSyncRelationship('rel-456', 'rel-123')).toBe(true)
+ })
+
+ it('returns true for any different ID', () => {
+ expect(shouldSyncRelationship('rel-999', 'rel-000')).toBe(true)
+ })
+ })
+
+ describe('when relationship IDs match', () => {
+ it('returns false (optimization: no sync needed)', () => {
+ expect(shouldSyncRelationship('rel-123', 'rel-123')).toBe(false)
+ })
+
+ it('returns false for any matching ID', () => {
+ expect(shouldSyncRelationship('rel-xyz', 'rel-xyz')).toBe(false)
+ })
+ })
+
+ describe('edge cases', () => {
+ it('handles empty string as session ID', () => {
+ expect(shouldSyncRelationship('', 'rel-123')).toBe(false)
+ })
+
+ it('handles empty string in both params', () => {
+ expect(shouldSyncRelationship('', '')).toBe(false)
+ })
+ })
+})
diff --git a/__tests__/factories/coaching-session.factory.ts b/__tests__/factories/coaching-session.factory.ts
new file mode 100644
index 00000000..86fae755
--- /dev/null
+++ b/__tests__/factories/coaching-session.factory.ts
@@ -0,0 +1,28 @@
+import type { CoachingSession } from '@/types/coaching-session'
+
+/**
+ * Creates a mock CoachingSession for testing purposes.
+ *
+ * @param overrides - Partial CoachingSession to override default values
+ * @returns A complete CoachingSession object with sensible defaults
+ *
+ * @example
+ * const session = createMockCoachingSession({
+ * id: 'session-456',
+ * coaching_relationship_id: 'rel-456'
+ * })
+ */
+export function createMockCoachingSession(
+ overrides?: Partial
+): CoachingSession {
+ const now = new Date().toISOString()
+
+ return {
+ id: 'session-123',
+ coaching_relationship_id: 'rel-123',
+ date: now,
+ created_at: now,
+ updated_at: now,
+ ...overrides,
+ }
+}
diff --git a/src/app/coaching-sessions/[id]/page.tsx b/src/app/coaching-sessions/[id]/page.tsx
index 1ed5bc9e..6a700bc7 100644
--- a/src/app/coaching-sessions/[id]/page.tsx
+++ b/src/app/coaching-sessions/[id]/page.tsx
@@ -19,6 +19,7 @@ import ShareSessionLink from "@/components/ui/share-session-link";
import { toast } from "sonner";
import { ForbiddenError } from "@/components/ui/errors/forbidden-error";
import { EntityApiError } from "@/types/general";
+import { shouldSyncRelationship } from "./relationship-sync";
export default function CoachingSessionsPage() {
const router = useRouter();
@@ -37,25 +38,32 @@ export default function CoachingSessionsPage() {
const { currentCoachingSession, currentCoachingSessionId, isError } = useCurrentCoachingSession();
// Get current coaching relationship state and data
- const { currentCoachingRelationshipId, setCurrentCoachingRelationshipId } =
+ const { currentCoachingRelationshipId, setCurrentCoachingRelationshipId, refresh } =
useCurrentCoachingRelationship();
-
- // Auto-sync relationship ID when session data loads (if not already set)
+ // Auto-sync relationship ID when session data loads
+ // This ensures the relationship selector always matches the current session
useEffect(() => {
if (
currentCoachingSession?.coaching_relationship_id &&
- !currentCoachingRelationshipId
+ shouldSyncRelationship(
+ currentCoachingSession.coaching_relationship_id,
+ currentCoachingRelationshipId
+ )
) {
setCurrentCoachingRelationshipId(
currentCoachingSession.coaching_relationship_id
);
+
+ // Force immediate fetch of new relationship data to prevent showing stale cached data
+ // This ensures the coaching session title shows the correct coach/coachee names
+ refresh();
}
- // setCurrentCoachingRelationshipId is stable and doesn't need to be in deps
- // eslint-disable-next-line react-hooks/exhaustive-deps
}, [
currentCoachingSession?.coaching_relationship_id,
currentCoachingRelationshipId,
+ setCurrentCoachingRelationshipId,
+ refresh,
]);
// Check for 403 Forbidden error AFTER all hooks are called
@@ -109,7 +117,7 @@ export default function CoachingSessionsPage() {
onError={handleShareError}
/>
diff --git a/src/app/coaching-sessions/[id]/relationship-sync.ts b/src/app/coaching-sessions/[id]/relationship-sync.ts
new file mode 100644
index 00000000..2a297392
--- /dev/null
+++ b/src/app/coaching-sessions/[id]/relationship-sync.ts
@@ -0,0 +1,36 @@
+/**
+ * Determines if coaching relationship ID should be synced from session data.
+ *
+ * The URL is the source of truth for the current session. We sync the relationship ID
+ * from the session data in two cases:
+ * 1. Store is empty (e.g., new tab/window) - fixes Issue #79
+ * 2. Store has a different relationship (e.g., navigating between sessions) - fixes Bug #228
+ *
+ * @param sessionRelationshipId - The relationship ID from the current session
+ * @param currentRelationshipId - The relationship ID currently in the store
+ * @returns true if we should sync the relationship ID
+ *
+ * @example
+ * // Store is empty (new tab)
+ * shouldSyncRelationship('rel-123', null) // returns true
+ *
+ * @example
+ * // Store has different relationship (switching sessions)
+ * shouldSyncRelationship('rel-456', 'rel-123') // returns true
+ *
+ * @example
+ * // Store matches session (same relationship)
+ * shouldSyncRelationship('rel-123', 'rel-123') // returns false
+ *
+ * @example
+ * // Session has no relationship (incomplete data)
+ * shouldSyncRelationship(undefined, 'rel-123') // returns false
+ */
+export function shouldSyncRelationship(
+ sessionRelationshipId: string | undefined,
+ currentRelationshipId: string | null
+): boolean {
+ if (!sessionRelationshipId) return false
+ // Always sync when empty (new tab) or when different (switching sessions)
+ return !currentRelationshipId || sessionRelationshipId !== currentRelationshipId
+}
diff --git a/src/components/ui/members/member-card.tsx b/src/components/ui/members/member-card.tsx
index f5168fc4..d150d1af 100644
--- a/src/components/ui/members/member-card.tsx
+++ b/src/components/ui/members/member-card.tsx
@@ -27,7 +27,7 @@ import {
SelectContent,
SelectItem,
} from "@/components/ui/select";
-import { CoachingRelationshipWithUserNames } from "@/types/coaching_relationship";
+import { CoachingRelationshipWithUserNames } from "@/types/coaching-relationship";
import { AuthStore } from "@/lib/stores/auth-store";
import { Id } from "@/types/general";
import { User, isAdminOrSuperAdmin, UserRoleState } from "@/types/user";
diff --git a/src/components/ui/members/member-container.tsx b/src/components/ui/members/member-container.tsx
index 61778581..e7875f0c 100644
--- a/src/components/ui/members/member-container.tsx
+++ b/src/components/ui/members/member-container.tsx
@@ -1,7 +1,7 @@
import { MemberList } from "./member-list";
import { AddMemberButton } from "./add-member-button";
import { User, isAdminOrSuperAdmin, sortUsersAlphabetically } from "@/types/user";
-import { CoachingRelationshipWithUserNames, isUserCoach } from "@/types/coaching_relationship";
+import { CoachingRelationshipWithUserNames, isUserCoach } from "@/types/coaching-relationship";
import { UserSession } from "@/types/user-session";
import { useAuthStore } from "@/lib/providers/auth-store-provider";
import { useCurrentUserRole } from "@/lib/hooks/use-current-user-role";
diff --git a/src/components/ui/members/member-list.tsx b/src/components/ui/members/member-list.tsx
index 6a3c5b71..2157c3f6 100644
--- a/src/components/ui/members/member-list.tsx
+++ b/src/components/ui/members/member-list.tsx
@@ -1,7 +1,7 @@
import { Card, CardContent } from "@/components/ui/card";
import { User, UserRoleState } from "@/types/user";
import { MemberCard } from "./member-card";
-import { CoachingRelationshipWithUserNames } from "@/types/coaching_relationship";
+import { CoachingRelationshipWithUserNames } from "@/types/coaching-relationship";
import { Id } from "@/types/general";
interface MemberListProps {
diff --git a/src/lib/api/coaching-relationships.ts b/src/lib/api/coaching-relationships.ts
index d6873071..f00e6849 100644
--- a/src/lib/api/coaching-relationships.ts
+++ b/src/lib/api/coaching-relationships.ts
@@ -6,7 +6,7 @@ import {
NewCoachingRelationship,
CoachingRelationshipWithUserNames,
defaultCoachingRelationshipWithUserNames,
-} from "@/types/coaching_relationship";
+} from "@/types/coaching-relationship";
import { EntityApi } from "./entity-api";
const ORGANIZATIONS_BASEURL: string = `${siteConfig.env.backendServiceURL}/organizations`;
diff --git a/src/lib/hooks/use-auto-select-single-relationship.ts b/src/lib/hooks/use-auto-select-single-relationship.ts
index 6b65e0d4..20cd191e 100644
--- a/src/lib/hooks/use-auto-select-single-relationship.ts
+++ b/src/lib/hooks/use-auto-select-single-relationship.ts
@@ -2,7 +2,7 @@
import { useEffect } from "react";
import { Id } from "@/types/general";
-import { CoachingRelationshipWithUserNames } from "@/types/coaching_relationship";
+import { CoachingRelationshipWithUserNames } from "@/types/coaching-relationship";
/**
* Custom hook that automatically selects a coaching relationship when:
@@ -12,14 +12,14 @@ import { CoachingRelationshipWithUserNames } from "@/types/coaching_relationship
*
* @param relationships Array of available coaching relationships
* @param isLoading Whether relationships are currently being loaded
- * @param currentId Currently selected coaching relationship ID
+ * @param currentId Currently selected coaching relationship ID (can be null)
* @param setCurrentId Function to set the current coaching relationship ID
* @param onSelect Optional callback fired when auto-selection occurs
*/
export const useAutoSelectSingleRelationship = (
relationships: CoachingRelationshipWithUserNames[] | undefined,
isLoading: boolean,
- currentId: Id,
+ currentId: Id | null,
setCurrentId: (id: Id) => void,
onSelect?: (relationshipId: Id) => void
) => {
diff --git a/src/lib/hooks/use-current-coaching-relationship.ts b/src/lib/hooks/use-current-coaching-relationship.ts
index 50aaa92d..52462625 100644
--- a/src/lib/hooks/use-current-coaching-relationship.ts
+++ b/src/lib/hooks/use-current-coaching-relationship.ts
@@ -3,15 +3,39 @@
import { useCoachingRelationship } from "@/lib/api/coaching-relationships";
import { useCoachingRelationshipStateStore } from "@/lib/providers/coaching-relationship-state-store-provider";
import { useCurrentOrganization } from "./use-current-organization";
+import type { CoachingRelationshipWithUserNames } from "@/types/coaching-relationship";
+import type { EntityApiError } from "@/types/general";
+
+/**
+ * Return type for the useCurrentCoachingRelationship hook.
+ */
+export interface UseCurrentCoachingRelationshipReturn {
+ /** Current coaching relationship ID from state store */
+ currentCoachingRelationshipId: string | null;
+ /** Full coaching relationship data from SWR (null if no IDs set) */
+ currentCoachingRelationship: CoachingRelationshipWithUserNames | null;
+ /** Loading state from SWR */
+ isLoading: boolean;
+ /** Error state from SWR */
+ isError: EntityApiError | false;
+ /** Current organization ID (needed for relationship operations) */
+ currentOrganizationId: string | null;
+ /** Function to set the current coaching relationship ID in store */
+ setCurrentCoachingRelationshipId: (id: string) => void;
+ /** Function to reset the coaching relationship state */
+ resetCoachingRelationshipState: () => void;
+ /** Function to refresh/revalidate the relationship data */
+ refresh: () => Promise;
+}
/**
* Hook that provides current coaching relationship state and data.
* Tracks the active coaching relationship ID and fetches the full relationship
* data using SWR when both organization ID and relationship ID are available.
- *
+ *
* @returns Object containing current relationship ID, full relationship data, loading state, and error state
*/
-export const useCurrentCoachingRelationship = () => {
+export const useCurrentCoachingRelationship = (): UseCurrentCoachingRelationshipReturn => {
const { currentCoachingRelationshipId, setCurrentCoachingRelationshipId, resetCoachingRelationshipState } =
useCoachingRelationshipStateStore((state) => state);
diff --git a/src/lib/hooks/use-current-coaching-session.ts b/src/lib/hooks/use-current-coaching-session.ts
index b1daee9f..7b363e58 100644
--- a/src/lib/hooks/use-current-coaching-session.ts
+++ b/src/lib/hooks/use-current-coaching-session.ts
@@ -3,16 +3,34 @@
import { useParams } from "next/navigation";
import { useCoachingSession } from "@/lib/api/coaching-sessions";
import { Id } from "@/types/general";
+import type { CoachingSession } from "@/types/coaching-session";
+import type { EntityApiError } from "@/types/general";
+
+/**
+ * Return type for the useCurrentCoachingSession hook.
+ */
+export interface UseCurrentCoachingSessionReturn {
+ /** Current coaching session ID from URL path parameter */
+ currentCoachingSessionId: string | null;
+ /** Full coaching session data from SWR (null if no ID in URL) */
+ currentCoachingSession: CoachingSession | null;
+ /** Loading state from SWR */
+ isLoading: boolean;
+ /** Error state from SWR */
+ isError: EntityApiError | false;
+ /** Function to refresh/revalidate the session data */
+ refresh: () => Promise;
+}
/**
* Hook that gets the current coaching session ID from URL path parameters
* and fetches the full coaching session data using SWR.
- *
+ *
* URL structure: /coaching-sessions/[id]
- *
+ *
* @returns Object containing current session ID, full session data, loading state, and error state
*/
-export const useCurrentCoachingSession = () => {
+export const useCurrentCoachingSession = (): UseCurrentCoachingSessionReturn => {
const params = useParams();
// Extract coaching session ID from URL path params (/coaching-sessions/123)
diff --git a/src/lib/relationships/relationship-utils.ts b/src/lib/relationships/relationship-utils.ts
index ca35a4b5..8f21ac73 100644
--- a/src/lib/relationships/relationship-utils.ts
+++ b/src/lib/relationships/relationship-utils.ts
@@ -1,4 +1,4 @@
-import { CoachingRelationshipWithUserNames } from "@/types/coaching_relationship";
+import { CoachingRelationshipWithUserNames } from "@/types/coaching-relationship";
import { User } from "@/types/user";
import { RelationshipRole } from "@/types/relationship-role";
diff --git a/src/lib/sessions/session-utils.ts b/src/lib/sessions/session-utils.ts
index 54d01d0a..1d9a5f96 100644
--- a/src/lib/sessions/session-utils.ts
+++ b/src/lib/sessions/session-utils.ts
@@ -1,6 +1,6 @@
import { DateTime } from "ts-luxon";
import { CoachingSession } from "@/types/coaching-session";
-import { CoachingRelationshipWithUserNames } from "@/types/coaching_relationship";
+import { CoachingRelationshipWithUserNames } from "@/types/coaching-relationship";
import { User } from "@/types/user";
import {
SessionUrgency,
diff --git a/src/lib/utils/user-roles.ts b/src/lib/utils/user-roles.ts
index 0f2e6670..89e660c8 100644
--- a/src/lib/utils/user-roles.ts
+++ b/src/lib/utils/user-roles.ts
@@ -1,5 +1,5 @@
import { User, Role } from "@/types/user";
-import { CoachingRelationshipWithUserNames, isUserCoach, isUserCoachee } from "@/types/coaching_relationship";
+import { CoachingRelationshipWithUserNames, isUserCoach, isUserCoachee } from "@/types/coaching-relationship";
import { RelationshipRole } from "@/types/relationship-role";
import { Id } from "@/types/general";
diff --git a/src/types/coaching_relationship.ts b/src/types/coaching-relationship.ts
similarity index 100%
rename from src/types/coaching_relationship.ts
rename to src/types/coaching-relationship.ts
diff --git a/src/types/coaching-session.ts b/src/types/coaching-session.ts
index aa061aac..8800dafa 100644
--- a/src/types/coaching-session.ts
+++ b/src/types/coaching-session.ts
@@ -1,7 +1,7 @@
import { DateTime } from "ts-luxon";
import { Id } from "@/types/general";
import { SortOrder } from "@/types/sorting";
-import { CoachingRelationship } from "@/types/coaching_relationship";
+import { CoachingRelationship } from "@/types/coaching-relationship";
import { User } from "@/types/user";
import { Organization } from "@/types/organization";
import { OverarchingGoal } from "@/types/overarching-goal";
diff --git a/src/types/session-title.ts b/src/types/session-title.ts
index 09e4f695..ec365526 100644
--- a/src/types/session-title.ts
+++ b/src/types/session-title.ts
@@ -1,5 +1,5 @@
import { DateTime } from "ts-luxon";
-import { CoachingRelationshipWithUserNames } from "./coaching_relationship";
+import { CoachingRelationshipWithUserNames } from "./coaching-relationship";
import { CoachingSession } from "./coaching-session";
import { siteConfig } from "@/site.config";
import { getDateTimeFromString } from "./general";