hensei-web/components/common/Select/index.tsx
Justin Edmund e4b7f0c356
Fix TypeScript errors for production build (#436)
## Summary
Fixed multiple TypeScript errors that were preventing the production
build from completing on Railway.

## Changes Made

### Nullable Type Fixes
- Fixed `searchParams.toString()` calls with optional chaining (`?.`)
and fallback values
- Fixed `pathname` nullable access in UpdateToastClient
- Added fallbacks for undefined values in translation interpolations

### Type Consistency Fixes
- Fixed recency parameter handling (string from URL, converted to number
internally)
- Removed duplicate local interface definitions for Party and User types
- Fixed Party type mismatches by using global type definitions

### API Route Error Handling
- Fixed error type checking in catch blocks for login/signup routes
- Added proper type guards for axios error objects

### Component Props Fixes
- Fixed RadixSelect.Trigger by removing invalid placeholder prop
- Fixed Toast and Tooltip components by using Omit to exclude
conflicting content type
- Added missing onAdvancedFilter prop to FilterBar components
- Fixed PartyFooter props with required parameters

## Test Plan
- [x] Fixed all TypeScript compilation errors locally
- [ ] Production build should complete successfully on Railway
- [ ] All affected components should function correctly

🤖 Generated with [Claude Code](https://claude.ai/code)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-04 01:47:10 -07:00

155 lines
4.1 KiB
TypeScript

import React, { useEffect, useState } from 'react'
import * as RadixSelect from '@radix-ui/react-select'
import classNames from 'classnames'
import Overlay from '~components/common/Overlay'
import ChevronIcon from '~public/icons/Chevron.svg'
import styles from './index.module.scss'
// Props
interface Props
extends React.DetailedHTMLProps<
React.SelectHTMLAttributes<HTMLSelectElement>,
HTMLSelectElement
> {
open: boolean
icon?: {
src: string
alt: string
}
trigger?: {
bound?: boolean
className?: string
placeholder?: string
size?: 'small' | 'medium' | 'large'
}
children?: React.ReactNode
onOpenChange?: () => void
onValueChange?: (value: string) => void
onClose?: () => void
overlayVisible?: boolean
}
const Select = React.forwardRef<HTMLButtonElement, Props>(function Select(
props: Props,
forwardedRef
) {
const [open, setOpen] = useState(false)
const [value, setValue] = useState('')
const triggerClasses = classNames(
{
[styles.trigger]: true,
[styles.disabled]: props.disabled,
[styles.bound]: props.trigger ? props.trigger.bound : false,
[styles.small]: props.trigger?.size === 'small',
[styles.medium]: !props.trigger || props.trigger?.size === 'medium',
[styles.large]: props.trigger?.size === 'large',
},
props.trigger?.className?.split(' ').map((className) => styles[className])
)
const selectClasses = classNames(
{
[styles.select]: true,
[styles.bound]: props.trigger ? props.trigger.bound : false,
},
props.className?.split(' ').map((className) => styles[className])
)
useEffect(() => {
setOpen(props.open)
}, [props.open])
useEffect(() => {
if (props.value && props.value !== '') setValue(`${props.value}`)
else setValue('')
}, [props.value])
function onValueChange(newValue: string) {
setValue(`${newValue}`)
if (props.onValueChange) props.onValueChange(newValue)
}
function onCloseAutoFocus() {
setOpen(false)
if (props.onClose) props.onClose()
}
function onEscapeKeyDown() {
setOpen(false)
if (props.onClose) props.onClose()
}
function onPointerDownOutside() {
setOpen(false)
if (props.onClose) props.onClose()
}
return (
<RadixSelect.Root
open={open}
value={value !== '' ? value : undefined}
onValueChange={onValueChange}
onOpenChange={props.onOpenChange}
>
<RadixSelect.Trigger
autoFocus={props.autoFocus || false}
className={triggerClasses}
ref={forwardedRef}
>
{props.icon?.src && <img alt={props.icon.alt} src={props.icon.src} />}
<RadixSelect.Value placeholder={props.trigger?.placeholder} />
{!props.disabled && (
<RadixSelect.Icon className={styles.icon}>
<ChevronIcon />
</RadixSelect.Icon>
)}
</RadixSelect.Trigger>
<RadixSelect.Portal>
<>
<Overlay
open={open}
visible={props.overlayVisible != null ? props.overlayVisible : true}
/>
<RadixSelect.Content
align="center"
className={selectClasses}
position="popper"
sideOffset={6}
onCloseAutoFocus={onCloseAutoFocus}
onEscapeKeyDown={onEscapeKeyDown}
onPointerDownOutside={onPointerDownOutside}
>
<RadixSelect.ScrollUpButton
className={classNames({
[styles.scroll]: true,
[styles.up]: true,
})}
>
<ChevronIcon />
</RadixSelect.ScrollUpButton>
<RadixSelect.Viewport>{props.children}</RadixSelect.Viewport>
<RadixSelect.ScrollDownButton
className={classNames({
[styles.scroll]: true,
[styles.down]: true,
})}
>
<ChevronIcon />
</RadixSelect.ScrollDownButton>
</RadixSelect.Content>
</>
</RadixSelect.Portal>
</RadixSelect.Root>
)
})
Select.defaultProps = {
overlayVisible: true,
}
export default Select