Skip to content
Merged
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
39 changes: 21 additions & 18 deletions docs/component-adapter/component-inventory.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,24 +265,27 @@

## DatePickerProps

| Prop | Type | Required | Description |
| --------------------------- | ------------------------------- | -------- | ---------------------------------------------------------------------- |
| **inputRef** | `Ref<HTMLInputElement \| null>` | No | React ref for the date input element |
| **isDisabled** | `boolean` | No | Disables the date picker and prevents interaction |
| **isInvalid** | `boolean` | No | Indicates that the field has an error |
| **onChange** | `(value: Date \| null) => void` | No | Callback when selected date changes |
| **onBlur** | `() => void` | No | Handler for blur events |
| **label** | `string` | Yes | Label text for the date picker field |
| **value** | `null \| Date` | No | Currently selected date value |
| **placeholder** | `string` | No | Placeholder text when no date is selected |
| **portalContainer** | `HTMLElement` | No | Element to use as the portal container |
| **description** | `React.ReactNode` | No | Optional description text for the field |
| **errorMessage** | `string` | No | Error message to display when the field is invalid |
| **isRequired** | `boolean` | No | Indicates if the field is required |
| **shouldVisuallyHideLabel** | `boolean` | No | Hides the label visually while keeping it accessible to screen readers |
| **className** | `string` | No | - |
| **id** | `string` | No | - |
| **name** | `string` | No | - |
| Prop | Type | Required | Description |
| --------------------------- | ------------------------------- | -------- | --------------------------------------------------------------------------------------------- |
| **inputRef** | `Ref<HTMLInputElement \| null>` | No | React ref for the date input element |
| **isDisabled** | `boolean` | No | Disables the date picker and prevents interaction |
| **isInvalid** | `boolean` | No | Indicates that the field has an error |
| **onChange** | `(value: Date \| null) => void` | No | Callback when selected date changes |
| **onBlur** | `() => void` | No | Handler for blur events |
| **label** | `string` | Yes | Label text for the date picker field |
| **value** | `null \| Date` | No | Currently selected date value |
| **placeholder** | `string` | No | Placeholder text when no date is selected |
| **portalContainer** | `HTMLElement` | No | Element to use as the portal container |
| **minDate** | `Date` | No | Minimum selectable date. Dates before this will be disabled. |
| **maxDate** | `Date` | No | Maximum selectable date. Dates after this will be disabled. |
| **isDateDisabled** | `(date: Date) => boolean` | No | Callback to determine if a specific date should be disabled. Return true to disable the date. |
| **description** | `React.ReactNode` | No | Optional description text for the field |
| **errorMessage** | `string` | No | Error message to display when the field is invalid |
| **isRequired** | `boolean` | No | Indicates if the field is required |
| **shouldVisuallyHideLabel** | `boolean` | No | Hides the label visually while keeping it accessible to screen readers |
| **className** | `string` | No | - |
| **id** | `string` | No | - |
| **name** | `string` | No | - |

## DescriptionListProps

