Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
14c5725
feat(dashboard): add CompensationHistory view from Job & pay tab
aaronlee777 Jun 1, 2026
e4355d3
feat(dashboard): combine multi-job compensation history into a filter…
aaronlee777 Jun 1, 2026
f1d9f55
refactor: drop FlexItem wrappers from CompensationHistory header
aaronlee777 Jun 1, 2026
551ba05
chore: relocate CompensationHistory to sdk-app design prototypes
aaronlee777 Jun 1, 2026
55b999e
feat(sdk-app): add component-state prototype viewer for design app
aaronlee777 Jun 2, 2026
ccaa727
refactor(sdk-app): split design components into View/Container layers
aaronlee777 Jun 2, 2026
b5c178c
refactor(sdk-app): nest contractor Views under contractor/management/
aaronlee777 Jun 2, 2026
e57edb9
refactor(sdk-app): flatten ContractorRehire container
aaronlee777 Jun 2, 2026
1b3bd97
refactor(sdk-app): lift shared contractor forms and flatten AddContra…
aaronlee777 Jun 2, 2026
24f5c56
refactor(sdk-app): tidy contractor-management prototype layout
aaronlee777 Jun 2, 2026
ea0a6c9
refactor(sdk-app): split shared PaymentMethodForm into View + container
aaronlee777 Jun 2, 2026
3c5b69a
Merge remote-tracking branch 'origin/main' into al/feat/compensation-…
aaronlee777 Jun 2, 2026
a186e79
refactor(sdk-app): split CompensationHistory into View + container
aaronlee777 Jun 2, 2026
9c33c2a
chore(sdk-app): drop unused MSW machinery from component-states viewer
aaronlee777 Jun 2, 2026
2d22780
feat(sdk-app): add component-states viewer for contractor self-onboar…
aaronlee777 Jun 2, 2026
065a6c2
refactor(sdk-app): rename ContractorSignaturePad → ContractorDocument…
aaronlee777 Jun 2, 2026
2bb5714
fix(sdk-app): bundle W-9 PDF locally for the DocumentSignature demo
aaronlee777 Jun 2, 2026
87764d1
chore(sdk-app): drop trailing blank line in Sidebar.module.scss
aaronlee777 Jun 2, 2026
848e4ea
chore(sdk-app): drop Math.random from mock buildDocument default
aaronlee777 Jun 3, 2026
009874b
refactor(sdk-app): personalize contractor copy + tidy shared scope
aaronlee777 Jun 3, 2026
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
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,8 @@
"msw": {
"workerDirectory": [
"e2e",
"e2e/public"
"e2e/public",
"sdk-app/public"
]
}
}
Binary file added sdk-app/public/sample-documents/w9.pdf
Binary file not shown.
89 changes: 35 additions & 54 deletions sdk-app/src/Sidebar.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -51,48 +51,6 @@
border-bottom: 0.0625rem solid var(--color-border);
}

.shortcutHint {
display: flex;
align-items: center;
gap: 0.375rem;
width: 100%;
margin-bottom: 0.5rem;
padding: 0.25rem 0.5rem;
border: none;
border-radius: 0.25rem;
background: transparent;
font: inherit;
font-size: 0.6875rem;
text-align: left;
color: var(--color-text-muted);
cursor: pointer;
user-select: none;

&:hover {
color: var(--color-text);
background: var(--color-hover-bg);
}

&:focus-visible {
outline: 0.125rem solid var(--color-active);
outline-offset: 0.125rem;
}
}

.shortcutHintKey {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 1.125rem;
padding: 0 0.3125rem;
border: 0.0625rem solid var(--color-border);
border-radius: 0.1875rem;
background: var(--color-badge-bg);
font-family: monospace;
font-size: 0.625rem;
color: var(--color-text);
}

.searchRow {
display: flex;
align-items: stretch;
Expand Down Expand Up @@ -158,10 +116,8 @@
justify-content: space-between;
padding: 0.5rem 1rem;
cursor: pointer;
font-weight: 500;
font-size: 0.8rem;
color: var(--color-text-muted);
user-select: none;

&:hover {
color: var(--color-text);
}
Expand All @@ -173,14 +129,17 @@
border-radius: 0.625rem;
font-size: 0.6875rem;
font-weight: 500;
color: var(--color-text-muted);
}

.categoryTitle {
display: inline-flex;
align-items: center;
gap: 0.375rem;
font-weight: 500;
font-size: 0.8rem;
font-size: 0.6875rem;
font-weight: 600;
letter-spacing: 0.05em;
text-transform: uppercase;
color: var(--color-text-muted);
}

