Skip to content
Draft
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
45 changes: 45 additions & 0 deletions docs/reference/endpoint-inventory.json
Original file line number Diff line number Diff line change
Expand Up @@ -1119,6 +1119,22 @@
"companyUuid"
]
},
"TimeOff.PolicyList": {
"endpoints": [
{
"method": "GET",
"path": "/v1/companies/:companyUuid/time_off_policies"
},
{
"method": "PUT",
"path": "/v1/time_off_policies/:timeOffPolicyUuid/deactivate"
}
],
"variables": [
"companyUuid",
"timeOffPolicyUuid"
]
},
"UNSTABLE_Hooks.hooks.useCompensationForm": {
"endpoints": [
{
Expand Down Expand Up @@ -1912,6 +1928,35 @@
"payrollUuid",
"wireInRequestUuid"
]
},
"TimeOff.TimeOffFlow": {
"blocks": [
"TimeOff.AddEmployeesHoliday",
"TimeOff.AddEmployeesToPolicy",
"TimeOff.HolidaySelectionForm",
"TimeOff.PolicyDetailsForm",
"TimeOff.PolicyList",
"TimeOff.PolicySettings",
"TimeOff.PolicyTypeSelector",
"TimeOff.ViewHolidayEmployees",
"TimeOff.ViewHolidaySchedule",
"TimeOff.ViewPolicyDetails",
"TimeOff.ViewPolicyEmployees"
],
"endpoints": [
{
"method": "GET",
"path": "/v1/companies/:companyUuid/time_off_policies"
},
{
"method": "PUT",
"path": "/v1/time_off_policies/:timeOffPolicyUuid/deactivate"
}
],
"variables": [
"companyUuid",
"timeOffPolicyUuid"
]
}
}
}
8 changes: 8 additions & 0 deletions docs/reference/endpoint-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,13 @@ import inventory from '@gusto/embedded-react-sdk/endpoint-inventory.json'
| | GET | `/v1/companies/:companyId/pay_schedules` |
| | POST | `/v1/companies/:companyUuid/payrolls/skip` |

## TimeOff components

| Component | Method | Path |
| --- | --- | --- |
| **TimeOff.PolicyList** | GET | `/v1/companies/:companyUuid/time_off_policies` |
| | PUT | `/v1/time_off_policies/:timeOffPolicyUuid/deactivate` |

## UNSTABLE_Hooks components

| Component | Method | Path |
Expand Down Expand Up @@ -252,3 +259,4 @@ Flows compose multiple blocks into a single workflow. The endpoint list for a fl
| **Employee.SelfOnboardingFlow** | Employee.EmploymentEligibility, Employee.FederalTaxes, Employee.Landing, Employee.OnboardingSummary, Employee.PaymentMethod, Employee.Profile, Employee.StateTaxes |
| **Payroll.PayrollExecutionFlow** | Payroll.ConfirmWireDetails |
| **Payroll.PayrollFlow** | Payroll.ConfirmWireDetails, Payroll.OffCycleFlow, Payroll.PayrollBlocker, Payroll.PayrollConfiguration, Payroll.PayrollEditEmployee, Payroll.PayrollLanding, Payroll.PayrollOverview, Payroll.PayrollReceipts, Payroll.TransitionFlow |
| **TimeOff.TimeOffFlow** | TimeOff.AddEmployeesHoliday, TimeOff.AddEmployeesToPolicy, TimeOff.HolidaySelectionForm, TimeOff.PolicyDetailsForm, TimeOff.PolicyList, TimeOff.PolicySettings, TimeOff.PolicyTypeSelector, TimeOff.ViewHolidayEmployees, TimeOff.ViewHolidaySchedule, TimeOff.ViewPolicyDetails, TimeOff.ViewPolicyEmployees |
46 changes: 46 additions & 0 deletions e2e/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { TransitionFlow } from '@/components/Payroll/Transition/TransitionFlow'
import { PaymentFlow } from '@/components/Contractor/Payments/PaymentFlow/PaymentFlow'
import { TerminationFlow } from '@/components/Employee/Terminations/TerminationFlow/TerminationFlow'
import { DismissalFlow } from '@/components/Payroll/Dismissal'
import { TimeOffFlow } from '@/components/TimeOff/TimeOffFlow/TimeOffFlow'
import '@/styles/sdk.scss'

const DEFAULT_API_BASE_URL = 'https://api.gusto.com'
Expand All @@ -24,6 +25,7 @@ type FlowType =
| 'contractor-payment'
| 'termination'
| 'dismissal'
| 'time-off'

interface E2EConfig {
flow: FlowType
Expand Down Expand Up @@ -91,14 +93,58 @@ function FlowRenderer({ config }: { config: E2EConfig }) {
return <TerminationFlow companyId={companyId} employeeId={employeeId} onEvent={handleEvent} />
case 'dismissal':
return <DismissalFlow companyId={companyId} employeeId={employeeId} onEvent={handleEvent} />
case 'time-off':
return <TimeOffFlow companyId={companyId} onEvent={handleEvent} />
default:
return <div>Unknown flow: {flow}</div>
}
}

