diff --git a/apps/web/app/[locale]/dashboard/app-url/[teamId]/page.tsx b/apps/web/app/[locale]/dashboard/app-url/[teamId]/page.tsx index 3f46cfc54..926e57603 100644 --- a/apps/web/app/[locale]/dashboard/app-url/[teamId]/page.tsx +++ b/apps/web/app/[locale]/dashboard/app-url/[teamId]/page.tsx @@ -7,16 +7,17 @@ import { useOrganizationTeams } from '@app/hooks/features/useOrganizationTeams'; import { useAtomValue } from 'jotai'; import { useTranslations } from 'next-intl'; import { useParams } from 'next/navigation'; -import React, { useMemo, useState } from 'react'; +import React, { useMemo } from 'react'; import { ArrowLeftIcon } from '@radix-ui/react-icons'; import { useRouter } from 'next/navigation'; -import { Breadcrumb, Card, Container } from '@/lib/components'; +import { Breadcrumb, Container } from '@/lib/components'; import { DashboardHeader } from '../../team-dashboard/[teamId]/components/dashboard-header'; import { useReportActivity } from '@/app/hooks/features/useReportActivity'; import { ProductivityStats } from '../components/ProductivityStats'; import { ProductivityChart } from '../components/ProductivityChart'; import { ProductivityHeader } from '../components/ProductivityHeader'; import { ProductivityTable } from '../components/ProductivityTable'; +import { Card } from '@components/ui/card'; interface ProductivityData { date: string; @@ -26,34 +27,36 @@ interface ProductivityData { } function AppUrls() { - // const { rapportDailyActivity } = useReportActivity(); - const { isTrackingEnabled } = useOrganizationTeams(); - const { updateDateRange, updateFilters, isManage } = useReportActivity(); - - const [groupBy, setGroupBy] = useState('date'); - - const router = useRouter(); const t = useTranslations(); + const router = useRouter(); const fullWidth = useAtomValue(fullWidthState); const paramsUrl = useParams<{ locale: string }>(); const currentLocale = paramsUrl?.locale; + const { isTrackingEnabled } = useOrganizationTeams(); + const { + activityReport, + loadingActivityReport, + handleGroupByChange, + updateDateRange, + updateFilters, + isManage + } = useReportActivity({ types: 'APPS-URLS' }); + const generateMonthData = (date: Date): ProductivityData[] => { + const year = date.getFullYear(); + const month = date.getMonth(); + const daysInMonth = new Date(year, month + 1, 0).getDate(); - const generateMonthData = (date: Date): ProductivityData[] => { - const year = date.getFullYear(); - const month = date.getMonth(); - const daysInMonth = new Date(year, month + 1, 0).getDate(); - - return Array.from({ length: daysInMonth }, (_, i) => ({ - date: new Date(year, month, i + 1).toISOString().split('T')[0], - productive: Math.floor(Math.random() * 50) + 25, - neutral: Math.floor(Math.random() * 40) + 20, - unproductive: Math.floor(Math.random() * 35) + 15, - })); - }; - const monthData = generateMonthData(new Date()); + return Array.from({ length: daysInMonth }, (_, i) => ({ + date: new Date(year, month, i + 1).toISOString().split('T')[0], + productive: Math.floor(Math.random() * 50) + 25, + neutral: Math.floor(Math.random() * 40) + 20, + unproductive: Math.floor(Math.random() * 35) + 15 + })); + }; + const monthData = generateMonthData(new Date()); const monthTotals = monthData.reduce( (acc, day) => ({ productive: acc.productive + day.productive, @@ -76,6 +79,8 @@ function AppUrls() { [currentLocale, t] ); + const handleBack = () => router.back(); + return (
- }> + } + > { + extends Omit { logType?: TimeLogType[]; activityLevel: { start: number; @@ -25,6 +30,7 @@ export interface UseReportActivityProps projectIds?: string[]; employeeIds?: string[]; teamIds?: string[]; + groupBy?: string; } const defaultProps: Required< @@ -57,26 +63,36 @@ const defaultProps: Required< teamIds: [] }; -export function useReportActivity() { +export type GroupByType = 'date' | 'project' | 'employee'; + +interface GroupByOptions { + groupBy: GroupByType; +} + +export function useReportActivity({ types }: { types?: 'TEAM-DASHBOARD' | 'APPS-URLS' }) { + // User and authentication const { user } = useAuthenticateUser(); + const { allteamsState, alluserState, isUserAllowedToAccess } = useTimelogFilterOptions(); + const isManage = user && isUserAllowedToAccess(user); + + // State management + const [currentFilters, setCurrentFilters] = useState>(defaultProps); const [rapportChartActivity, setRapportChartActivity] = useAtom(timeLogsRapportChartState); const [rapportDailyActivity, setRapportDailyActivity] = useAtom(timeLogsRapportDailyState); const [statisticsCounts, setStatisticsCounts] = useAtom(timesheetStatisticsCountsState); const [activityReport, setActivityReport] = useAtom(activityReportState); - const { allteamsState, alluserState, isUserAllowedToAccess } = useTimelogFilterOptions(); - + // API queries const { loading: loadingTimeLogReportDailyChart, queryCall: queryTimeLogReportDailyChart } = useQuery(getTimeLogReportDailyChart); - const { loading: loadingTimeLogReportDaily, queryCall: queryTimeLogReportDaily } = useQuery(getTimeLogReportDaily); + const { loading: loadingTimeLogReportDaily, queryCall: queryTimeLogReportDaily } = + useQuery(getTimeLogReportDaily); const { loading: loadingTimesheetStatisticsCounts, queryCall: queryTimesheetStatisticsCounts } = useQuery(getTimesheetStatisticsCounts); - const { loading: loadingActivityReport, queryCall: queryActivityReport } = useQuery(getActivityReport); - - const [currentFilters, setCurrentFilters] = useState>(defaultProps); - const isManage = user && isUserAllowedToAccess(user); + const { loading: loadingActivityReport, queryCall: queryActivityReport } = + useQuery(getActivityReport); - // Memoize the merged props to avoid recalculation + // Props merging logic const getMergedProps = useMemo(() => { if (!user?.employee.organizationId) { return null; @@ -94,20 +110,17 @@ export function useReportActivity() { logType: (customProps?.logType || currentFilters.logType || defaultProps.logType) as TimeLogType[], startDate: (customProps?.startDate || currentFilters.startDate || defaultProps.startDate) as string, endDate: (customProps?.endDate || currentFilters.endDate || defaultProps.endDate) as string, - projectIds: (customProps?.projectIds || - currentFilters.projectIds || - defaultProps.projectIds) as string[], + groupBy: (customProps?.groupBy || currentFilters.groupBy || defaultProps.groupBy) as string, + projectIds: (customProps?.projectIds || currentFilters.projectIds || defaultProps.projectIds) as string[], employeeIds: isManage ? alluserState?.map(({ employee: { id } }) => id).filter(Boolean) : [user.employee.id], teamIds: allteamsState?.map(({ id }) => id).filter(Boolean), activityLevel: { - start: - customProps?.activityLevel?.start ?? + start: customProps?.activityLevel?.start ?? currentFilters.activityLevel?.start ?? defaultProps.activityLevel.start, - end: - customProps?.activityLevel?.end ?? + end: customProps?.activityLevel?.end ?? currentFilters.activityLevel?.end ?? defaultProps.activityLevel.end }, @@ -116,16 +129,23 @@ export function useReportActivity() { }; return merged as Required; }; - }, [user?.employee.organizationId, user?.employee.id, user?.tenantId, currentFilters, isManage, alluserState, allteamsState]); + }, [ + user?.employee.organizationId, + user?.employee.id, + user?.tenantId, + currentFilters, + isManage, + alluserState, + allteamsState + ]); - // Generic fetch function to reduce code duplication + // Generic fetch function const fetchReport = useCallback( async ( - queryFn: - | typeof queryTimeLogReportDailyChart - | typeof queryTimeLogReportDaily - | typeof queryTimesheetStatisticsCounts - | typeof queryActivityReport, + queryFn: typeof queryTimeLogReportDailyChart | + typeof queryTimeLogReportDaily | + typeof queryTimesheetStatisticsCounts | + typeof queryActivityReport, setData: ((data: T[]) => void) | null, customProps?: Partial ) => { @@ -138,10 +158,15 @@ export function useReportActivity() { try { const mergedProps = getMergedProps(customProps); + const response = await queryFn(mergedProps); - if (setData && Array.isArray(response.data)) { - setData(response.data as T[]); + if (setData) { + if (response?.data && Array.isArray(response.data)) { + setData(response.data as T[]); + } else { + setData([]); + } } if (customProps) { @@ -160,6 +185,7 @@ export function useReportActivity() { [user, getMergedProps] ); + // Specific fetch functions const fetchReportActivity = useCallback( (customProps?: Partial) => fetchReport(queryTimeLogReportDailyChart, setRapportChartActivity, customProps), @@ -204,6 +230,7 @@ export function useReportActivity() { [user, getMergedProps, queryTimesheetStatisticsCounts, setStatisticsCounts] ); + // Update handlers const updateDateRange = useCallback( (startDate: Date, endDate: Date) => { const newProps = { @@ -215,10 +242,22 @@ export function useReportActivity() { fetchReportActivity(newProps), fetchDailyReport(newProps), fetchStatisticsCounts(newProps), - fetchActivityReport(newProps) + (types === 'APPS-URLS' && fetchActivityReport(newProps)) || null ]).catch(console.error); }, - [fetchReportActivity, fetchDailyReport, fetchStatisticsCounts, fetchActivityReport] + [fetchReportActivity, fetchDailyReport, fetchStatisticsCounts, fetchActivityReport, types] + ); + + const handleGroupByChange = useCallback( + async (groupByType: GroupByType): Promise => { + try { + const options: GroupByOptions = { groupBy: groupByType }; + await fetchActivityReport(options); + } catch (error) { + console.error('Failed to update activity grouping:', error); + } + }, + [fetchActivityReport] ); const updateFilters = useCallback( @@ -227,34 +266,43 @@ export function useReportActivity() { fetchReportActivity(newFilters), fetchDailyReport(newFilters), fetchStatisticsCounts(newFilters), - fetchActivityReport(newFilters) + (types === 'APPS-URLS' && fetchActivityReport(newFilters)) || null ]).catch(console.error); }, - [fetchReportActivity, fetchDailyReport, fetchStatisticsCounts, fetchActivityReport] + [fetchReportActivity, fetchDailyReport, fetchStatisticsCounts, fetchActivityReport, types] ); + // Initial data fetch useEffect(() => { if (user) { Promise.all([ fetchReportActivity(), fetchDailyReport(), fetchStatisticsCounts(), - fetchActivityReport() + (types === 'APPS-URLS' && fetchActivityReport()) || null ]).catch(console.error); } - }, [user, fetchReportActivity, fetchDailyReport, fetchStatisticsCounts, fetchActivityReport]); + }, [user, fetchReportActivity, fetchDailyReport, fetchStatisticsCounts, fetchActivityReport, types]); return { + // Loading states loadingTimeLogReportDailyChart, loadingTimeLogReportDaily, loadingTimesheetStatisticsCounts, loadingActivityReport, + + // Data states rapportChartActivity, rapportDailyActivity, statisticsCounts, activityReport, + + // Update handlers updateDateRange, updateFilters, + handleGroupByChange, + + // Other states currentFilters, setStatisticsCounts, isManage diff --git a/apps/web/lib/features/unverified-email.tsx b/apps/web/lib/features/unverified-email.tsx index 799a87062..196c9f482 100644 --- a/apps/web/lib/features/unverified-email.tsx +++ b/apps/web/lib/features/unverified-email.tsx @@ -45,7 +45,7 @@ export function UnverifiedEmail() { 'border dark:border-[#28292F] dark:shadow-lg dark:bg-[#1B1D22]' )} > - + {t('pages.home.SENT_EMAIL_VERIFICATION_YOU_NEED_TO')} {t('common.VERIFY')} @@ -100,12 +100,12 @@ export function ConfirmUserModal({ open, user, closeModal }: { open: boolean; us
-
+
{t('common.SECURITY_CODE')} -
+
-
+
{"Didn't recieve code ?"}