Expand Down
9 changes: 9 additions & 0 deletions docs/reference/endpoint-inventory.json
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,15 @@
{
"method": "POST",
"path": "/v1/companies/:companyId/pay_schedules"
},
{
"method": "GET",
"path": "/v1/companies/:companyUuid/payment_configs"
}
],
"variables": [
"companyId",
"companyUuid",
"payScheduleId"
]
},
Expand Down Expand Up @@ -1309,6 +1314,10 @@
"method": "POST",
"path": "/v1/companies/:companyId/pay_schedules"
},
{
"method": "GET",
"path": "/v1/companies/:companyUuid/payment_configs"
},
{
"method": "PUT",
"path": "/v1/companies/:companyUuid/tax_requirements/:state"
Expand Down
1 change: 1 addition & 0 deletions docs/reference/endpoint-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import inventory from '@gusto/embedded-react-sdk/endpoint-inventory.json'
| | PUT | `/v1/companies/:companyId/pay_schedules/:payScheduleId` |
| | GET | `/v1/companies/:companyId/pay_schedules` |
| | POST | `/v1/companies/:companyId/pay_schedules` |
| | GET | `/v1/companies/:companyUuid/payment_configs` |
| **Company.StateTaxes** | PUT | `/v1/companies/:companyUuid/tax_requirements/:state` |
| | GET | `/v1/companies/:companyUuid/tax_requirements/:state` |
| | GET | `/v1/companies/:companyUuid/tax_requirements` |
Expand Down
33 changes: 32 additions & 1 deletion src/components/Common/UI/DatePicker/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
} from 'react-aria-components'
import { useTranslation } from 'react-i18next'
import classNames from 'classnames'
import { parseDate } from '@internationalized/date'
import { parseDate, type CalendarDate } from '@internationalized/date'
import { useFieldIds } from '../hooks/useFieldIds'
import styles from './DatePicker.module.scss'
import type { DatePickerProps } from './DatePickerTypes'
Expand All @@ -26,6 +26,21 @@ import CaretLeft from '@/assets/icons/caret-left.svg?react'
import AlertCircle from '@/assets/icons/alert-circle.svg?react'
import { formatDateToStringDate } from '@/helpers/dateFormatting'

function dateToCalendarDate(date: Date): CalendarDate | undefined {
if (!(date instanceof Date) || isNaN(date.getTime())) {
return undefined
}

// Use local date parts to avoid UTC timezone shift from toISOString()
const dateString = [
String(date.getFullYear()).padStart(4, '0'),
String(date.getMonth() + 1).padStart(2, '0'),
String(date.getDate()).padStart(2, '0'),
].join('-')

return parseDate(dateString)
}

