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
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.alertContainer {
padding: toRem(20);
border-bottom: 1px solid var(--g-colorBorderSecondary);

> * > [role='alert'] {
margin-bottom: 0;
}
}
225 changes: 225 additions & 0 deletions sdk-app/src/design/prototypes/employee-compensation/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
import { useState } from 'react'
import styles from './employeeCompensation.module.scss'
import { useComponentContext } from '@/contexts/ComponentAdapter/useComponentContext'
import { DataView, Flex, useDataView } from '@/components/Common'
import { HamburgerMenu } from '@/components/Common/HamburgerMenu'
import PencilSvg from '@/assets/icons/pencil.svg?react'
import TrashCanSvg from '@/assets/icons/trashcan.svg?react'

interface CompensationRow {
id: string
job: string
wage: string
payType: string
startDate: string
}

const rows: CompensationRow[] = [
{
id: '1',
job: 'Administrator',
wage: '$20.00 per hour',
payType: 'By the hour',
startDate: 'April 5, 2019',
},
{
id: '2',
job: 'Administrative Supervisor',
wage: '$35.00 per hour',
payType: 'By the hour',
startDate: 'May 1, 2019',
},
]

interface PendingChange {
id: string
title: string
date: string
details: string[]
}

const initialPendingChanges: PendingChange[] = [
{
id: 'pending-1',
title: 'Administrative Supervisor',
date: 'May 24, 2026',
details: [
'Job title will change to Senior Administrator',
'Wage will change to $26.00 per hour',
],
},
{
id: 'pending-2',
title: 'Key Holder',
date: 'June 1, 2026',
details: ['Alejandro will start this job on June 1, with a wage of $22.00 per hour'],
},
]

export function EmployeeCompensation() {
const Components = useComponentContext()
const [isReviewOpen, setIsReviewOpen] = useState(false)
const [pendingChanges, setPendingChanges] = useState<PendingChange[]>(initialPendingChanges)
const [cancellingId, setCancellingId] = useState<string | null>(null)
const [cancelledTitles, setCancelledTitles] = useState<string[]>([])

const handleCancelChange = (change: PendingChange) => {
setCancellingId(change.id)
setTimeout(() => {
setPendingChanges(prev => prev.filter(c => c.id !== change.id))
setCancelledTitles(prev => [...prev, change.title])
setCancellingId(null)
}, 1500)
}

const dataViewProps = useDataView<CompensationRow>({
data: rows,
columns: [
{
key: 'job',
title: 'Job',
render: row => (
<>
<Components.Text size="sm" weight="medium">
{row.job}
</Components.Text>
<Components.Text variant="supporting" size="sm">
{row.wage}
</Components.Text>
</>
),
},
{
key: 'payType',
title: 'Pay type',
render: row => row.payType,
},
{
key: 'startDate',
title: 'Start date',
render: row => row.startDate,
},
],
itemMenu: row => (
<HamburgerMenu
triggerLabel={`Actions for ${row.job}`}
items={[
{
label: 'Edit',
icon: <PencilSvg aria-hidden />,
onClick: () => {},
},
{
label: 'Delete',
icon: <TrashCanSvg aria-hidden />,
onClick: () => {},
},
]}
/>
),
})

return (
<Flex flexDirection="column" gap={4}>
<Components.Box
withPadding={false}
header={
<Flex justifyContent="space-between" alignItems="center" gap={4}>
<Components.Heading as="h3" styledAs="h4">
Compensation
</Components.Heading>
</Flex>
}
>
<div className={styles.alertContainer}>
<Components.Alert
label="There are multiple pending changes to Alejandro's compensation."
status="warning"
>
<Components.Button
variant="secondary"
onClick={() => {
setIsReviewOpen(true)
}}
>
Review
</Components.Button>
</Components.Alert>
</div>
<DataView isWithinBox label="Compensation" {...dataViewProps} />
</Components.Box>
<Components.Modal
isOpen={isReviewOpen}
onClose={() => {
setIsReviewOpen(false)
}}
shouldCloseOnBackdropClick
footer={
<Flex justifyContent="flex-end" gap={8}>
<Components.Button
variant="secondary"
onClick={() => {
setIsReviewOpen(false)
}}
>
Close
</Components.Button>
</Flex>
}
>
<Flex flexDirection="column" gap={32}>
<Flex flexDirection="column" gap={4}>
<Components.Heading as="h3" styledAs="h4">
Review pending changes
</Components.Heading>
<Components.Text variant="supporting">
Placeholder content describing the pending compensation changes for Alejandro.
</Components.Text>
</Flex>
<Flex flexDirection="column" gap={20}>
{cancelledTitles.map((title, index) => (
<Components.Alert
key={`cancelled-${index}`}
label="Change cancelled"
status="success"
onDismiss={() => {}}
/>
))}
{pendingChanges.map(change => (
<Components.Box
key={change.id}
footer={
<Components.Button
variant="secondary"
isLoading={cancellingId === change.id}
onClick={() => {
handleCancelChange(change)
}}
>
{cancellingId === change.id ? 'Cancelling' : 'Cancel change'}
</Components.Button>
}
>
<Flex flexDirection="column" gap={16}>
<Flex flexDirection="column" gap={0}>
<Components.Text weight="medium">{change.title}</Components.Text>
<Components.Text variant="supporting" size="sm">
{change.date}
</Components.Text>
</Flex>
<Flex flexDirection="column" gap={4}>
{change.details.map((detail, index) => (
<Components.Text key={index} size="sm">
{detail}
</Components.Text>
))}
</Flex>
</Flex>
</Components.Box>
))}
</Flex>
</Flex>
</Components.Modal>
</Flex>
)
}
8 changes: 7 additions & 1 deletion sdk-app/src/design/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ export const categorizedRegistry: CategorizedRegistry = {
description: 'The contractor-facing onboarding experience after receiving an invite link.',
},
],
Employees: [],
Employees: [
{
name: 'Employee Compensation',
path: '/design/employee-compensation',
description: 'A prototype for managing employee compensation.',
},
],
Payroll: [],
}
2 changes: 2 additions & 0 deletions sdk-app/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { ContractorDismiss } from './design/prototypes/contractor-management/con
import { ContractorRehire } from './design/prototypes/contractor-management/contractor-list/ContractorRehire'
import { AddContractor } from './design/prototypes/contractor-management/contractor-list/AddContractor'
import { ContractorSelfOnboarding } from './design/prototypes/contractor-management/self-onboarding'
import { EmployeeCompensation } from './design/prototypes/employee-compensation'
import './app.scss'
import '@/styles/sdk.scss'