const FLOW_OPTIONS: { value: FlowType; label: string }[] = [
{ value: 'employee-onboarding', label: 'Employee Onboarding' },
{ value: 'employee-self-onboarding', label: 'Employee Self-Onboarding' },
{ value: 'company-onboarding', label: 'Company Onboarding' },
{ value: 'contractor-onboarding', label: 'Contractor Onboarding' },
{ value: 'payroll', label: 'Payroll' },
{ value: 'transition', label: 'Transition' },
{ value: 'contractor-payment', label: 'Contractor Payment' },
{ value: 'termination', label: 'Termination' },
{ value: 'dismissal', label: 'Dismissal' },
{ value: 'time-off', label: 'Time Off Management' },
]

function FlowSelector({ currentFlow }: { currentFlow: FlowType }) {
const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
const params = new URLSearchParams(window.location.search)
params.set('flow', e.target.value)
window.location.search = params.toString()
}

return (
<div style={{ padding: '8px 0', borderBottom: '1px solid #e5e7eb', marginBottom: 16 }}>
<label htmlFor="flow-selector" style={{ marginRight: 8, fontSize: 14 }}>
Flow:
</label>
<select
id="flow-selector"
value={currentFlow}
onChange={handleChange}
style={{ fontSize: 14 }}
>
{FLOW_OPTIONS.map(opt => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
</div>
)
}

function App({ config }: { config: E2EConfig }) {
return (
<StrictMode>
<FlowSelector currentFlow={config.flow} />
<GustoProvider config={{ baseUrl: config.baseUrl }}>
<FlowRenderer config={config} />
</GustoProvider>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { BaseComponent, type BaseComponentInterface } from '@/components/Base'
import { componentEvents } from '@/shared/constants'

export interface AddEmployeesHolidayProps extends BaseComponentInterface {
companyId: string
}

export function AddEmployeesHoliday(props: AddEmployeesHolidayProps) {
return (
<BaseComponent {...props}>
<div>
<p>Add Employees to Holiday (companyId: {props.companyId})</p>
<button
onClick={() => {
props.onEvent(componentEvents.TIME_OFF_HOLIDAY_ADD_EMPLOYEES_DONE)
}}
>
Done
</button>
<button
onClick={() => {
props.onEvent(componentEvents.TIME_OFF_HOLIDAY_ADD_EMPLOYEES_ERROR, {
alert: { type: 'error', title: 'Failed to add employees to holiday' },
})
}}
>
Simulate Error
</button>
<button
onClick={() => {
props.onEvent(componentEvents.CANCEL)
}}
>
Cancel
</button>
</div>
</BaseComponent>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { BaseComponent, type BaseComponentInterface } from '@/components/Base'
import { componentEvents } from '@/shared/constants'

export interface AddEmployeesToPolicyProps extends BaseComponentInterface {
policyId: string
}

export function AddEmployeesToPolicy(props: AddEmployeesToPolicyProps) {
return (
<BaseComponent {...props}>
<div>
<p>Add Employees to Policy + Starting Balances (policyId: {props.policyId})</p>
<button
onClick={() => {
props.onEvent(componentEvents.TIME_OFF_ADD_EMPLOYEES_DONE)
}}
>
Done
</button>
<button
onClick={() => {
props.onEvent(componentEvents.TIME_OFF_ADD_EMPLOYEES_ERROR, {
alert: { type: 'error', title: 'Failed to add employees' },
})
}}
>
Simulate Error
</button>
<button
onClick={() => {
props.onEvent(componentEvents.CANCEL)
}}
>
Cancel
</button>
</div>
</BaseComponent>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { BaseComponent, type BaseComponentInterface } from '@/components/Base'
import { componentEvents } from '@/shared/constants'

export interface HolidaySelectionFormProps extends BaseComponentInterface {
companyId: string
}

export function HolidaySelectionForm(props: HolidaySelectionFormProps) {
return (
<BaseComponent {...props}>
<div>
<p>Holiday Selection Form (companyId: {props.companyId})</p>
<button
onClick={() => {
props.onEvent(componentEvents.TIME_OFF_HOLIDAY_SELECTION_DONE)
}}
>
Done
</button>
<button
onClick={() => {
props.onEvent(componentEvents.TIME_OFF_HOLIDAY_CREATE_ERROR, {
alert: { type: 'error', title: 'Failed to create holiday policy' },
})
}}
>
Simulate Error
</button>
<button
onClick={() => {
props.onEvent(componentEvents.CANCEL)
}}
>
Cancel
</button>
</div>
</BaseComponent>
)
}
44 changes: 44 additions & 0 deletions src/components/TimeOff/PolicyDetailsForm/PolicyDetailsForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { BaseComponent, type BaseComponentInterface } from '@/components/Base'
import { componentEvents } from '@/shared/constants'

export interface PolicyDetailsFormProps extends BaseComponentInterface {
companyId: string
policyType: 'sick' | 'vacation'
}

export function PolicyDetailsForm(props: PolicyDetailsFormProps) {
return (
<BaseComponent {...props}>
<div>
<p>
Policy Details Form (type: {props.policyType}, companyId: {props.companyId})
</p>
<button
onClick={() => {
props.onEvent(componentEvents.TIME_OFF_POLICY_DETAILS_DONE, {
policyId: 'mock-policy-id',
})
}}
>
Done
</button>
<button
onClick={() => {
props.onEvent(componentEvents.TIME_OFF_POLICY_CREATE_ERROR, {
alert: { type: 'error', title: 'Failed to create policy' },
})
}}
>
Simulate Error
</button>
<button
onClick={() => {
props.onEvent(componentEvents.CANCEL)
}}
>
Cancel
</button>
</div>
</BaseComponent>
)
}
Loading
Loading