## 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>
155 lines
4.1 KiB
TypeScript
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
|