Expand Down Expand Up @@ -43,6 +44,7 @@ const router = createBrowserRouter([
],
},
{ path: 'contractor-self-onboarding', element: <ContractorSelfOnboarding /> },
{ path: 'employee-compensation', element: <EmployeeCompensation /> },
],
},
],
Expand Down
7 changes: 7 additions & 0 deletions src/components/Common/UI/Alert/Alert.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@
margin-top: toRem(-4); // Pull content closer to H6
}

.actions {
display: flex;
align-items: center;
gap: toRem(8);
flex-shrink: 0;
}

.dismiss {
color: var(--g-colorBodySubContent);
flex-shrink: 0;
Expand Down
9 changes: 7 additions & 2 deletions src/components/Common/UI/Alert/Alert.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useId, useRef } from 'react'
import { Children, isValidElement, useEffect, useId, useRef } from 'react'
import classNames from 'classnames'
import { ButtonIcon } from '../Button/ButtonIcon'
import { type AlertProps, AlertDefaults } from './AlertTypes'
Expand Down Expand Up @@ -34,6 +34,10 @@ export function Alert(rawProps: AlertProps) {
}
}, [disableScrollIntoView])

const childrenArray = Children.toArray(children)
const hasChildren = childrenArray.length > 0
const childrenAreAllElements = hasChildren && childrenArray.every(child => isValidElement(child))

return (
<div className={classNames(styles.root, className)}>
<div
Expand All @@ -48,6 +52,7 @@ export function Alert(rawProps: AlertProps) {
<div className={styles.iconLabelContainer}>
<div className={styles.icon}>{icon || defaultIcon}</div>
<h6 id={id}>{label}</h6>
{childrenAreAllElements && <div className={styles.actions}>{children}</div>}
{onDismiss && (
<div className={styles.dismiss}>
<ButtonIcon variant="tertiary" onClick={onDismiss} aria-label="Dismiss alert">
Expand All @@ -57,7 +62,7 @@ export function Alert(rawProps: AlertProps) {
)}
</div>
</div>
{children && <div className={styles.content}>{children}</div>}
{hasChildren && !childrenAreAllElements && <div className={styles.content}>{children}</div>}
</div>
</div>
)
Expand Down
Loading