Added more documentation to the project.

This commit is contained in:
LosEvons
2025-12-01 01:24:34 +00:00
parent be312ba140
commit 21a3308499
3 changed files with 110 additions and 1 deletions
+19
View File
@@ -4,6 +4,13 @@ import useQuery from "@/shared/hooks/useQuery";
const GlossaryContext = createContext(null);
/**
* GlossaryProvider - React context provider that fetches glossary entries
* and exposes them to the component tree.
*
* @param {{children: import('react').ReactNode}} props Component children.
* @returns {JSX.Element} Provider supplying `{ entries, wordMap, isLoading, error }`.
*/
export function GlossaryProvider({ children }) {
const { data: entries = [], isLoading, error } = useQuery("/glossary");
@@ -29,6 +36,18 @@ export function GlossaryProvider({ children }) {
</GlossaryContext.Provider>
);
}
/**
* useGlossary - hook to consume glossary context.
*
* @returns {{
* entries: Array<any>,
* wordMap: Record<string, any>,
* isLoading: boolean,
* error: any
* }} The glossary context value.
* @throws {Error} If called outside a `GlossaryProvider`.
*/
export function useGlossary() {
const context = useContext(GlossaryContext);
+65 -1
View File
@@ -28,6 +28,13 @@ const UserContext = createContext({
});
// Create a provider component
/**
* UserProvider - React context provider that supplies authentication state
* and helper actions to the component tree.
*
* @param {{children: import('react').ReactNode}} props
* @returns {JSX.Element} The provider wrapping `children`.
*/
export function UserProvider({ children }) {
const [auth, setAuth] = useState(defaultUserState);
const makeRequest = useRequest();
@@ -37,11 +44,25 @@ export function UserProvider({ children }) {
userOptions[0]
);
// Check if user is logged in on initial load
/**
* resetAuthState - reset authentication state to defaults and clear
* persisted `actAs` selection from local storage.
*
* @returns {void}
*/
const resetAuthState = () => {
setAuth(defaultUserState);
clearActAs();
};
useEffect(() => {
/**
* checkUserSession - internal helper to call `/auth/session` and
* update the `auth` state accordingly. Allows HTTP 401 responses to
* be handled gracefully (treated as not logged in).
*
* @returns {Promise<void>} resolves when the session check finishes.
*/
async function checkUserSession() {
try {
const response = await makeRequest("/auth/session", undefined, {
@@ -76,7 +97,13 @@ export function UserProvider({ children }) {
checkUserSession();
}, [pathname]);
// Logout function
/**
* logout - perform logout request and reset client state on success.
* If the server responds with status 200 the local auth state is reset
* and the page is reloaded to clear any cached state.
*
* @returns {Promise<void>} resolves when the logout completes.
*/
const logout = async () => {
const response = await makeRequest("/auth/logout");
if (response.status === 200) {
@@ -94,15 +121,33 @@ export function UserProvider({ children }) {
}
// Custom hook to use the user context
/**
* useUser - React hook to access the `UserContext`.
*
* @returns {{auth: object, logout: function, setActAs: function, actAs: object}}
* The value provided by `UserProvider`.
*/
export function useUser() {
return useContext(UserContext);
}
/**
* useIsLoggedIn - hook that returns whether the current user is logged in.
*
* @returns {boolean} `true` when a session is active, otherwise `false`.
*/
export function useIsLoggedIn() {
const { auth } = useUser();
return auth?.isLoggedIn ?? false;
}
/**
* useLoggedOutGuard - redirect guard for routes that should be visible
* only to logged-out users. When called inside a component it will
* navigate to `/` if the user is already authenticated.
*
* @returns {void}
*/
export const useLoggedOutGuard = () => {
const { auth } = useUser();
const router = useRouter();
@@ -111,6 +156,14 @@ export const useLoggedOutGuard = () => {
}, [auth, router]);
};
/**
* useIsAdmin - hook that enforces admin-only access to a route and returns
* whether the current user has admin privileges. The hook will redirect
* non-admin users to the root (`/`) when used inside a component.
*
* @returns {boolean|undefined} `true` if current user is admin, `false`
* if not an admin, or `undefined` when there is no authenticated user yet.
*/
export function useIsAdmin() {
const router = useRouter();
const pathname = usePathname();
@@ -127,6 +180,17 @@ export function useIsAdmin() {
return auth?.user?.id ? auth.user.is_admin : undefined;
}
/**
* checkUseIsAdmin - utility that inspects the current `UserContext` and
* returns a boolean indicating admin rights.
*
* Note: this function relies on `useUser()` so it must be called from
* React component or hook context (i.e. during render) to access the
* current context value.
*
* @returns {boolean|undefined} `true` when user is admin, `false` when
* not an admin, or `undefined` when there is no authenticated user.
*/
export function checkUseIsAdmin() {
let is_admin = true;
const { auth, actAs } = useUser();
+26
View File
@@ -2,11 +2,32 @@ import { NextResponse } from "next/server";
import { AUTH_COOKIE_NAME } from "@/shared/const";
// Paths only logged-in users can access
/**
* authRequired - array of path prefixes that require an authenticated
* session (users without a valid session cookie will be redirected
* to the login page when attempting to access these paths).
* @type {string[]}
*/
const authRequired = ["/grammar/exercises", "/edit_info"];
// Restricted paths logged-in users cannot access
/**
* unauthRequired - array of path prefixes that should not be accessible
* to already authenticated users (e.g. login/register pages).
* @type {string[]}
*/
const unauthRequired = ["/auth/login", "/auth/register"];
/**
* middleware - Next.js middleware that protects certain routes by
* checking for an authentication cookie. If a user without a valid
* session attempts to access a protected path they are redirected to
* the login page and the original path is provided as a `redirect`
* query parameter. Responses are marked non-cacheable.
*
* @param {import('next/server').NextRequest} request
* @returns {import('next/server').NextResponse}
*/
export async function middleware(request) {
const { pathname } = request.nextUrl;
const sessionCookie = request.cookies.get(AUTH_COOKIE_NAME);
@@ -24,6 +45,11 @@ export async function middleware(request) {
return response;
}
/**
* config - Next.js middleware configuration. The `matcher` controls which
* paths the middleware runs for; this pattern excludes API routes, Next
* internals and common static files.
*/
export const config = {
matcher: [
"/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)",