From baff5fae92c537ec17be197ed0b0c77a7c432c68 Mon Sep 17 00:00:00 2001 From: shebz2023 Date: Wed, 20 Nov 2024 10:50:36 +0200 Subject: [PATCH] user Growth overtime chart --- src/Chart/TeamChart.tsx | 218 ++++++++++++++++++++++ src/components/AdminDashboardTable.tsx | 10 + src/components/AdminTeamDetails.tsx | 242 ++++++++++++++++++++++++- 3 files changed, 468 insertions(+), 2 deletions(-) create mode 100644 src/Chart/TeamChart.tsx diff --git a/src/Chart/TeamChart.tsx b/src/Chart/TeamChart.tsx new file mode 100644 index 00000000..56c400f9 --- /dev/null +++ b/src/Chart/TeamChart.tsx @@ -0,0 +1,218 @@ +/* eslint-disable no-console */ +/* eslint-disable no-restricted-syntax */ +/* eslint-disable no-plusplus */ +import React from 'react'; +import { Line } from 'react-chartjs-2'; +import { + Chart as ChartJS, + CategoryScale, + LinearScale, + PointElement, + LineElement, + Title, + Tooltip, + Legend, +} from 'chart.js'; + +ChartJS.register( + CategoryScale, + LinearScale, + PointElement, + LineElement, + Title, + Tooltip, + Legend, +); + +interface TeamChartProps { + timeframe?: 'daily' | 'weekly' | 'monthly'; + CurrentTeam: any[]; + loginsbyDate: any[]; +} + +function TeamChart({ + timeframe = 'daily', + CurrentTeam, + loginsbyDate, +}: TeamChartProps) { + function organizeLoginData(loginData: any) { + const currentDate = new Date(); + const currentYear = currentDate.getFullYear(); + function getWeekNumber(date: any) { + const tempDate: any = new Date(date); + tempDate.setUTCDate( + tempDate.getUTCDate() + 4 - (tempDate.getUTCDay() || 7), + ); + const yearStart: any = new Date( + Date.UTC(tempDate.getUTCFullYear(), 0, 1), + ); + return Math.ceil(((tempDate - yearStart) / 86400000 + 1) / 7); + } + // Initialize result arrays + const weeklyData = Array(54) + .fill(0) + .map((_, i) => ({ week: i + 1, success: 0, failed: 0 })); + const monthlyData = Array(12) + .fill(0) + .map((_, i) => ({ month: i + 1, success: 0, failed: 0 })); + const dailyData = Array(7) + .fill(0) + .map((_, i) => ({ day: i, success: 0, failed: 0 })); + for (const [dateString, { success, failed }] of Object.entries( + loginData, + ) as any) { + const date = new Date(dateString); + const isoWeekNumber = getWeekNumber(date); + const month = date.getUTCMonth(); + const dayOfWeek = (date.getUTCDay() + 6) % 7; + const weekStart = new Date(currentDate); + weekStart.setUTCDate( + currentDate.getUTCDate() - currentDate.getUTCDay() + 1, + ); + const weekEnd = new Date(weekStart); + weekEnd.setUTCDate(weekStart.getUTCDate() + 6); + if (date >= weekStart && date <= weekEnd) { + dailyData[dayOfWeek].success += success; + dailyData[dayOfWeek].failed += failed; + } + // Weekly data + if (isoWeekNumber <= 54) { + weeklyData[isoWeekNumber - 1].success += success; + weeklyData[isoWeekNumber - 1].failed += failed; + } + // Monthly data + monthlyData[month].success += success; + monthlyData[month].failed += failed; + } + const weekDays = [ + 'Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday', + 'Sunday', + ]; + const currentWeekData = dailyData.map((data, index) => ({ + day: weekDays[index], + success: data.success, + failed: data.failed, + })); + return { + currentWeek: currentWeekData, + weekly: weeklyData, + monthly: monthlyData.map((data, index) => ({ + month: new Date(0, index).toLocaleString('en', { month: 'long' }), + success: data.success, + failed: data.failed, + })), + }; + } + + const organizedData = organizeLoginData(loginsbyDate); + + const weeklyDataset = organizedData.weekly + .filter((_, index) => index % 3 === 0) + .map((item) => item.success); + + const chartData = { + daily: { + labels: ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'], + datasets: [ + { + label: CurrentTeam[0].name, + data: organizedData.currentWeek.map((item: any) => item.success), + fill: false, + borderColor: '#4F46E5', + tension: 0.4, + }, + ], + }, + weekly: { + labels: [ + '03', + '06', + '09', + '12', + '15', + '18', + '21', + '24', + '27', + '30', + '31', + '34', + '37', + '40', + '43', + '46', + '49', + '54', + ], + datasets: [ + { + label: CurrentTeam[0].name, + data: weeklyDataset, + fill: false, + borderColor: '#4F46E5', + tension: 0.4, + }, + ], + }, + monthly: { + labels: Array.from({ length: 12 }, (_, i) => + String(i + 1).padStart(2, '0'), + ), + datasets: [ + { + label: CurrentTeam[0].name, + data: organizedData.monthly.map((item: any) => item.success), + fill: false, + borderColor: '#4F46E5', + tension: 0.4, + }, + ], + }, + }; + + const options = { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + position: 'bottom' as const, + }, + tooltip: { + mode: 'index' as const, + intersect: false, + }, + }, + scales: { + y: { + beginAtZero: true, + grid: { + color: '#D1D5DB', + }, + ticks: { + color: '#6B7280', + }, + }, + x: { + grid: { + display: false, + }, + ticks: { + color: '#6B7280', + }, + }, + }, + }; + + return ( +
+ +
+ ); +} + +export default TeamChart; diff --git a/src/components/AdminDashboardTable.tsx b/src/components/AdminDashboardTable.tsx index 33644a6d..7bc3808e 100644 --- a/src/components/AdminDashboardTable.tsx +++ b/src/components/AdminDashboardTable.tsx @@ -54,6 +54,16 @@ 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 dfe5588c..9a19ff07 100644 --- a/src/components/AdminTeamDetails.tsx +++ b/src/components/AdminTeamDetails.tsx @@ -1,9 +1,16 @@ 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; - team?: string; + teams?: string; organization?: string; program?: string; phase?: string; @@ -16,9 +23,11 @@ interface TeamData { interface TeamDetailsModalProps { isOpen: boolean; onClose: () => void; - teamData: TeamData | null; + selectedteam: TeamData | null; + Teams?: any; } +<<<<<<< HEAD const TeamDetailsModal: React.FC = ({ isOpen, onClose, teamData }) => { if (!isOpen) return null; @@ -31,6 +40,112 @@ const TeamDetailsModal: React.FC = ({ isOpen, onClose, te const handleAttendanceSummaryLeave = () => { setShowAttendanceSummary(false); }; +======= +// Add this near the top of your TeamDetailsModal component +const loginStats = { + daily: { + passed: 60, + failed: 40, + total: '200k', + }, + weekly: { + passed: 75, + failed: 25, + total: '1.2M', + }, + monthly: { + passed: 85, + failed: 15, + total: '5M', + }, +}; + +function TeamDetailsModal({ + isOpen, + onClose, + selectedteam, + Teams, +}: TeamDetailsModalProps) { + const [activeTab, setActiveTab] = useState<'overview' | 'logins'>('overview'); + const [timeframe, setTimeframe] = useState<'daily' | 'weekly' | 'monthly'>( + 'daily', + ); + const [showAttendanceSummary, setShowAttendanceSummary] = useState(false); + + const handleAttendanceSummaryEnter = () => setShowAttendanceSummary(true); + const handleAttendanceSummaryLeave = () => setShowAttendanceSummary(false); + + if (!isOpen) return null; + + const CurrentTeam = Teams?.filter( + (items: any) => items?.name === selectedteam?.teams, + ); + + const average = + (parseInt(CurrentTeam[0]?.avgRatings?.quality, 2) + + parseInt(CurrentTeam[0]?.avgRatings?.quantity, 2) + + parseInt(CurrentTeam[0]?.avgRatings?.professional_Skills, 2)) / + 3; + + const activeMembers = CurrentTeam[0]?.members.filter( + (item: any) => item.status.status !== 'suspended', + ); + const droppedMembers = CurrentTeam[0]?.members.filter( + (item: any) => item.status.status === 'suspended', + ); + function mapLoginsByDate(team: any) { + if (!team || !Array.isArray(team[0].members)) { + throw new Error('Invalid team object'); + } + const loginCounts: any = {}; + team[0].members.forEach((member: any) => { + const activities = member.profile?.activity; + + if (Array.isArray(activities)) { + activities.forEach((activity) => { + const rawDate = activity.date; + const timestamp = parseInt(rawDate, 10); + if (!Number.isNaN(timestamp)) { + const loginDate = new Date(timestamp).toISOString().split('T')[0]; + if (!loginCounts[loginDate]) { + loginCounts[loginDate] = { success: 0, failed: 0 }; + } + if (activity.failed === 1) { + loginCounts[loginDate].failed += 1; + } else { + loginCounts[loginDate].success += 1; + } + } + }); + } + }); + return loginCounts; + } + const loginsbyDate = mapLoginsByDate(CurrentTeam); + const orgName = localStorage.getItem('orgName'); + + function calculateLoginPercentages(data: any) { + let totalSuccess = 0; + let totalFailed = 0; + + // Sum up all successes and failures + Object.values(data).forEach(({ success, failed }: any) => { + totalSuccess += success; + totalFailed += failed; + }); + + // Calculate percentages + const total = totalSuccess + totalFailed; + const successPercentage = total > 0 ? (totalSuccess / total) * 100 : 0; + const failedPercentage = total > 0 ? (totalFailed / total) * 100 : 0; + + return { + successPercentage: successPercentage.toFixed(2), + failedPercentage: failedPercentage.toFixed(2), + totalLogins: total, + }; + } +>>>>>>> fdc13ed (user Growth overtime chart) return (
@@ -103,6 +218,7 @@ const TeamDetailsModal: React.FC = ({ isOpen, onClose, te Users
+<<<<<<< HEAD

Active Members

@@ -114,6 +230,95 @@ const TeamDetailsModal: React.FC = ({ isOpen, onClose, te

{teamData?.droppedUsers || '0'}

+======= + {[ + ['TTL Name', CurrentTeam[0]?.ttl?.profile?.name || 'Sostene'], + ['Team Name', selectedteam?.teams || 'Team Name'], + ['Organization', selectedteam?.organization || orgName], + [ + 'Program', + CurrentTeam[0]?.cohort?.program?.name || 'Program Name', + ], + [ + 'Phase', + CurrentTeam[0]?.cohort?.phase?.name || 'Current Phase', + ], + ['Cohort', CurrentTeam[0]?.cohort.name || 'Current Cohort'], + ].map(([label, value], idx) => ( + // eslint-disable-next-line react/no-array-index-key +
+ +

{value}

+
+ ))} +
+ +
+
+ +
+
+

+ Active Members +

+

+ {activeMembers?.length || '0'} +

+
+
+

+ Dropped Members +

+

+ {droppedMembers?.length || '0'} +

+
+
+
+ +
+ + {showAttendanceSummary && ( +
+

+ Quality: {CurrentTeam[0]?.avgRatings?.quality || 0} +

+

+ Quantity: {CurrentTeam[0]?.avgRatings?.quality || 0} +

+

+ Professionalism:{' '} + {CurrentTeam[0]?.avgRatings?.professional_Skills || 0} +

+
+ )} +
+ +
+ +
+

+ {average || '0'} / 5.0 +

+
+>>>>>>> fdc13ed (user Growth overtime chart)
@@ -133,6 +338,7 @@ const TeamDetailsModal: React.FC = ({ isOpen, onClose, te

Quantity: 2.3

Professionalism: 3.1

+<<<<<<< HEAD )} @@ -145,6 +351,38 @@ const TeamDetailsModal: React.FC = ({ isOpen, onClose, te {teamData?.rating || '4.5'} / 5.0

+======= +
+
+

+ Logins Attempt Status +

+ +
+

+ Total Logins:{' '} + + {' '} + {calculateLoginPercentages(loginsbyDate).totalLogins} + +

+
+ + +>>>>>>> fdc13ed (user Growth overtime chart)