diff --git a/e2e/main.tsx b/e2e/main.tsx index e340c5194..a006c57f4 100644 --- a/e2e/main.tsx +++ b/e2e/main.tsx @@ -114,33 +114,42 @@ const FLOW_OPTIONS: { value: FlowType; label: string }[] = [ ] function FlowSelector({ currentFlow }: { currentFlow: FlowType }) { - const handleChange = (e: React.ChangeEvent) => { - const params = new URLSearchParams(window.location.search) - params.set('flow', e.target.value) - window.location.search = params.toString() - } - return ( - + Flow: + {FLOW_OPTIONS.map((opt, i) => { + const params = new URLSearchParams(window.location.search) + params.set('flow', opt.value) + const isActive = opt.value === currentFlow + return ( + + {i > 0 && |} + + {opt.label} + + + ) + })} + ) } diff --git a/src/components/UNSTABLE_TimeOff/PolicyList/PolicyListPresentation.module.scss b/src/components/UNSTABLE_TimeOff/PolicyList/PolicyListPresentation.module.scss new file mode 100644 index 000000000..b33b09745 --- /dev/null +++ b/src/components/UNSTABLE_TimeOff/PolicyList/PolicyListPresentation.module.scss @@ -0,0 +1,6 @@ +.actionsCell { + display: flex; + align-items: center; + justify-content: flex-end; + gap: toRem(12); +} diff --git a/src/components/UNSTABLE_TimeOff/PolicyList/PolicyListPresentation.tsx b/src/components/UNSTABLE_TimeOff/PolicyList/PolicyListPresentation.tsx new file mode 100644 index 000000000..937ce5100 --- /dev/null +++ b/src/components/UNSTABLE_TimeOff/PolicyList/PolicyListPresentation.tsx @@ -0,0 +1,153 @@ +import { useState } from 'react' +import { useTranslation } from 'react-i18next' +import type { PolicyListPresentationProps, PolicyListItem } from './PolicyListTypes' +import styles from './PolicyListPresentation.module.scss' +import { + DataView, + Flex, + EmptyData, + ActionsLayout, + HamburgerMenu, + useDataView, +} from '@/components/Common' +import { useComponentContext } from '@/contexts/ComponentAdapter/useComponentContext' +import { useI18n } from '@/i18n' + +export function PolicyListPresentation({ + policies, + onCreatePolicy, + onEditPolicy, + onFinishSetup, + onDeletePolicy, + deleteSuccessAlert, + onDismissDeleteAlert, + isDeletingPolicyId, +}: PolicyListPresentationProps) { + const { Button, Heading, Text, Alert, Dialog } = useComponentContext() + useI18n('Company.TimeOff.TimeOffPolicies') + const { t } = useTranslation('Company.TimeOff.TimeOffPolicies') + + const [deletePolicyDialogState, setDeletePolicyDialogState] = useState<{ + isOpen: boolean + policy: PolicyListItem | null + }>({ + isOpen: false, + policy: null, + }) + + const handleOpenDeleteDialog = (policy: PolicyListItem) => { + setDeletePolicyDialogState({ isOpen: true, policy }) + } + + const handleCloseDeleteDialog = () => { + setDeletePolicyDialogState({ isOpen: false, policy: null }) + } + + const handleConfirmDelete = () => { + if (deletePolicyDialogState.policy) { + onDeletePolicy(deletePolicyDialogState.policy) + handleCloseDeleteDialog() + } + } + + const { ...dataViewProps } = useDataView({ + data: policies, + columns: [ + { + title: t('tableHeaders.name'), + render: (policy: PolicyListItem) => {policy.name}, + }, + { + title: t('tableHeaders.enrolled'), + render: (policy: PolicyListItem) => ( + {policy.enrolledDisplay} + ), + }, + ], + itemMenu: (policy: PolicyListItem) => { + const isDeleting = isDeletingPolicyId === policy.uuid + + return ( +
+ {!policy.isComplete && ( + + )} + { + onEditPolicy(policy) + }, + }, + { + label: t('actions.deletePolicy'), + onClick: () => { + handleOpenDeleteDialog(policy) + }, + }, + ]} + /> +
+ ) + }, + emptyState: () => ( + + + + + + ), + }) + + return ( + + {deleteSuccessAlert && ( + + )} + + + {t('pageTitle')} + {policies.length > 0 && ( + + )} + + + + + + {t('deletePolicyDialog.description', { + name: deletePolicyDialogState.policy?.name ?? '', + })} + + + ) +} diff --git a/src/components/UNSTABLE_TimeOff/PolicyList/PolicyListTypes.ts b/src/components/UNSTABLE_TimeOff/PolicyList/PolicyListTypes.ts new file mode 100644 index 000000000..775ca1ebc --- /dev/null +++ b/src/components/UNSTABLE_TimeOff/PolicyList/PolicyListTypes.ts @@ -0,0 +1,18 @@ +export interface PolicyListItem { + uuid: string + name: string + policyType: string + isComplete: boolean + enrolledDisplay: string +} + +export interface PolicyListPresentationProps { + policies: PolicyListItem[] + onCreatePolicy: () => void + onEditPolicy: (policy: PolicyListItem) => void + onFinishSetup: (policy: PolicyListItem) => void + onDeletePolicy: (policy: PolicyListItem) => void + deleteSuccessAlert?: string | null + onDismissDeleteAlert?: () => void + isDeletingPolicyId?: string | null +} diff --git a/src/i18n/en/Company.TimeOff.TimeOffPolicies.json b/src/i18n/en/Company.TimeOff.TimeOffPolicies.json index 19f393b76..7c7be940c 100644 --- a/src/i18n/en/Company.TimeOff.TimeOffPolicies.json +++ b/src/i18n/en/Company.TimeOff.TimeOffPolicies.json @@ -1,31 +1,35 @@ { - "pageTitle": "Time off policies", - "addPolicyCta": "Add policy", + "pageTitle": "Time Off Policies", + "createPolicyCta": "Create policy", "tableHeaders": { "name": "Name", - "enrolled": "Enrolled", - "actions": "Actions" + "enrolled": "Enrolled" }, + "tableLabel": "Time off policies", "actions": { - "viewPolicy": "View policy", "editPolicy": "Edit policy", "deletePolicy": "Delete policy" }, + "finishSetupCta": "Finish setup", + "allEmployeesLabel": "All employees", + "enrolledDash": "\u2013", "employeeCount_one": "{{count}} employee", "employeeCount_other": "{{count}} employees", "incompleteBadge": "Incomplete", "holidayPayPolicy": "Holiday pay policy", "deletePolicyDialog": { "title": "Are you sure you want to delete the policy \"{{name}}\"?", - "description": "This will delete the policy \"{{name}}\" and all associated time off requests." + "description": "This will delete the policy \"{{name}}\" and all associated time off requests.", + "confirmCta": "Delete policy", + "cancelCta": "Cancel" }, "deleteHolidayDialog": { "title": "Are you sure you want to delete the company holiday pay policy?", "description": "This will delete the company holiday pay policy." }, "emptyState": { - "heading": "You haven't added any time off policies yet", - "body": "Time off policies help you track and approve employee leave." + "heading": "You don't have any time off policies", + "body": "Manage employee time off by creating a policy." }, "selectPolicyType": { "title": "Select policy type", diff --git a/src/types/i18next.d.ts b/src/types/i18next.d.ts index e5baef665..b2192c5f0 100644 --- a/src/types/i18next.d.ts +++ b/src/types/i18next.d.ts @@ -479,17 +479,19 @@ export interface CompanyTimeOffHolidayPolicy{ }; export interface CompanyTimeOffTimeOffPolicies{ "pageTitle":string; -"addPolicyCta":string; +"createPolicyCta":string; "tableHeaders":{ "name":string; "enrolled":string; -"actions":string; }; +"tableLabel":string; "actions":{ -"viewPolicy":string; "editPolicy":string; "deletePolicy":string; }; +"finishSetupCta":string; +"allEmployeesLabel":string; +"enrolledDash":string; "employeeCount_one":string; "employeeCount_other":string; "incompleteBadge":string; @@ -497,6 +499,8 @@ export interface CompanyTimeOffTimeOffPolicies{ "deletePolicyDialog":{ "title":string; "description":string; +"confirmCta":string; +"cancelCta":string; }; "deleteHolidayDialog":{ "title":string;