Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useEffect, useMemo, useState, type ReactNode } from 'react'
import { useEmployeesListSuspense } from '@gusto/embedded-api/react-query/employeesList'
import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from 'react'
import { useEmployeesList } from '@gusto/embedded-api/react-query/employeesList'
import { keepPreviousData } from '@tanstack/react-query'
import { usePayrollsGetSuspense } from '@gusto/embedded-api/react-query/payrollsGet'
import { usePayrollsCalculateMutation } from '@gusto/embedded-api/react-query/payrollsCalculate'
import type { Employee } from '@gusto/embedded-api/models/components/employee'
Expand All @@ -9,6 +10,7 @@ import { useTranslation } from 'react-i18next'
import { usePayrollsUpdateMutation } from '@gusto/embedded-api/react-query/payrollsUpdate'
import type { PayrollEmployeeCompensationsType } from '@gusto/embedded-api/models/components/payrollemployeecompensationstype'
import type { PayrollUpdateEmployeeCompensations } from '@gusto/embedded-api/models/components/payrollupdate'
import type { PayrollPrepared } from '@gusto/embedded-api/models/components/payrollprepared'
import { usePreparedPayrollData } from '../usePreparedPayrollData'
import { payrollSubmitHandler, type ApiPayrollBlocker } from '../PayrollBlocker/payrollHelpers'
import { PayrollConfigurationPresentation } from './PayrollConfigurationPresentation'
Expand All @@ -18,6 +20,7 @@ import { componentEvents } from '@/shared/constants'
import { useComponentDictionary, useI18n } from '@/i18n'
import { useBase } from '@/components/Base'
import type { PaginationItemsPerPage } from '@/components/Common/PaginationControl/PaginationControlTypes'
import { Loading } from '@/components/Common'
import { useDateFormatter } from '@/hooks/useDateFormatter'

const isCalculating = (processingRequest?: PayrollProcessingRequest | null) =>
Expand Down Expand Up @@ -60,21 +63,48 @@ export const Root = ({
const [isPolling, setIsPolling] = useState(false)
const [payrollBlockers, setPayrollBlockers] = useState<ApiPayrollBlocker[]>([])

const { data: employeeData, isFetching: isFetchingEmployeeData } = useEmployeesListSuspense({
companyId,
payrollUuid: payrollId, // get back list of employees to specific payroll
per: itemsPerPage,
page: currentPage,
sortBy: 'name', // sort alphanumeric by employee last_names
})
const { data: employeeData, isFetching: isFetchingEmployeeData } = useEmployeesList(
{
companyId,
payrollUuid: payrollId,
per: itemsPerPage,
page: currentPage,
sortBy: 'name',
},
{ placeholderData: keepPreviousData },
)

const employeeDataRef = useRef(employeeData)
employeeDataRef.current = employeeData

// get list of employee uuids to filter into prepare endpoint to get back employee_compensation data
const employeeUuids = useMemo(() => {
return employeeData.showEmployees?.map(e => e.uuid) || []
}, [employeeData.showEmployees])
return employeeData?.showEmployees?.map(e => e.uuid) || []
}, [employeeData?.showEmployees])

const totalPages = Number(employeeData?.httpMeta.response.headers.get('x-total-pages') ?? 1)
const totalCount = Number(employeeData?.httpMeta.response.headers.get('x-total-count') ?? 0)

const [displayedEmployees, setDisplayedEmployees] = useState<Employee[]>([])
const [isDataInSync, setIsDataInSync] = useState(false)

const handlePayrollDataReady = useCallback((preparedPayroll: PayrollPrepared) => {
const currentEmployeeData = employeeDataRef.current
if (!currentEmployeeData?.showEmployees || !preparedPayroll.employeeCompensations) {
setIsDataInSync(false)
return
}

const employeeUuids = currentEmployeeData.showEmployees.map(e => e.uuid)
const preparedUuids = new Set(preparedPayroll.employeeCompensations.map(c => c.employeeUuid))
const inSync = employeeUuids.length > 0 && employeeUuids.every(uuid => preparedUuids.has(uuid))

const totalPages = Number(employeeData.httpMeta.response.headers.get('x-total-pages') ?? 1)
const totalCount = Number(employeeData.httpMeta.response.headers.get('x-total-count') ?? 0)
if (inSync) {
setDisplayedEmployees(currentEmployeeData.showEmployees)
setIsDataInSync(true)
} else {
setIsDataInSync(false)
}
}, [])

