diff --git a/src/Chart/PieChart.tsx b/src/Chart/PieChart.tsx index 2ad039f2..bc60e08e 100644 --- a/src/Chart/PieChart.tsx +++ b/src/Chart/PieChart.tsx @@ -61,7 +61,7 @@ const PieChart: React.FC = () => { return (
-
+
diff --git a/src/Chart/ProgressBar.tsx b/src/Chart/ProgressBar.tsx new file mode 100644 index 00000000..162309a2 --- /dev/null +++ b/src/Chart/ProgressBar.tsx @@ -0,0 +1,41 @@ +import React from 'react'; + +interface ProgressBarProps { + passedPercentage: number; // Percentage of passed logins + failedPercentage: number; // Percentage of failed logins +} + +function ProgressBar({ passedPercentage, failedPercentage }: ProgressBarProps) { + return ( +
+
+
+ {passedPercentage}% +
+
+ {failedPercentage}% +
+
+
+

+ Green: Passed + Logins +

+

+ Red: Failed Logins +

+
+
+ ); +} + +export default ProgressBar; diff --git a/src/components/AdminDashboardTable.tsx b/src/components/AdminDashboardTable.tsx index 7bc3808e..af90dc24 100644 --- a/src/components/AdminDashboardTable.tsx +++ b/src/components/AdminDashboardTable.tsx @@ -1,12 +1,16 @@ import { useQuery } from '@apollo/client'; -import React from 'react'; +import React, { useState } from 'react'; import { FaEye } from 'react-icons/fa'; import { useTranslation } from 'react-i18next'; import DataTable from './DataTable'; import { GET_TEAMS_CARDS } from './CoordinatorCard'; +import TeamDetailsModal from './AdminTeamDetails'; function DashboardTableDesign() { const { t } = useTranslation(); + const [selectedTeam, setSelectedTeam] = useState(null); + const [isModalOpen, setIsModalOpen] = useState(false); + const { data: TeamsData, loading, @@ -18,6 +22,7 @@ function DashboardTableDesign() { }, fetchPolicy: 'network-only', }); + const TableData = TeamsData?.getAllTeams.map((items: any) => ({ teams: items.name, users: items.members.length, @@ -27,6 +32,11 @@ function DashboardTableDesign() { ), })); + const handleViewClick = (team: any) => { + setSelectedTeam(team); + setIsModalOpen(true); + }; + const organizationColumns = [ { Header: t('Teams'), accessor: 'teams' }, { Header: t('Logins'), accessor: 'logins' }, @@ -34,17 +44,16 @@ function DashboardTableDesign() { { Header: t('action'), accessor: '', - Cell: () => ( - <> - - + Cell: ({ row }: any) => ( + ), }, ]; @@ -54,8 +63,6 @@ function DashboardTableDesign() { columns={organizationColumns} data={TableData ? (TableData as any[]) : []} title={t('Teams metrices')} -<<<<<<< HEAD -======= loading={loading} /> setIsModalOpen(false)} selectedteam={selectedTeam} Teams={TeamsData?.getAllTeams} ->>>>>>> fdc13ed (user Growth overtime chart) />
); diff --git a/src/components/AdminTeamDetails.tsx b/src/components/AdminTeamDetails.tsx index 9a19ff07..4b95d5b3 100644 --- a/src/components/AdminTeamDetails.tsx +++ b/src/components/AdminTeamDetails.tsx @@ -1,12 +1,8 @@ import React, { useState } from 'react'; -<<<<<<< HEAD -import { FaAngleDown } from "react-icons/fa6"; -======= import { FaAngleDown } from 'react-icons/fa6'; import TeamChart from '../Chart/TeamChart'; import ProgressBar from '../Chart/ProgressBar'; import UsersChart from '../Chart/usersChart'; ->>>>>>> fdc13ed (user Growth overtime chart) interface TeamData { ttlName?: string; @@ -27,20 +23,6 @@ interface TeamDetailsModalProps { Teams?: any; } -<<<<<<< HEAD -const TeamDetailsModal: React.FC = ({ isOpen, onClose, teamData }) => { - if (!isOpen) return null; - - const [showAttendanceSummary, setShowAttendanceSummary] = useState(false); - - const handleAttendanceSummaryEnter = () => { - setShowAttendanceSummary(true); - }; - - const handleAttendanceSummaryLeave = () => { - setShowAttendanceSummary(false); - }; -======= // Add this near the top of your TeamDetailsModal component const loginStats = { daily: { @@ -145,92 +127,51 @@ function TeamDetailsModal({ totalLogins: total, }; } ->>>>>>> fdc13ed (user Growth overtime chart) return (
-
+
-
-

+
+

-

+ +

+
-
-
-
- -

{teamData?.ttlName || 'Sostene'}

-
- -
- -

{teamData?.team}

-
- -
- -

{teamData?.organization || 'Organization Name'}

-
- -
- -

{teamData?.program || 'Program Name'}

-
- -
- -

{teamData?.phase || 'Current Phase'}

-
- -
- -

{teamData?.cohort || 'Current Cohort'}

-
-
- -
-
- +
+ {activeTab === 'overview' && ( +
-<<<<<<< HEAD -
-

Active Members

-

- {teamData?.activeUsers || '0'} -

-
-
-

Dropped Members

-

- {teamData?.droppedUsers || '0'} -

-======= {[ ['TTL Name', CurrentTeam[0]?.ttl?.profile?.name || 'Sostene'], ['Team Name', selectedteam?.teams || 'Team Name'], @@ -318,40 +259,48 @@ function TeamDetailsModal({ {average || '0'} / 5.0

->>>>>>> fdc13ed (user Growth overtime chart)
- -
- - {showAttendanceSummary && ( -
-

Quality: 1.5

-

Quantity: 2.3

-

Professionalism: 3.1

-
-<<<<<<< HEAD - )} -
- -
- -
-

- {teamData?.rating || '4.5'} / 5.0 -

+ )} + + {activeTab === 'logins' && ( +
+
+ + +
-=======

@@ -382,13 +331,12 @@ function TeamDetailsModal({ CurrentTeam={CurrentTeam} loginsbyDate={loginsbyDate} /> ->>>>>>> fdc13ed (user Growth overtime chart)

-
+ )}
); -}; +} -export default TeamDetailsModal; \ No newline at end of file +export default TeamDetailsModal; diff --git a/src/components/DashboardCards.tsx b/src/components/DashboardCards.tsx index 3c4a51d9..622c65fe 100644 --- a/src/components/DashboardCards.tsx +++ b/src/components/DashboardCards.tsx @@ -165,7 +165,7 @@ function DashboardCards() { labels: [ 'Accepted Invitations', 'Pending Invitations', - 'Declined Invitations', + 'Cancelled Invitations', ], datasets: [ { @@ -187,7 +187,9 @@ function DashboardCards() {
{/* Tickets Overview */}
-

Tickets Overview

+

+ Tickets Overview +

{getTicketsDataLoading ? ( -

Invitations Overview

+

+ Invitations Overview +

{getInvitationsDataLoading ? ( {/* Teams Card */} -
+
{!getAllTeamsDataLoading && ( <> -
- {' '} - {/* Increased icon size */} -

+

+ +

TEAMS

-
- +
+ {totalTeams}
@@ -241,54 +244,10 @@ function DashboardCards() {
{/* Upcoming Events */} -
-

Upcoming Events

-
- {upcomingEvents.length ? ( - upcomingEvents.map((event) => ( - -
- -
-
- {event.title} - -

- By {event.hostName} -

-
-
-

- {event.timeToStart} - {event.timeToEnd && ` - ${event.timeToEnd}`} -

-

- - {format(new Date(event.start), 'dd, MMM yyyy')} - -  -  - - {format(new Date(event.end), 'dd, MMM yyyy')} - -

-
-
-
- - )) - ) : ( -
- -

- Oops! No upcoming events scheduled -

-
- )} -
- {getEventsDataLoading && ( +
+

Upcoming Events

+ + {getEventsDataLoading ? (
+ ) : ( +
+ {upcomingEvents.length ? ( + upcomingEvents.map((event) => ( + +
+ +
+
+ {event.title} - +

+ By {event.hostName} +

+
+
+

+ {event.timeToStart} + {event.timeToEnd && ` - ${event.timeToEnd}`} +

+

+ + {format(new Date(event.start), 'dd, MMM yyyy')} + +  -  + + {format(new Date(event.end), 'dd, MMM yyyy')} + +

+
+
+
+ + )) + ) : ( +
+ +

+ Oops! No upcoming events scheduled +

+
+ )} +
)}
diff --git a/src/components/TraineeHeader.tsx b/src/components/TraineeHeader.tsx index 78d22cb2..8b6ef6d8 100644 --- a/src/components/TraineeHeader.tsx +++ b/src/components/TraineeHeader.tsx @@ -7,6 +7,7 @@ import { MenuIcon, SunIcon, XIcon } from '@heroicons/react/outline'; import { MoonIcon, BellIcon } from '@heroicons/react/solid'; import { useLazyQuery, useSubscription, gql } from '@apollo/client'; import { toast } from 'react-toastify'; +import { h } from '@fullcalendar/core/preact'; import Logo from '../assets/logo.svg'; import LogoWhite from '../assets/logoWhite.svg'; import Avatar from '../assets/avatar.png'; @@ -18,7 +19,6 @@ import { GET_PROFILE } from '../queries/user.queries'; import { UserContext } from '../hook/useAuth'; import { NotificationSubscription } from '../Mutations/notificationMutation'; import { getAllNotification } from '../queries/notification.queries'; -import { h } from '@fullcalendar/core/preact'; import { handleError } from './ErrorHandle'; export const TICKETS_NOTS_SUB = gql` diff --git a/src/pages/AdminDashboard.tsx b/src/pages/AdminDashboard.tsx index 329d7e2f..9cf7b423 100644 --- a/src/pages/AdminDashboard.tsx +++ b/src/pages/AdminDashboard.tsx @@ -3,17 +3,14 @@ import { t } from 'i18next'; import { useTranslation } from 'react-i18next'; import { useMutation } from '@apollo/client'; import { toast } from 'react-toastify'; -import PieChart from '../Chart/PieChart'; +import DashboardCards from '../components/DashboardCards'; import BarChart from '../Chart/BarChart'; -import UsersChart from '../Chart/usersChart'; // eslint-disable-next-line import/no-useless-path-segments import useDocumentTitle from '../hook/useDocumentTitle'; -import Comingsoon from './Comingsoon'; import Button from '../components/Buttons'; import { UserContext } from '../hook/useAuth'; import { INVITE_USER_MUTATION } from '../Mutations/manageStudentMutations'; import { handleError } from '../components/ErrorHandle'; -import { FaEye } from 'react-icons/fa'; import DashboardTableDesign from '../components/AdminDashboardTable'; import { UserChart } from '../Chart/LineChart'; @@ -127,23 +124,16 @@ function AdminDashboard() {
+
+ +
-
-
- -
-
-
- Users -
- -
- -
-
- Teams -
- +
+ +
+
+
+ Teams
diff --git a/tests/other-tests/AdminDashboard.test.tsx b/tests/other-tests/AdminDashboard.test.tsx index 98110d90..ec0f3ee9 100644 --- a/tests/other-tests/AdminDashboard.test.tsx +++ b/tests/other-tests/AdminDashboard.test.tsx @@ -9,14 +9,26 @@ import { waitFor, waitForElementToBeRemoved, } from '@testing-library/react'; -import '@testing-library/jest-dom' +import '@testing-library/jest-dom'; import { act } from 'react-dom/test-utils'; import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client'; import AdminDashboard from '../../src/pages/AdminDashboard'; +beforeAll(() => { + global.ResizeObserver = class { + observe() {} + unobserve() {} + disconnect() {} + }; +}); + const client = new ApolloClient({ cache: new InMemoryCache() }); describe('', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + test('should render account component', async () => { act(() => { render( @@ -36,7 +48,8 @@ describe('', () => { expect(inviteBtn).toBeInTheDocument(); expect(removeInviteModel).toBeInTheDocument(); - act(() => { + // Simulate user interactions + await act(async () => { fireEvent.change(inviteInput, { target: { value: 'admin@devpulse.co' }, }); diff --git a/tests/other-tests/DashboardCard.test.tsx b/tests/other-tests/DashboardCard.test.tsx new file mode 100644 index 00000000..a4c7d69e --- /dev/null +++ b/tests/other-tests/DashboardCard.test.tsx @@ -0,0 +1,91 @@ +import { render, screen, waitFor } from '@testing-library/react'; +import { MemoryRouter } from 'react-router-dom'; +import DashboardCards from '../../src/components/DashboardCards'; +import ThemeProvider, { ThemeContext } from '../../src/hook/ThemeProvider'; +import { GET_TEAMS_CARDS } from '../../src/components/CoordinatorCard'; +import GET_TICKETS from '../../src/queries/tickets.queries'; +import { GET_EVENTS } from '../../src/queries/event.queries'; +import { GET_ALL_INVITATIONS } from '../../src/queries/invitation.queries'; +import React from 'react'; +import { MockedProvider } from '@apollo/client/testing'; + +global.ResizeObserver = class { + observe() {} + unobserve() {} + disconnect() {} +}; + +const mocks = [ + { + request: { + query: GET_TICKETS, + }, + result: { + data: { + getAllTickets: [ + { id: '1', status: 'active' }, + { id: '2', status: 'closed' }, + ], + }, + }, + }, + { + request: { + query: GET_TEAMS_CARDS, + variables: { + orgToken: localStorage.getItem('orgToken'), + }, + }, + result: { + data: { + getAllTeams: [ + { id: 'team1', name: 'Team One' }, + { id: 'team2', name: 'Team Two' }, + ], + }, + }, + }, +]; + +const mocksEmptyState = [ + { + request: { + query: GET_TICKETS, + }, + result: { + data: { + getAllTickets: [], + }, + }, + }, + { + request: { + query: GET_TEAMS_CARDS, + variables: { + orgToken: localStorage.getItem('orgToken'), + }, + }, + result: { + data: { + getAllTeams: [], + }, + }, + }, +]; + +test('renders DashboardCards and shows loading skeletons', async () => { + render( + + + + ); + + expect(screen.getByText(/tickets overview/i)).toBeInTheDocument(); + expect(screen.getByText(/invitations overview/i)).toBeInTheDocument(); + expect(screen.getByText(/upcoming events/i)).toBeInTheDocument(); + + await waitFor(() => { + expect(screen.getByText(/teams/i)).toBeInTheDocument(); + expect(screen.getByText(/2/i)).toBeInTheDocument(); + }); +}); \ No newline at end of file