Expand All @@ -197,25 +156,47 @@
list-style: none;
}

.item a {
.item > a {
display: block;
padding: 0.375rem 1rem 0.375rem 1.75rem;
padding: 0.375rem 1rem 0.375rem 1.5rem;
color: var(--color-text);
text-decoration: none;
font-size: 0.8125rem;
border-left: 0.1875rem solid transparent;
font-weight: 500;
transition:
background 0.1s,
color 0.1s;

&:hover {
background: var(--color-hover-bg);
}

&:global(.active) {
color: var(--color-active);
}
}

.subItems {
list-style: none;
margin: 0;
padding: 0;
}

.subItem a {
display: block;
padding: 0.3125rem 1rem 0.3125rem 2.25rem;
color: var(--color-text-muted);
text-decoration: none;
font-size: 0.75rem;
transition:
background 0.1s,
border-color 0.1s;
color 0.1s;

&:hover {
background: var(--color-hover-bg);
}

&:global(.active) {
background: var(--color-active-bg);
border-left-color: var(--color-active);
color: var(--color-active);
font-weight: 500;
}
}
74 changes: 53 additions & 21 deletions sdk-app/src/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState, useMemo } from 'react'
import { NavLink } from 'react-router-dom'
import { NavLink, useLocation } from 'react-router-dom'
import {
categorizedRegistry as previewRegistry,
CATEGORIES as PREVIEW_CATEGORIES,
Expand All @@ -21,6 +21,16 @@ interface SidebarProps {
onShowShortcuts: () => void
}

interface SidebarItem {
name: string
path?: string
children?: SidebarItem[]
}

function isUnder(pathname: string, target: string): boolean {
return pathname === target || pathname.startsWith(`${target}/`)
}

const PREVIEW_CATEGORY_LABELS: Record<string, string> = {
InformationRequests: 'Info Requests',
EmployeeManagement: 'Employee Management',
Expand All @@ -38,16 +48,20 @@ function CategorySection({
mode,
}: {
category: string
items: { name: string; path?: string }[]
items: SidebarItem[]
searchQuery: string
mode: AppMode
}) {
const [collapsed, setCollapsed] = useState(false)
const { pathname } = useLocation()

const filteredItems = useMemo(() => {
if (!searchQuery) return items
const q = searchQuery.toLowerCase()
return items.filter(item => item.name.toLowerCase().includes(q))
return items.filter(item => {
if (item.name.toLowerCase().includes(q)) return true
return item.children?.some(child => child.name.toLowerCase().includes(q)) ?? false
})
}, [items, searchQuery])

if (searchQuery && filteredItems.length === 0) return null
Expand All @@ -73,17 +87,50 @@ function CategorySection({
/>
{displayCategory}
</div>

<span className={styles.categoryCount}>{filteredItems.length}</span>
</div>
{!collapsed && (
<ul className={styles.items}>
{filteredItems.map(item => {
const to =
mode === 'design' && item.path ? item.path : `/${category.toLowerCase()}/${item.name}`
const showChildren =
!!item.children?.length && !!item.path && isUnder(pathname, item.path)
return (
<li key={item.name} className={styles.item}>
<NavLink to={to}>{item.name}</NavLink>
<NavLink to={to} end={!item.children}>
{item.name}
</NavLink>
{showChildren && item.children && (
<ul className={styles.subItems}>
{item.children.map(child => {
const childPath = child.path ?? '#'
// A child is "active" when the URL is under its path,
// unless the URL is under a deeper sibling's path
// (so e.g. Prototype stays highlighted while drilled
// into the live flow but un-highlights on Component
// states).
const isActiveByPath = !!child.path && isUnder(pathname, child.path)
const isUnderDeeperSibling =
!!child.path &&
item.children!.some(
other =>
other !== child &&
!!other.path &&
other.path.startsWith(`${child.path!}/`) &&
isUnder(pathname, other.path),
)
const isActive = isActiveByPath && !isUnderDeeperSibling
return (
<li key={child.name} className={styles.subItem}>
<NavLink to={childPath} className={() => (isActive ? 'active' : '')}>
{child.name}
</NavLink>
</li>
)
})}
</ul>
)}
</li>
)
})}
Expand All @@ -93,14 +140,7 @@ function CategorySection({
)
}

export function Sidebar({
mode,
searchQuery,
onSearchChange,
isOpen,
onToggle,
onShowShortcuts,
}: SidebarProps) {
export function Sidebar({ mode, searchQuery, onSearchChange, isOpen, onToggle }: SidebarProps) {
const placeholder = mode === 'design' ? 'Search prototypes...' : 'Search components...'

if (!isOpen) {
Expand All @@ -122,14 +162,6 @@ export function Sidebar({
return (
<aside className={styles.root}>
<div className={styles.search}>
<button
type="button"
className={styles.shortcutHint}
onClick={onShowShortcuts}
aria-label="Show keyboard shortcuts"
>
Press <kbd className={styles.shortcutHintKey}>?</kbd> for shortcuts
</button>
<div className={styles.searchRow}>
<input
type="text"
Expand Down
30 changes: 28 additions & 2 deletions sdk-app/src/design/DesignLayout.module.scss
Original file line number Diff line number Diff line change
@@ -1,10 +1,36 @@
.shell {
display: flex;
align-items: stretch;
min-height: 100vh;
}

.bodyArea {
flex: 1;
min-width: 0;
display: flex;
justify-content: center;
}

.bodyContent {
padding: 2rem;
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
transition: max-width 0.2s ease;
}

.rightRail {
flex-shrink: 0;
display: flex;
position: sticky;
top: var(--topbar-height, 0);
height: calc(100vh - var(--topbar-height, 0px));
align-self: flex-start;

&:empty {
display: none;
}
}

.switcherContainer {
position: fixed;
bottom: 1.5rem;
Expand Down
11 changes: 9 additions & 2 deletions sdk-app/src/design/DesignLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,19 @@ export function DesignLayout() {
config={{ baseUrl: `${window.location.origin}/api/` }}
theme={resolvedTheme === 'dark' ? darkTheme : undefined}
>
<div className={styles.bodyContent} style={maxWidth ? { maxWidth } : undefined}>
<Outlet context={{ entities }} />
<div className={styles.shell}>
<main className={styles.bodyArea}>
<div className={styles.bodyContent} style={maxWidth ? { maxWidth } : undefined}>
<Outlet context={{ entities }} />
</div>
</main>
<div id={DESIGN_RIGHT_RAIL_ID} className={styles.rightRail} />
</div>
<div className={styles.switcherContainer}>
<BreakpointSwitcher value={breakpoint} onChange={setBreakpoint} />
</div>
</GustoProvider>
)
}

export const DESIGN_RIGHT_RAIL_ID = 'design-right-rail'
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { Contractor } from '@gusto/embedded-api-v-2025-11-15/models/components/contractor'
import { AddressForm, type AddressFormValues } from '../../common/AddressForm'
import { AddressForm, type AddressFormValues } from '../../shared/AddressForm/AddressForm'
import { contractorName } from '../../shared/contractorName'
import { useBase } from '@/components/Base/useBase'
import { CONTRACTOR_TYPE } from '@/shared/constants'

interface ContractorAddressFormProps {
contractor: Contractor
Expand All @@ -16,6 +18,7 @@ export function ContractorAddressForm({
onSave,
}: ContractorAddressFormProps) {
const { baseSubmitHandler } = useBase()
const isBusiness = contractor.type === CONTRACTOR_TYPE.BUSINESS

const handleSubmit = async (data: AddressFormValues) => {
await baseSubmitHandler(data, async payload => {
Expand All @@ -26,7 +29,7 @@ export function ContractorAddressForm({
return (
<AddressForm
heading="Edit address"
description="Update the contractor\u2019s home address."
description={`Update ${contractorName(contractor)}’s ${isBusiness ? 'business' : 'home'} address.`}
defaultValues={{
street1: contractor.address?.street1 ?? '',
street2: contractor.address?.street2 ?? '',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { Contractor } from '@gusto/embedded-api-v-2025-11-15/models/components/contractor'
import { ContractorDetails } from './ContractorDetails'

export interface ContractorDetailsDemoProps {
contractor: Contractor
/** When true, render the Edit button with a no-op handler. */
editable?: boolean
}

/**
* Renders ContractorDetails for state demos. The component is purely
* presentational and doesn't use `useBase`, so no BaseComponent wrapper
* is needed.
*/
export function ContractorDetailsDemo({ contractor, editable }: ContractorDetailsDemoProps) {
return <ContractorDetails contractor={contractor} onEdit={editable ? () => {} : undefined} />
}
Loading
Loading