const handleItemsPerPageChange = (newCount: PaginationItemsPerPage) => {
setItemsPerPage(newCount)
Expand All @@ -92,19 +122,6 @@ export const Root = ({
setCurrentPage(totalPages)
}

const pagination = {
currentPage,
handleFirstPage,
handlePreviousPage,
handleNextPage,
handleLastPage,
handleItemsPerPageChange,
totalPages,
totalCount,
isFetching: isFetchingEmployeeData,
itemsPerPage,
}

const { data: payrollData } = usePayrollsGetSuspense(
{
companyId,
Expand All @@ -122,14 +139,31 @@ export const Root = ({
preparedPayroll,
paySchedule,
isLoading: isPrepareLoading,
isPaginating,
handlePreparePayroll,
} = usePreparedPayrollData({
companyId,
payrollId,
employeeUuids,
sortBy: 'last_name', // sort alphanumeric by employee last_names to match employees GET
sortBy: 'last_name',
onDataReady: handlePayrollDataReady,
})

const isPaginationFetching = isFetchingEmployeeData || isPaginating || !isDataInSync

const pagination = {
currentPage,
handleFirstPage,
handlePreviousPage,
handleNextPage,
handleLastPage,
handleItemsPerPageChange,
totalPages,
totalCount,
isFetching: isPaginationFetching,
itemsPerPage,
}

const onCalculatePayroll = async () => {
// Clear any existing blockers before attempting calculation
setPayrollBlockers([])
Expand Down Expand Up @@ -241,14 +275,18 @@ export const Root = ({
}
: undefined

if (!employeeData) {
return <Loading />
}
Comment on lines +278 to +280
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we base this on isFetching instead? could we have no value for employee data here but not actually be loading?


return (
<PayrollConfigurationPresentation
onCalculatePayroll={onCalculatePayroll}
onEdit={onEdit}
onToggleExclude={onToggleExclude}
onViewBlockers={onViewBlockers}
employeeCompensations={preparedPayroll?.employeeCompensations || []}
employeeDetails={employeeData.showEmployees || []}
employeeDetails={displayedEmployees}
payPeriod={preparedPayroll?.payPeriod}
paySchedule={paySchedule}
isOffCycle={preparedPayroll?.offCycle}
Expand Down
29 changes: 26 additions & 3 deletions src/components/Payroll/usePreparedPayrollData.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useEffect, useCallback } from 'react'
import { useState, useEffect, useCallback, useRef, useMemo } from 'react'
import { usePayrollsPrepareMutation } from '@gusto/embedded-api/react-query/payrollsPrepare'
import { usePaySchedulesGet } from '@gusto/embedded-api/react-query/paySchedulesGet'
import type { PayrollPrepared } from '@gusto/embedded-api/models/components/payrollprepared'
Expand All @@ -11,26 +11,33 @@ interface UsePreparedPayrollDataParams {
payrollId: string
employeeUuids?: string[]
sortBy?: PayrollPrepareSortBy
onDataReady?: (preparedPayroll: PayrollPrepared) => void
}

interface UsePreparedPayrollDataReturn {
handlePreparePayroll: () => Promise<void>
preparedPayroll: PayrollPrepared | undefined
paySchedule: PayScheduleObject | undefined
isLoading: boolean
isPaginating: boolean
hasInitialData: boolean
}

export const usePreparedPayrollData = ({
companyId,
payrollId,
employeeUuids,
sortBy,
onDataReady,
}: UsePreparedPayrollDataParams): UsePreparedPayrollDataReturn => {
const { mutateAsync: preparePayroll, isPending: isPreparePayrollPending } =
usePayrollsPrepareMutation()
const [preparedPayroll, setPreparedPayroll] = useState<PayrollPrepared | undefined>()
const hasInitialDataRef = useRef(false)
const { baseSubmitHandler } = useBase()

const employeeUuidsKey = useMemo(() => employeeUuids?.join(',') ?? '', [employeeUuids])

const { data: payScheduleData, isLoading: isPayScheduleLoading } = usePaySchedulesGet(
{
companyId,
Expand All @@ -54,19 +61,35 @@ export const usePreparedPayrollData = ({
},
})
setPreparedPayroll(result.payrollPrepared)
if (result.payrollPrepared) {
hasInitialDataRef.current = true
onDataReady?.(result.payrollPrepared)
}
})
}, [companyId, payrollId, preparePayroll, employeeUuids, baseSubmitHandler])
}, [
companyId,
payrollId,
preparePayroll,
employeeUuidsKey,
baseSubmitHandler,
sortBy,
onDataReady,
])

useEffect(() => {
void handlePreparePayroll()
}, [handlePreparePayroll])

const isLoading = isPreparePayrollPending || isPayScheduleLoading
const isInitialLoading = isPreparePayrollPending && !hasInitialDataRef.current
const isPaginating = isPreparePayrollPending && hasInitialDataRef.current
const isLoading = isInitialLoading || isPayScheduleLoading

return {
handlePreparePayroll,
preparedPayroll,
paySchedule: payScheduleData?.payScheduleObject,
isLoading,
isPaginating,
hasInitialData: hasInitialDataRef.current,
}
}