From d880643fca1f987f465dcc52a83e91041828de8e Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 4 Sep 2025 00:12:10 -0700 Subject: [PATCH] Fix TypeScript errors for production build - Fixed nullable searchParams with optional chaining and fallbacks - Fixed recency type handling (string from URL, number internally) - Removed duplicate Party/User interface definitions, use global types - Fixed error handling in API routes with proper type checking - Fixed props access in UI components (placeholder, content types) - Added missing required props to components - Fixed type mismatches with next-intl rich text interpolation --- app/[locale]/[username]/ProfilePageClient.tsx | 32 ++++--------------- app/[locale]/p/[party]/PartyPageClient.tsx | 9 +++++- app/[locale]/saved/SavedPageClient.tsx | 18 ++++------- app/[locale]/saved/page.tsx | 4 +-- app/[locale]/teams/TeamsPageClient.tsx | 20 ++++-------- app/api/auth/login/route.ts | 13 +++++--- app/api/auth/signup/route.ts | 21 ++++++------ app/components/UpdateToastClient.tsx | 2 +- components/character/CharacterUnit/index.tsx | 2 +- components/common/Editor/index.tsx | 2 +- components/common/Popover/index.tsx | 2 +- components/common/Select/index.tsx | 1 - components/common/Toast/index.tsx | 2 +- components/common/Tooltip/index.tsx | 2 +- components/extra/GuidebookUnit/index.tsx | 2 +- 15 files changed, 56 insertions(+), 76 deletions(-) diff --git a/app/[locale]/[username]/ProfilePageClient.tsx b/app/[locale]/[username]/ProfilePageClient.tsx index 49a38a41..4d20b314 100644 --- a/app/[locale]/[username]/ProfilePageClient.tsx +++ b/app/[locale]/[username]/ProfilePageClient.tsx @@ -24,25 +24,6 @@ interface Pagination { record_count: number; } -interface Party { - id: string; - shortcode: string; - name: string; - element: number; - // Add other properties as needed -} - -interface User { - id: string; - username: string; - avatar: { - picture: string; - element: string; - }; - gender: string; - // Add other properties as needed -} - interface Props { initialData: { user: User; @@ -74,7 +55,7 @@ const ProfilePageClient: React.FC = ({ const [fetching, setFetching] = useState(false) const [element, setElement] = useState(initialElement || 0) const [raid, setRaid] = useState(initialRaid || '') - const [recency, setRecency] = useState(initialRecency || '') + const [recency, setRecency] = useState(initialRecency ? parseInt(initialRecency, 10) : 0) // Initialize app state with raid groups useEffect(() => { @@ -85,7 +66,7 @@ const ProfilePageClient: React.FC = ({ // Update URL when filters change useEffect(() => { - const params = new URLSearchParams(searchParams.toString()) + const params = new URLSearchParams(searchParams?.toString() ?? '') // Update or remove parameters based on filter values if (element) { @@ -101,14 +82,14 @@ const ProfilePageClient: React.FC = ({ } if (recency) { - params.set('recency', recency) + params.set('recency', recency.toString()) } else { params.delete('recency') } // Only update URL if filters are changed const newQueryString = params.toString() - const currentQuery = searchParams.toString() + const currentQuery = searchParams?.toString() ?? '' if (newQueryString !== currentQuery) { router.push(`/${initialData.user.username}${newQueryString ? `?${newQueryString}` : ''}`) @@ -128,7 +109,7 @@ const ProfilePageClient: React.FC = ({ if (element) url.searchParams.set('element', element.toString()) if (raid) url.searchParams.set('raid_id', raid) - if (recency) url.searchParams.set('recency', recency) + if (recency) url.searchParams.set('recency', recency.toString()) const response = await fetch(url.toString(), { headers: { @@ -163,7 +144,7 @@ const ProfilePageClient: React.FC = ({ setElement(filters.element || 0) } if ('recency' in filters) { - setRecency(filters.recency || '') + setRecency(filters.recency || 0) } if ('raid' in filters) { setRaid(filters.raid || '') @@ -226,6 +207,7 @@ const ProfilePageClient: React.FC = ({ = ({ party, raidGroups }) => { handleTabChanged={handleTabChanged} pushHistory={pushHistory} /> - + {}} + updateCallback={async () => ({})} + /> ) } diff --git a/app/[locale]/saved/SavedPageClient.tsx b/app/[locale]/saved/SavedPageClient.tsx index a4b276f6..32117f8a 100644 --- a/app/[locale]/saved/SavedPageClient.tsx +++ b/app/[locale]/saved/SavedPageClient.tsx @@ -17,13 +17,6 @@ import { defaultFilterset } from '~/utils/defaultFilters' import { appState } from '~/utils/appState' // Types -interface Party { - id: string; - shortcode: string; - name: string; - element: number; - // Add other properties as needed -} interface Props { initialData: { @@ -52,7 +45,7 @@ const SavedPageClient: React.FC = ({ const [parties, setParties] = useState(initialData.teams) const [element, setElement] = useState(initialElement || 0) const [raid, setRaid] = useState(initialRaid || '') - const [recency, setRecency] = useState(initialRecency || '') + const [recency, setRecency] = useState(initialRecency ? parseInt(initialRecency, 10) : 0) const [fetching, setFetching] = useState(false) // Initialize app state with raid groups @@ -64,7 +57,7 @@ const SavedPageClient: React.FC = ({ // Update URL when filters change useEffect(() => { - const params = new URLSearchParams(searchParams.toString()) + const params = new URLSearchParams(searchParams?.toString() ?? '') // Update or remove parameters based on filter values if (element) { @@ -80,14 +73,14 @@ const SavedPageClient: React.FC = ({ } if (recency) { - params.set('recency', recency) + params.set('recency', recency.toString()) } else { params.delete('recency') } // Only update URL if filters are changed const newQueryString = params.toString() - const currentQuery = searchParams.toString() + const currentQuery = searchParams?.toString() ?? '' if (newQueryString !== currentQuery) { router.push(`/saved${newQueryString ? `?${newQueryString}` : ''}`) @@ -100,7 +93,7 @@ const SavedPageClient: React.FC = ({ setElement(filters.element || 0) } if ('recency' in filters) { - setRecency(filters.recency || '') + setRecency(filters.recency || 0) } if ('raid' in filters) { setRaid(filters.raid || '') @@ -180,6 +173,7 @@ const SavedPageClient: React.FC = ({ party.element === element) + filteredTeams = filteredTeams.filter((party: any) => party.element === element) } if (raid) { - filteredTeams = filteredTeams.filter(party => party.raid?.id === raid) + filteredTeams = filteredTeams.filter((party: any) => party.raid?.id === raid) } // Prepare data for client component diff --git a/app/[locale]/teams/TeamsPageClient.tsx b/app/[locale]/teams/TeamsPageClient.tsx index 8529f715..902e7036 100644 --- a/app/[locale]/teams/TeamsPageClient.tsx +++ b/app/[locale]/teams/TeamsPageClient.tsx @@ -23,14 +23,6 @@ import LoadingRep from '~/components/reps/LoadingRep' import ErrorSection from '~/components/ErrorSection' // Types -interface Party { - id: string; - shortcode: string; - name: string; - element: number; - // Add other properties as needed -} - interface Pagination { current_page: number; total_pages: number; @@ -69,7 +61,7 @@ const TeamsPageClient: React.FC = ({ const [fetching, setFetching] = useState(false) const [element, setElement] = useState(initialElement || 0) const [raid, setRaid] = useState(initialRaid || '') - const [recency, setRecency] = useState(initialRecency || '') + const [recency, setRecency] = useState(initialRecency ? parseInt(initialRecency, 10) : 0) const [advancedFilters, setAdvancedFilters] = useState({}) const { toggleFavorite } = useFavorites(parties, setParties) @@ -83,7 +75,7 @@ const TeamsPageClient: React.FC = ({ // Update URL when filters change useEffect(() => { - const params = new URLSearchParams(searchParams.toString()) + const params = new URLSearchParams(searchParams?.toString() ?? '') // Update or remove parameters based on filter values if (element) { @@ -99,14 +91,14 @@ const TeamsPageClient: React.FC = ({ } if (recency) { - params.set('recency', recency) + params.set('recency', recency.toString()) } else { params.delete('recency') } // Only update URL if filters are changed const newQueryString = params.toString() - const currentQuery = searchParams.toString() + const currentQuery = searchParams?.toString() ?? '' if (newQueryString !== currentQuery) { router.push(`/teams${newQueryString ? `?${newQueryString}` : ''}`) @@ -126,7 +118,7 @@ const TeamsPageClient: React.FC = ({ if (element) url.searchParams.set('element', element.toString()) if (raid) url.searchParams.set('raid', raid) - if (recency) url.searchParams.set('recency', recency) + if (recency) url.searchParams.set('recency', recency.toString()) const response = await fetch(url.toString()) const data = await response.json() @@ -150,7 +142,7 @@ const TeamsPageClient: React.FC = ({ setElement(filters.element || 0) } if ('recency' in filters) { - setRecency(filters.recency || '') + setRecency(filters.recency || 0) } if ('raid' in filters) { setRaid(filters.raid || '') diff --git a/app/api/auth/login/route.ts b/app/api/auth/login/route.ts index 8ad66868..5fde0cf1 100644 --- a/app/api/auth/login/route.ts +++ b/app/api/auth/login/route.ts @@ -84,11 +84,14 @@ export async function POST(request: NextRequest) { } // For authentication errors - if (error.response?.status === 401) { - return NextResponse.json( - { error: 'Invalid email or password' }, - { status: 401 } - ) + if (error && typeof error === 'object' && 'response' in error) { + const axiosError = error as any + if (axiosError.response?.status === 401) { + return NextResponse.json( + { error: 'Invalid email or password' }, + { status: 401 } + ) + } } console.error('Login error:', error) diff --git a/app/api/auth/signup/route.ts b/app/api/auth/signup/route.ts index bc1be741..4a40ba88 100644 --- a/app/api/auth/signup/route.ts +++ b/app/api/auth/signup/route.ts @@ -49,15 +49,18 @@ export async function POST(request: NextRequest) { } // Handle specific API errors - if (error.response?.data?.error) { - const apiError = error.response.data.error - - // Username or email already in use - if (apiError.includes('username') || apiError.includes('email')) { - return NextResponse.json( - { error: apiError }, - { status: 409 } // Conflict - ) + if (error && typeof error === 'object' && 'response' in error) { + const axiosError = error as any + if (axiosError.response?.data?.error) { + const apiError = axiosError.response.data.error + + // Username or email already in use + if (apiError.includes('username') || apiError.includes('email')) { + return NextResponse.json( + { error: apiError }, + { status: 409 } // Conflict + ) + } } } diff --git a/app/components/UpdateToastClient.tsx b/app/components/UpdateToastClient.tsx index a00189c2..1a706e80 100644 --- a/app/components/UpdateToastClient.tsx +++ b/app/components/UpdateToastClient.tsx @@ -54,7 +54,7 @@ export default function UpdateToastClient({ initialVersion }: UpdateToastClientP setUpdateToastOpen(false) } - const path = pathname.replaceAll('/', '') + const path = pathname?.replaceAll('/', '') || '' // Only render toast if we have valid version data with update_type if (!['about', 'updates', 'roadmap'].includes(path) && effectiveVersion && effectiveVersion.update_type) { diff --git a/components/character/CharacterUnit/index.tsx b/components/character/CharacterUnit/index.tsx index ddb87061..9c6f9a6d 100644 --- a/components/character/CharacterUnit/index.tsx +++ b/components/character/CharacterUnit/index.tsx @@ -270,7 +270,7 @@ const CharacterUnit = ({ message={ <> {t.rich('modals.characters.messages.remove', { - character: gridCharacter?.object.name[locale], + character: gridCharacter?.object.name[locale] || '', strong: (chunks) => {chunks} })} diff --git a/components/common/Editor/index.tsx b/components/common/Editor/index.tsx index 2a244752..3ffa0c24 100644 --- a/components/common/Editor/index.tsx +++ b/components/common/Editor/index.tsx @@ -86,7 +86,7 @@ const Editor = ({ renderLabel({ options, node }) { return `${node.attrs.id.name[locale] ?? node.attrs.id.granblue_en}` }, - suggestion: mentionSuggestionOptions, + suggestion: mentionSuggestionOptions as any, HTMLAttributes: { class: classNames({ [styles.mention]: true, diff --git a/components/common/Popover/index.tsx b/components/common/Popover/index.tsx index a121f162..4d84c5ee 100644 --- a/components/common/Popover/index.tsx +++ b/components/common/Popover/index.tsx @@ -77,7 +77,7 @@ const Popover = React.forwardRef(function Popover( [styles.empty]: true, })} > - {props.placeholder} + {props.trigger?.placeholder} ) diff --git a/components/common/Select/index.tsx b/components/common/Select/index.tsx index 122d294d..dca8fd82 100644 --- a/components/common/Select/index.tsx +++ b/components/common/Select/index.tsx @@ -97,7 +97,6 @@ const Select = React.forwardRef(function Select( {props.icon?.src && {props.icon.alt}} diff --git a/components/common/Toast/index.tsx b/components/common/Toast/index.tsx index 7ff95d40..64b29877 100644 --- a/components/common/Toast/index.tsx +++ b/components/common/Toast/index.tsx @@ -4,7 +4,7 @@ import classNames from 'classnames' import * as ToastPrimitive from '@radix-ui/react-toast' import styles from './index.module.scss' -interface Props extends ToastPrimitive.ToastProps { +interface Props extends Omit { altText: string className?: string title?: string diff --git a/components/common/Tooltip/index.tsx b/components/common/Tooltip/index.tsx index f3a51523..10a4d329 100644 --- a/components/common/Tooltip/index.tsx +++ b/components/common/Tooltip/index.tsx @@ -2,7 +2,7 @@ import React, { PropsWithChildren } from 'react' import * as TooltipPrimitive from '@radix-ui/react-tooltip' import styles from './index.module.scss' -interface Props extends TooltipPrimitive.TooltipContentProps { +interface Props extends Omit { content: React.ReactNode open?: boolean onOpenChange?: (open: boolean) => void diff --git a/components/extra/GuidebookUnit/index.tsx b/components/extra/GuidebookUnit/index.tsx index 06b10d84..5340892e 100644 --- a/components/extra/GuidebookUnit/index.tsx +++ b/components/extra/GuidebookUnit/index.tsx @@ -144,7 +144,7 @@ const GuidebookUnit = ({ message={ <> {t.rich('modals.guidebooks.messages.remove', { - guidebook: guidebook?.name[locale], + guidebook: guidebook?.name[locale] || '', strong: (chunks) => {chunks} })}