function calendarDateValueToDate(dateValue: DateValue | null): Date | null {
if (!dateValue) return null

Expand All @@ -52,6 +67,9 @@ export const DatePicker = ({
onBlur,
value,
portalContainer,
minDate,
maxDate,
isDateDisabled,
...props
}: DatePickerProps) => {
const { t } = useTranslation()
Expand All @@ -67,6 +85,16 @@ export const DatePicker = ({
const formattedDate = value ? formatDateToStringDate(value) : ''
const internalValue = formattedDate ? parseDate(formattedDate) : null

// Convert date constraint props to react-aria DateValue format
const minValue = minDate ? dateToCalendarDate(minDate) : undefined
const maxValue = maxDate ? dateToCalendarDate(maxDate) : undefined
const isDateUnavailable = isDateDisabled
? (dateValue: DateValue) => {
const jsDate = calendarDateValueToDate(dateValue)
return jsDate ? isDateDisabled(jsDate) : false
}
: undefined

// Handle internal onChange to convert DateValue back to Date
const handleChange = (dateValue: DateValue | null) => {
if (onChange) {
Expand Down Expand Up @@ -95,6 +123,9 @@ export const DatePicker = ({
isInvalid={isInvalid}
value={internalValue}
onChange={handleChange}
minValue={minValue}
maxValue={maxValue}
isDateUnavailable={isDateUnavailable}
{...props}
>
<Group>
Expand Down
13 changes: 13 additions & 0 deletions src/components/Common/UI/DatePicker/DatePickerTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,17 @@ export interface DatePickerProps
* Element to use as the portal container
*/
portalContainer?: HTMLElement
/**
* Minimum selectable date. Dates before this will be disabled.
*/
minDate?: Date
/**
* Maximum selectable date. Dates after this will be disabled.
*/
maxDate?: Date
/**
* Callback to determine if a specific date should be disabled.
* Return true to disable the date.
*/
isDateDisabled?: (date: Date) => boolean
}
5 changes: 5 additions & 0 deletions src/components/Company/PaySchedule/PaySchedule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { usePaySchedulesGetPreview } from '@gusto/embedded-api/react-query/paySc
import { usePaySchedulesUpdateMutation } from '@gusto/embedded-api/react-query/paySchedulesUpdate'
import { usePaySchedulesGetAllSuspense } from '@gusto/embedded-api/react-query/paySchedulesGetAll'
import { usePaySchedulesCreateMutation } from '@gusto/embedded-api/react-query/paySchedulesCreate'
import { usePaymentConfigsGet } from '@gusto/embedded-api/react-query/paymentConfigsGet'
import type { PayScheduleObject as PayScheduleType } from '@gusto/embedded-api/models/components/payscheduleobject'
import type { Frequency } from '@gusto/embedded-api/models/operations/postv1companiescompanyidpayschedules'
import type { MODE, PayScheduleInputs, PayScheduleOutputs } from './usePaySchedule'
Expand Down Expand Up @@ -54,6 +55,9 @@ const Root = ({ companyId, children, defaultValues }: PayScheduleProps) => {
companyId,
})

const { data: paymentConfigs } = usePaymentConfigsGet({ companyUuid: companyId })
const paymentSpeed = paymentConfigs?.paymentConfigs?.paymentSpeed

const [mode, setMode] = useState<MODE>(
paySchedules.payScheduleList?.length === 0 ? 'ADD_PAY_SCHEDULE' : 'LIST_PAY_SCHEDULES',
)
Expand Down Expand Up @@ -219,6 +223,7 @@ const Root = ({ companyId, children, defaultValues }: PayScheduleProps) => {
payPeriodPreview: payPreviewData?.object?.payPeriods,
payPreviewLoading: isLoading,
currentPaySchedule,
paymentSpeed,
}}
>
<span data-testid="pay-schedule-edit-form">
Expand Down
7 changes: 5 additions & 2 deletions src/components/Company/PaySchedule/_parts/Edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const Edit = () => {
const Components = useComponentContext()
const { t } = useTranslation('Company.PaySchedule')
const dateFormatter = useDateFormatter()
const { payPeriodPreview, mode, payPreviewLoading } = usePaySchedule()
const { payPeriodPreview, mode, payPreviewLoading, paymentSpeed } = usePaySchedule()
const { setValue } = useFormContext<PayScheduleInputs>()
const [selectedPayPeriodIndex, setSelectedPayPeriodIndex] = useState(0)

Expand Down Expand Up @@ -73,8 +73,11 @@ export const Edit = () => {
<DatePickerField
name="anchorPayDate"
label={t('labels.firstPayDate')}
description={t('descriptions.anchorPayDateDescription')}
description={t('descriptions.anchorPayDateDescription', {
count: paymentSpeed ? Number(paymentSpeed.split('-')[0]) : 2,
})}
isRequired
minDate={new Date()}
/>
<DatePickerField
name="anchorEndOfPayPeriod"
Expand Down
1 change: 1 addition & 0 deletions src/components/Company/PaySchedule/usePaySchedule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type PayScheduleContextType = {
currentPaySchedule: PayScheduleType | undefined | null
payPeriodPreview?: PayPeriods[]
payPreviewLoading?: boolean
paymentSpeed?: string
}

export const PayScheduleSchema = z.object({
Expand Down
3 changes: 2 additions & 1 deletion src/i18n/en/Company.PaySchedule.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"loading": "Loading...",
"descriptions": {
"frequencyOptionsDescription": "Select the pay days for the month.",
"anchorPayDateDescription": "Please account for the 2 days it will take to process payroll.",
"anchorPayDateDescription_one": "Please account for the {{count}} day it will take to process payroll.",
"anchorPayDateDescription_other": "Please account for the {{count}} days it will take to process payroll.",
"anchorEndOfPayPeriodDescription": "The last date of the first pay period to help calculate future pay periods. This can be the same date as the first pay date."
},
"payPreview": {
Expand Down
3 changes: 2 additions & 1 deletion src/types/i18next.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,8 @@ export interface CompanyPaySchedule{
"loading":string;
"descriptions":{
"frequencyOptionsDescription":string;
"anchorPayDateDescription":string;
"anchorPayDateDescription_one":string;
"anchorPayDateDescription_other":string;
"anchorEndOfPayPeriodDescription":string;
};
"payPreview":{
Expand Down
Loading