Skip to content
Merged
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use client';
import { z } from 'zod';
import Link from 'next/link';
import React, { useState } from 'react';
import { useRouter } from 'next/navigation';
import { forgotPasswordSchema } from './forgotPasswordSchema';
Expand All @@ -11,6 +10,7 @@ import { forgotPasswordApiCall } from '@src/apiServices/authService';
import { toast } from 'react-toastify';
import { showApiErrorInToast } from '@src/utils/toastUtils';
import { en } from '@src/constants/lang/en';
import { SuperLink } from '@src/utils/HiLink';

const ForgotPassword = () => {
const router = useRouter();
Expand Down Expand Up @@ -49,12 +49,12 @@ const ForgotPassword = () => {
/>
<p className="text-sm font-light text-gray-500 dark:text-gray-400">
{en.Auth.Login} &nbsp;
<Link
<SuperLink
href={RouteEnum.LOGIN}
className="font-medium text-primary-600 hover:underline dark:text-primary-500"
>
{en.Auth.SignIn}
</Link>
</SuperLink>
</p>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { DateFormats } from '@src/constants/dateFormats';
import { en } from '@src/constants/lang/en';
import { RouteEnum } from '@src/constants/route.enum';
import { format } from 'date-fns';
import Link from 'next/link';
import { useEffect } from 'react';
import ApprovalListSkeleton from './ApprovalListSkeleton';
import { RootState } from '@src/store/store';
import { fetchUnapprovedLessons } from '@src/store/features/approvalSlice';
import { useAppDispatch, useAppSelector } from '@src/store/hooks';
import { updateSystemPreferencesData } from '@src/store/features/systemPreferenceSlice';
import { SuperLink } from '@src/utils/HiLink';

const columns = [
en.common.lesson,
Expand Down Expand Up @@ -83,12 +83,12 @@ const ApprovalList = () => {
className="px-4 py-2 font-medium text-gray-900 whitespace-nowrap dark:text-white"
>
<div className="flex items-center">
<Link
<SuperLink
className="ml-2 hover:underline"
href={`${RouteEnum.APPROVALS}/${lesson.id}`}
>
{lesson.name}
</Link>
</SuperLink>
</div>
</th>
<td className="px-4 py-2">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import ViewLesson from '@src/shared/components/ViewLesson';
import { TBreadcrumb } from '@src/shared/types/breadcrumbType';
import { TLesson, TRoadmap } from '@src/shared/types/contentRepository';
import { selectUser } from '@src/store/features/userSlice';
import { SuperLink } from '@src/utils/HiLink';
import { showApiErrorInToast } from '@src/utils/toastUtils';
import { UserTypeIdEnum } from 'lib/shared/src';
import Link from 'next/link';
import { useParams, useRouter } from 'next/navigation';
import { useEffect, useMemo, useState } from 'react';
import { useSelector } from 'react-redux';
Expand Down Expand Up @@ -87,13 +87,13 @@ const LessonDetails = () => {
<>
<ViewLesson lesson={lesson} links={links} isPending={true} />
{canEdit && (
<Link
<SuperLink
href={`${RouteEnum.CONTENT}/${roadmapId}/${courseId}/edit/${lessonId}`}
>
<span className="fixed flex items-center bottom-4 right-4 rounded-full bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600 disabled:bg-gray-500">
<PencilIcon className="flex-shrink-0 inline w-4 h-4 me-1" />| Edit
</span>
</Link>
</SuperLink>
)}
</>
);
Expand Down
49 changes: 33 additions & 16 deletions apps/quick-learn-frontend/src/app/(Dashboard)/dashboard/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@ import { getUser } from '@src/apiServices/authService';
import { RouteEnum } from '@src/constants/route.enum';
import { useFetchContentRepositoryMetadata } from '@src/context/contextHelperService';
import Navbar from '@src/shared/components/Navbar';
import { TUser } from '@src/shared/types/userTypes';
import { selectHideNavbar } from '@src/store/features/uiSlice';
import { setUser } from '@src/store/features/userSlice';
import { useAppDispatch, useAppSelector } from '@src/store/hooks';
import { usePathname } from 'next/navigation';
import { useEffect } from 'react';
import { useEffect, useRef } from 'react';

// Create a module-level flag for the entire session
let hasInitialFetchStarted = false;
let currentFetchPromise: Promise<TUser | void> | null = null;

export default function Layout({
children,
Expand All @@ -19,26 +24,38 @@ export default function Layout({
const pathname = usePathname();
const { fetchMetadata, fetchApprovalData } =
useFetchContentRepositoryMetadata();
const isFetching = useRef(false);

useEffect(() => {
const fetchData = async () => {
getUser()
.then((res) => {
if (hasInitialFetchStarted || isFetching.current || currentFetchPromise) {
return;
}

isFetching.current = true;

currentFetchPromise = getUser()
.then(async (res) => {
if (!hasInitialFetchStarted) {
hasInitialFetchStarted = true;
dispatch(setUser(res.data));
fetchMetadata(res?.data.user_type_id);
fetchApprovalData(res?.data.user_type_id);
})
.catch((err) => console.log(err));
};
fetchData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

// Add paths that should be full width
const fullWidthPaths = [RouteEnum.MY_LEARNING_PATH];

const isFullWidth = fullWidthPaths.some((path) => pathname?.startsWith(path));
await fetchMetadata(res?.data.user_type_id);
await fetchApprovalData(res?.data.user_type_id);
}
return res.data;
})
.catch((err) => {
hasInitialFetchStarted = false;
currentFetchPromise = null;
console.log(err);
})
.finally(() => {
isFetching.current = false;
});
}, [dispatch, fetchMetadata, fetchApprovalData]);

const fullWidthPaths = [RouteEnum.MY_LEARNING_PATH];
const isFullWidth = fullWidthPaths.some((path) => pathname?.startsWith(path));
const mainClasses = isFullWidth
? 'mt-16 w-full'
: 'max-w-screen-2xl mx-auto mt-16 py-3 px-4 sm:py-5 lg:px-8';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use client';
import { RouteEnum } from '@src/constants/route.enum';
import Link from 'next/link';
import { useMemo, useState } from 'react';
import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/24/outline';
import { TUserType } from '@src/shared/types/userTypes';
Expand All @@ -15,6 +14,7 @@ import {
} from '@src/store/features/teamSlice';
import { useAppDispatch, useAppSelector } from '@src/store/hooks';
import { en } from '@src/constants/lang/en';
import { SuperLink } from '@src/utils/HiLink';
const TeamMemberListing = () => {
const dispatch = useAppDispatch();
const [searchInputValue, setSearchInputValue] = useState(''); // Local state for input value
Expand Down Expand Up @@ -60,6 +60,12 @@ const TeamMemberListing = () => {
debouncedSearch(value); // Debounce the Redux update and API call
};

function showRange() {
const initial = filteredTotal === 0 ? 0 : (currentPage - 1) * 10 + 1;
const end = Math.min(currentPage * 10, filteredTotal);
return `${initial} ${en.teams.to} ${end} ${en.teams.of} ${filteredTotal}`;
}

return (
<>
<section className="relative overflow-hidden bg-white shadow-md sm:rounded-sm">
Expand All @@ -81,13 +87,13 @@ const TeamMemberListing = () => {
onChange={handleSearchChange}
id="search"
/>
<Link
<SuperLink
id="addNewMember"
href={`${RouteEnum.TEAM_EDIT}/add`}
className="cursor-pointer items-center justify-center px-4 py-2 text-sm font-medium text-white rounded bg-primary-700 hover:bg-primary-800 focus:ring-2 focus:ring-primary-300"
>
{en.teams.addNewMember}
</Link>
</SuperLink>
</div>
</div>
<div className="flex flex-wrap items-center gap-x-8 p-4 border-t border-b border-gray-300 space-y-3 sm:flex sm:space-y-0 sm:space-x-4">
Expand Down Expand Up @@ -131,22 +137,7 @@ const TeamMemberListing = () => {
<div>
<p className="text-sm text-gray-700">
{en.teams.showing}{' '}
<span className="font-medium">
{currentPage > 1
? (currentPage - 1) * 10 + 1
: filteredTotal === 0
? 0
: 1}
</span>{' '}
{en.teams.to}{' '}
<span className="font-medium">
{currentPage * 10 <= filteredTotal
? currentPage * 10
: filteredTotal}
</span>{' '}
{en.teams.to}
<span> </span>
<span className="font-medium">{filteredTotal}</span>{' '}
<span className="font-medium">{showRange()}</span>{' '}
{en.teams.results}
</p>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// components/TeamTable.tsx
import { useEffect } from 'react';
import { format } from 'date-fns';
import Link from 'next/link';
import { toast } from 'react-toastify';
import { DateFormats } from '@src/constants/dateFormats';
import { CustomClipBoardIcon } from '@src/shared/components/UIElements';
Expand All @@ -11,7 +10,7 @@ import TeamMemberListingSkeleton from './TeamMemberListingSkeleton';
import { RootState } from '@src/store/store';
import { fetchTeamMembers } from '@src/store/features/teamSlice';
import { useAppDispatch, useAppSelector } from '@src/store/hooks';

import { SuperLink } from '@src/utils/HiLink';
const TeamTable = () => {
const dispatch = useAppDispatch();
const {
Expand Down Expand Up @@ -94,10 +93,10 @@ const TeamTable = () => {
key={user.uuid}
className="border-b border-gray-200 hover:bg-gray-100"
>
<td className="px-4 py-2 font-medium text-gray-900 whitespace-nowrap capitalize hover:underline">
<Link href={`${RouteEnum.TEAM}/${user.id}`}>
<td className="px-4 py-2 font-medium text-gray-900 whitespace-nowrap capitalize">
<SuperLink href={`${RouteEnum.TEAM}/${user.id}`}>
{user.first_name} {user.last_name}
</Link>
</SuperLink>
</td>
<td className="px-4 py-2">
<div className="inline-flex items-center bg-primary-100 text-primary-800 text-xs font-medium px-2 py-0.5 rounded capitalize">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use client';
import Link from 'next/link';
import { useParams } from 'next/navigation';
import { useParams, useRouter } from 'next/navigation';
import { useCallback, useEffect, useState } from 'react';
import {
CursorArrowRippleIcon,
Expand Down Expand Up @@ -31,7 +30,6 @@ import {
showApiMessageInToast,
} from '@src/utils/toastUtils';
import { Tooltip } from 'flowbite-react';
import { useRouter } from 'next/navigation';
import EmptyState from '@src/shared/components/EmptyStatePlaceholder';
import TeamMemberDetailsSkeleton from './TeamMemberDetailsSkeleton';
import {
Expand All @@ -47,6 +45,7 @@ import {
import ActivityGraph, { Course } from '@src/shared/modals/ActivityGraph';
import { useDispatch } from 'react-redux';
import { decrementTotalUsers } from '@src/store/features/teamSlice';
import { SuperLink } from '@src/utils/HiLink';

const defaultlinks: TBreadcrumb[] = [{ name: 'Team', link: RouteEnum.TEAM }];

Expand All @@ -65,9 +64,8 @@ const TeamMemberDetails = () => {
TRoadmapCategories[]
>([]);
const [userProgress, setUserProgress] = useState<UserLessonProgress[]>([]);
const [userDailyLessonProgressData, setUserDalyLessonProgressData] = useState<
TUserDailyProgress[]
>([]);
const [userDailyLessonProgressData, setUserDailyLessonProgressData] =
useState<TUserDailyProgress[]>([]);
const [allCourses, setAllCourses] = useState<TUserCourse[]>([]);
const [userActivityModal, setUserActivityModal] = useState(false);

Expand Down Expand Up @@ -101,7 +99,7 @@ const TeamMemberDetails = () => {

// Set the user's daily lesson progress data
getUserDailyLessonProgress(Number(userId))
.then((res) => setUserDalyLessonProgressData(res.data))
.then((res) => setUserDailyLessonProgressData(res.data))
.catch((e) => showApiErrorInToast(e));

setLinks([
Expand Down Expand Up @@ -217,19 +215,19 @@ const TeamMemberDetails = () => {
{member?.first_name} {member?.last_name}
</h1>
<p className="text-sm text-gray-500">
({member?.assigned_roadmaps?.length || 0} {en.common.roadmaps},{' '}
({member?.assigned_roadmaps?.length ?? 0} {en.common.roadmaps},{' '}
{allCourses.length} {en.common.courses})
</p>

{/* Action Buttons */}
<div className="flex items-center justify-center gap-2 mt-4">
<Tooltip content="Edit User">
<Link
<SuperLink
href={`${RouteEnum.TEAM_EDIT}/${userId}`}
className="text-black bg-gray-300 hover:bg-blue-800 hover:text-white focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-full text-sm p-2.5 text-center inline-flex items-center"
>
<PencilIcon className="h-4 w-4" />
</Link>
</SuperLink>
</Tooltip>

<Tooltip content="Deactivate User">
Expand Down
2 changes: 0 additions & 2 deletions apps/quick-learn-frontend/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
'use client';
import 'flowbite/dist/flowbite.css';
import './global.css';
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { ReduxProvider } from '@src/store/provider';
import { getClientIp } from '@src/apiServices/ipService';

Expand Down
12 changes: 8 additions & 4 deletions apps/quick-learn-frontend/src/context/contextHelperService.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
'use client';
import { getContentRepositoryMetadata } from '@src/apiServices/contentRepositoryService';
import {
getContentRepositoryMetadata,
getSystemPreferences,
} from '@src/apiServices/contentRepositoryService';
import { updateContentRepository } from '@src/store/features/metadataSlice';
import { UserTypeIdEnum } from 'lib/shared/src';
import { usePathname } from 'next/navigation';
import { useAppDispatch } from '@src/store/hooks';
import { RouteEnum } from '@src/constants/route.enum';
import { fetchSystemPreferences } from '@src/store/features/systemPreferenceSlice';
import { updateSystemPreferencesData } from '@src/store/features/systemPreferenceSlice';

// Custom hook to fetch content repository metadata
export const useFetchContentRepositoryMetadata = (forceFetch = false) => {
Expand All @@ -26,12 +29,13 @@ export const useFetchContentRepositoryMetadata = (forceFetch = false) => {
}
};

const fetchApprovalData = (user_type: number) => {
const fetchApprovalData = async (user_type: number) => {
if (
path !== RouteEnum.APPROVALS &&
![UserTypeIdEnum.EDITOR, UserTypeIdEnum.MEMBER].includes(user_type)
) {
dispatch(fetchSystemPreferences());
const res = await getSystemPreferences();
dispatch(updateSystemPreferencesData(res.data));
}
};

Expand Down
Loading