Quran_Tech_Server / F_Pro /src /lib /StudentContext.tsx
aboalaa147's picture
Initial deployment
eb6a2f9
Raw
History Blame Contribute Delete
5.63 kB
import React, { createContext, useContext, useEffect, useState, useCallback } from 'react';
import { useAuth } from './authContext';
import { getApiBaseUrl } from './api/config';
const BASE_URL = getApiBaseUrl();
const TOKEN_KEY = 'authToken';
// โ”€โ”€โ”€ Types โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
export interface SheikhDto {
id: number;
name: string;
country: string;
rating: number;
pricePerHour: number;
experienceYears: number;
description: string;
specializations: string;
}
export interface SheikhProfileDto extends SheikhDto {
bio?: string;
certificateUrl?: string;
profilePhotoUrl?: string;
}
interface FilterRequest {
name: string | null;
country: string | null;
ratingAsc: boolean | null;
priceAsc: boolean | null;
}
// โ”€โ”€โ”€ API helpers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
function getHeaders(): HeadersInit {
const token = localStorage.getItem(TOKEN_KEY) ?? '';
return {
'Content-Type': 'application/json',
...(token ? { Authorization: `Bearer ${token}` } : {}),
};
}
export async function apiFetchAllSheikhs(): Promise<SheikhDto[]> {
const res = await fetch(`${BASE_URL}/api/sheikhs/allSheikhsToStudents`, {
headers: getHeaders(),
});
if (!res.ok) throw new Error(`Failed to fetch sheikhs: ${res.status}`);
const data = await res.json();
if (!Array.isArray(data)) return [];
// Debug: log first sheikh to see the structure
if (data.length > 0) {
console.log('[StudentContext] Sheikh data structure:', Object.keys(data[0]));
console.log('[StudentContext] First sheikh:', data[0]);
}
return data;
}
export async function apiFilterSheikhs(filter: FilterRequest): Promise<SheikhDto[]> {
const res = await fetch(`${BASE_URL}/api/sheikhs/filter`, {
method: 'POST',
headers: getHeaders(),
body: JSON.stringify(filter),
});
if (!res.ok) throw new Error(`Failed to filter sheikhs: ${res.status}`);
const data = await res.json();
return Array.isArray(data) ? data : [];
}
export async function apiFetchSheikhById(id: string): Promise<SheikhProfileDto> {
if (!id || id === 'undefined') {
throw new Error('Invalid sheikh ID');
}
// โœ… ุงุณุชุฎุฏู… /profile ูƒู…ุง ู‡ูˆ ู…ุฐูƒูˆุฑ ููŠ ุงู„ุชูˆุซูŠู‚
const res = await fetch(`${BASE_URL}/api/sheikhs/${id}/profile`, {
headers: getHeaders(),
});
if (res.status === 401 || res.status === 403) {
throw new Error('Please login again - session expired');
}
if (!res.ok) throw new Error(`Sheikh not found: ${res.status}`);
const data = await res.json();
// โœ… ุงู„ู€ API ุจูŠุฑุฌุน { profile, numberOfSessions }
if (!data.profile) {
throw new Error('Invalid profile data');
}
// โœ… ุฃุฑุฌุน ุงู„ู€ profile ูู‚ุท (ุฃูˆ ุงุฑุฌุน data ูƒุงู…ู„ุฉ ู„ูˆ ู…ุญุชุงุฌ numberOfSessions)
return data.profile as SheikhProfileDto;
}
// โ”€โ”€โ”€ Context โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
interface StudentContextType {
sheikhs: SheikhDto[];
loading: boolean;
error: string | null;
fetchSheikhs: () => Promise<void>;
filterSheikhs: (filter: FilterRequest) => Promise<void>;
fetchSheikhById: (id: string) => Promise<SheikhProfileDto | null>;
}
const StudentContext = createContext<StudentContextType | undefined>(undefined);
export function StudentProvider({ children }: { children: React.ReactNode }) {
const { user } = useAuth();
const [sheikhs, setSheikhs] = useState<SheikhDto[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const fetchSheikhs = async () => {
if (!user || user.role !== 'student') return;
setLoading(true);
setError(null);
try {
const data = await apiFetchAllSheikhs();
setSheikhs(data);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to load sheikhs');
} finally {
setLoading(false);
}
};
const filterSheikhs = async (filter: FilterRequest) => {
setLoading(true);
setError(null);
try {
const data = await apiFilterSheikhs(filter);
setSheikhs(data);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to filter sheikhs');
} finally {
setLoading(false);
}
};
const fetchSheikhById = useCallback(async (id: string): Promise<SheikhProfileDto | null> => {
try {
return await apiFetchSheikhById(id);
} catch (err) {
console.error('Failed to fetch sheikh:', err);
setError(err instanceof Error ? err.message : 'Failed to load sheikh profile');
return null;
}
}, []); // stable reference โ€” apiFetchSheikhById is a module-level function
useEffect(() => {
if (user?.role === 'student') {
fetchSheikhs();
}
}, [user]);
return (
<StudentContext.Provider value={{
sheikhs,
loading,
error,
fetchSheikhs,
filterSheikhs,
fetchSheikhById
}}>
{children}
</StudentContext.Provider>
);
}
export function useStudent() {
const context = useContext(StudentContext);
if (!context) throw new Error('useStudent must be used within StudentProvider');
return context;
}