Files
conference-room-booking-system/frontend/src/context/UserContext.tsx

101 lines
2.7 KiB
TypeScript

/**
* UserContext
* Provides user data and loading/error state for users.
* @context
*/
// External imports
import React, {
createContext,
useContext,
useEffect,
useState,
ReactNode,
} from "react";
// API imports
import { getUsers } from "../apis/users";
// Type-only imports
import type { User } from "../interfaces";
// Context imports
import { useBookings } from "./BookingContext";
/**
* useAvailableUsers
* Hook to get available users for a time slot using context.
* @param {Object} params - Parameters for filtering users
* @param {string} params.start_time - Start time
* @param {string} params.end_time - End time
* @param {string|number} [params.exclude_booking_id] - Booking ID to exclude
* @returns {User[]} Array of available users
*/
export function useAvailableUsers(params: {
start_time: string;
end_time: string;
exclude_booking_id?: string | number;
}): User[] {
const { users } = useUsers();
const { bookings } = useBookings();
const start = new Date(params.start_time).getTime();
const end = new Date(params.end_time).getTime();
const overlappingBookings = bookings.filter((b) => {
if (params.exclude_booking_id && b.id === params.exclude_booking_id)
return false;
const bStart = new Date(b.start_time).getTime();
const bEnd = new Date(b.end_time).getTime();
return bStart < end && bEnd > start;
});
const busyInvitees = new Set<string>();
overlappingBookings.forEach((b) => {
(b.invitees || []).forEach((email) => busyInvitees.add(email));
});
return users.filter((u) => u.email && !busyInvitees.has(u.email));
}
interface UserContextType {
users: User[];
loading: boolean;
error: Error | null;
}
const UserContext = createContext<UserContextType | undefined>(undefined);
/**
* UserProvider
* Provides user context to child components.
* @param {ReactNode} children - Child components
* @returns {JSX.Element} User context provider
*/
export const UserProvider: React.FC<{ children: ReactNode }> = ({
children,
}) => {
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
getUsers()
.then((data) => {
setUsers(data || []);
setLoading(false);
})
.catch((err) => {
setError(err);
setLoading(false);
});
}, []);
return (
<UserContext.Provider value={{ users, loading, error }}>
{children}
</UserContext.Provider>
);
};
export const useUsers = () => {
const context = useContext(UserContext);
if (!context) throw new Error("useUsers must be used within a UserProvider");
return context;
};