Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions frontend/src/app/signup/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
'use client'

import { usePathname } from 'next/navigation'
import Image from 'next/image'
import { Check } from 'lucide-react'
Expand All @@ -17,7 +16,7 @@ export default function OnboardingLayout({
children: React.ReactNode
}) {
const pathname = usePathname()

// Determine current step index
const getCurrentStepIndex = () => {
// Handle sub-routes like students-add, students-edit
Expand All @@ -34,14 +33,15 @@ export default function OnboardingLayout({

return stepIndex
}

const currentStepIndex = getCurrentStepIndex()

return (
<div className="min-h-screen flex bg-background">
{/* Left Sidebar Progress */}
<div className="w-64 border-default p-8 flex flex-col bg-black border-r border-default
<div className="fixed left-0 top-0 w-64 border-default p-8 flex flex-col bg-black border-r border-default
transition-all duration-300 ease-in-out shadow-md h-screen z-50 lg:z-auto text-center items-center">

<div className="mb-12 bg-white rounded-md p-1 w-fit">
<Image
src="/tss.png"
Expand All @@ -51,31 +51,31 @@ export default function OnboardingLayout({
priority
/>
</div>

<div className="flex-1">
<div className="space-y-1">
{steps.map((step, index) => {
const isCompleted = index < currentStepIndex
const isCurrent = index === currentStepIndex
const isUpcoming = index > currentStepIndex

return (
<div key={step.id} className="flex items-start">
<div className="flex flex-col items-center">
{/* Step indicator */}
<div className={`w-12 h-12 rounded-full flex items-center justify-center text-sm font-medium
${isCompleted ? 'bg-accent border-2 border-gray-200 text-white' : ''}
${isCurrent ? 'bg-accent text-amber-400 border-2 border-amber-400' : ''}
${isUpcoming ? 'border-gray-200 border-2 text-white' : ''}
${!isCompleted && !isCurrent && !isUpcoming ? 'text-primary' : ''}
`}>
${isCompleted ? 'bg-accent border-2 border-gray-200 text-white' : ''}
${isCurrent ? 'bg-accent text-amber-400 border-2 border-amber-400' : ''}
${isUpcoming ? 'border-gray-200 border-2 text-white' : ''}
${!isCompleted && !isCurrent && !isUpcoming ? 'text-primary' : ''}
`}>
{isCompleted ? (
<Check className="w-4 h-4" />
) : (
<span>{index + 1}</span>
)}
</div>

{/* Connector line */}
{index < steps.length - 1 && (
<div className={`
Expand All @@ -90,9 +90,9 @@ export default function OnboardingLayout({
</div>
</div>
</div>

{/* Main Content */}
<div className="flex-1 overflow-auto">
<div className="flex-1 ml-64 overflow-y-auto min-h-screen">
{children}
</div>
</div>
Expand Down
7 changes: 5 additions & 2 deletions frontend/src/app/signup/sessions/add/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ export default function AddSessionsPage() {
JSON.stringify(existingSessions)
);

router.push("/signup/sessions");
// Navigate with refresh flag to trigger reload
router.push("/signup/sessions?refresh=true");
} catch (error) {
console.error("Failed to create session:", error);
setError(
Expand All @@ -160,7 +161,8 @@ export default function AddSessionsPage() {
}

return (
<div className="flex items-center justify-center min-h-screen p-8">
<div className="flex items-center justify-center min-h-screen p-8 overflow-y-auto">
<div className="w-full flex justify-center py-8">
<div className="max-w-md w-full">
<button
onClick={handleBack}
Expand Down Expand Up @@ -386,6 +388,7 @@ export default function AddSessionsPage() {
</div>
</form>
</div>
</div>
</div>
);
}
142 changes: 78 additions & 64 deletions frontend/src/app/signup/sessions/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,48 @@ import { useEffect, useState } from "react";

export default function SessionsPage() {
const router = useRouter();
const { sessions, isLoading: loadingSessions, refetch } = useSessions();
const [selectedSessionId, setSelectedSessionId] = useState<string>("");

// Use the hook to get students for the selected session
// Hook now handles therapist ID from both auth context AND localStorage
const { sessions, isLoading: loadingSessions, refetch } = useSessions();

const { students: sessionStudents, isLoading: loadingStudents } =
useSessionStudentsForSession(selectedSessionId);

// Get the first session's ID when sessions load
useEffect(() => {
if (sessions.length > 0 && !selectedSessionId) {
setSelectedSessionId(sessions[0].id);
}
}, [sessions, selectedSessionId]);

// Check if user is authenticated
useEffect(() => {
const userId =
localStorage.getItem("temp_userId") || localStorage.getItem("userId"); // Check temp_userId first
localStorage.getItem("temp_userId") || localStorage.getItem("userId");

if (!userId) {
router.push("/signup/welcome");
}
}, [router]);

// Handle refresh from navigation and initial load
useEffect(() => {
if (typeof window !== 'undefined') {
const searchParams = new URLSearchParams(window.location.search);
if (searchParams.get('refresh') === 'true') {
// Add small delay to ensure session is saved
setTimeout(() => {
refetch();
}, 300);
// Clean up URL
window.history.replaceState({}, '', '/signup/sessions');
} else {
// Normal refetch on mount
refetch();
}
}
}, [refetch]);

// Refetch sessions when page loads
refetch();
}, [router, refetch]);
// Set first session as selected
useEffect(() => {
if (sessions.length > 0 && !selectedSessionId) {
setSelectedSessionId(sessions[0].id);
}
}, [sessions, selectedSessionId]);

const handleBack = () => {
router.push("/signup/students");
Expand All @@ -50,7 +67,6 @@ export default function SessionsPage() {
router.push("/signup/complete");
};

// Helper functions for formatting
const formatTime = (datetime: string) => {
const date = new Date(datetime);
return date.toLocaleTimeString("en-US", {
Expand Down Expand Up @@ -120,63 +136,61 @@ export default function SessionsPage() {
</>
) : (
<>
{/* Session Card Display */}
<div className="bg-card rounded-lg border border-default p-6 mb-6">
{sessions.length > 0 && (
<div className="space-y-4">
<div className="text-sm text-secondary mb-2">
<div>Session Name</div>
<div className="font-semibold text-primary">
{formatTime(sessions[0].start_datetime)} -{" "}
{formatTime(sessions[0].end_datetime)}, Does not repeat
</div>
{sessions[0].location && (
<div className="mt-1">{sessions[0].location}</div>
)}
<div className="space-y-4">
<div className="text-sm text-secondary mb-2">
<div className="font-semibold text-primary text-lg mb-1">
{sessions[0].session_name}
</div>
<div className="font-semibold text-primary">
{formatTime(sessions[0].start_datetime)} -{" "}
{formatTime(sessions[0].end_datetime)}, Does not repeat
</div>
{sessions[0].location && (
<div className="mt-1">{sessions[0].location}</div>
)}
</div>

<div>
<div className="text-sm text-secondary mb-2">Students</div>
<div className="space-y-2">
{loadingStudents ? (
<div className="text-sm text-secondary">
Loading students...
</div>
) : sessionStudents.length > 0 ? (
sessionStudents.map((student: any) => (
<div
key={student.id}
className="flex items-center gap-3 p-2 bg-background rounded-lg border border-default"
>
<Avatar
name={getAvatarName(
student.first_name,
student.last_name,
student.id
)}
variant={getAvatarVariant(student.id)}
className="w-8 h-8"
/>
<span className="text-sm text-primary">
{student.first_name} {student.last_name}
</span>
<span className="text-xs text-secondary ml-auto">
ID
</span>
</div>
))
) : (
<div className="text-sm text-secondary">
No students in this session
<div>
<div className="text-sm text-secondary mb-2">Students</div>
<div className="space-y-2">
{loadingStudents ? (
<div className="text-sm text-secondary">
Loading students...
</div>
) : sessionStudents.length > 0 ? (
sessionStudents.map((student: any) => (
<div
key={student.id}
className="flex items-center gap-3 p-2 bg-background rounded-lg border border-default"
>
<Avatar
name={getAvatarName(
student.first_name,
student.last_name,
student.id
)}
variant={getAvatarVariant(student.id)}
className="w-8 h-8"
/>
<span className="text-sm text-primary">
{student.first_name} {student.last_name}
</span>
<span className="text-xs text-secondary ml-auto">
ID
</span>
</div>
)}
</div>
))
) : (
<div className="text-sm text-secondary">
No students in this session
</div>
)}
</div>
</div>
)}
</div>
</div>

{/* Show other sessions as cards */}
{sessions.length > 1 && (
<div className="space-y-2 mb-6">
<p className="text-sm text-secondary mb-2">
Expand Down Expand Up @@ -217,4 +231,4 @@ export default function SessionsPage() {
</div>
</div>
);
}
}
2 changes: 1 addition & 1 deletion frontend/src/app/signup/students/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export default function StudentsPage() {
</p>
<Button
onClick={handleAddStudent}
className="bg-accent hover:bg-accent-hover text-white"
className="text-white"
>
<Plus className="w-4 h-4 mr-2" />
Add Your First Student
Expand Down
26 changes: 20 additions & 6 deletions frontend/src/hooks/useSessions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// hooks/useSessions.ts - UPDATED WITH STUDENTESSIONS CACHE INVALIDATION
// hooks/useSessions.ts

import { useAuthContext } from "@/contexts/authContext";
import { getSessions as getSessionsApi } from "@/lib/api/sessions";
Expand All @@ -9,6 +9,7 @@ import type {
} from "@/lib/api/theSpecialStandardAPI.schemas";
import type { QueryObserverResult } from "@tanstack/react-query";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useEffect, useState } from "react";

interface UseSessionsReturn {
sessions: Session[];
Expand Down Expand Up @@ -43,23 +44,38 @@ interface UseSessionReturn {
export function useSessions(params?: UseSessionsParams): UseSessionsReturn {
const queryClient = useQueryClient();
const api = getSessionsApi();
const { userId: therapistId } = useAuthContext();
const { userId: contextUserId } = useAuthContext();

// Support onboarding flow by checking localStorage
const [therapistId, setTherapistId] = useState<string | undefined>(contextUserId ?? undefined);

useEffect(() => {
// If we don't have a therapist ID from context, check localStorage (onboarding)
if (!contextUserId && typeof window !== 'undefined') {
const tempUserId = localStorage.getItem("temp_userId") || localStorage.getItem("userId");
if (tempUserId) {
setTherapistId(tempUserId);
}
} else if (contextUserId) {
setTherapistId(contextUserId);
}
}, [contextUserId]);

const {
data: sessionsResponse,
isLoading,
error,
refetch,
} = useQuery({
queryKey: ["sessions", params],
queryKey: ["sessions", params, therapistId],
queryFn: () =>
api.getSessions({
limit: params?.limit ?? 100,
startdate: params?.startdate,
enddate: params?.enddate,
therapist_id: therapistId!,
}),
enabled: !!therapistId,
enabled: !!therapistId, // Only run when we have a valid therapist ID
});

const sessions = sessionsResponse ?? [];
Expand All @@ -81,10 +97,8 @@ export function useSessions(params?: UseSessionsParams): UseSessionsReturn {
mutationFn: ({ id, data }: { id: string; data: UpdateSessionInput }) =>
api.patchSessionsId(id, data),
onSuccess: () => {
// Invalidate all session-related queries
queryClient.invalidateQueries({ queryKey: ["sessions"] });
queryClient.invalidateQueries({ queryKey: ["session"] });
// Invalidate all studentSessions queries since session notes are displayed there
queryClient.invalidateQueries({
queryKey: ["studentSessions"],
exact: false,
Expand Down