Migrate all remaining components to App Router navigation
- Add 'use client' directives to all remaining components
- Update imports from next/router to next/navigation
- Replace router.locale with getCookie('NEXT_LOCALE')
- Update LanguageSwitch to use router.refresh()
- Replace router.asPath with usePathname()
Components migrated:
- Common: SelectWithInput, Editor
- Layout: Header, Layout, MentionList, ElementToggle
- Misc: UpdateToast, SearchModal, RaidCombobox, FilterModal
- Misc: ChangelogUnit, HovercardHeader, LanguageSwitch
- Extra: GuidebookUnit, GuidebookResult
- Reps: GridRep, CharacterRep, WeaponRep, SummonRep
Added migration PRD documentation to track progress.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
d48bdf25b6
commit
c6b53eb538
20 changed files with 272 additions and 73 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
|
'use client'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { useRouter } from 'next/router'
|
import { getCookie } from 'cookies-next'
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
|
|
||||||
|
|
@ -12,10 +13,8 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
const ElementToggle = ({ currentElement, sendValue, ...props }: Props) => {
|
const ElementToggle = ({ currentElement, sendValue, ...props }: Props) => {
|
||||||
// Router and localization
|
// Localization
|
||||||
const router = useRouter()
|
const locale = (getCookie('NEXT_LOCALE') as string) || 'en'
|
||||||
const locale =
|
|
||||||
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
|
|
||||||
|
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
|
'use client'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { deleteCookie } from 'cookies-next'
|
import { deleteCookie, getCookie } from 'cookies-next'
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import clonedeep from 'lodash.clonedeep'
|
import clonedeep from 'lodash.clonedeep'
|
||||||
|
|
@ -37,10 +37,8 @@ const Header = () => {
|
||||||
// Localization
|
// Localization
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
|
|
||||||
// Router
|
// Locale
|
||||||
const router = useRouter()
|
const locale = (getCookie('NEXT_LOCALE') as string) || 'en'
|
||||||
const locale =
|
|
||||||
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
|
|
||||||
|
|
||||||
// State management
|
// State management
|
||||||
const [alertOpen, setAlertOpen] = useState(false)
|
const [alertOpen, setAlertOpen] = useState(false)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { useRouter } from 'next/router'
|
'use client'
|
||||||
|
import { getCookie } from 'cookies-next'
|
||||||
|
|
||||||
import UncapIndicator from '~components/uncap/UncapIndicator'
|
import UncapIndicator from '~components/uncap/UncapIndicator'
|
||||||
import WeaponLabelIcon from '~components/weapon/WeaponLabelIcon'
|
import WeaponLabelIcon from '~components/weapon/WeaponLabelIcon'
|
||||||
|
|
@ -28,9 +29,7 @@ const Proficiency = [
|
||||||
]
|
]
|
||||||
|
|
||||||
const HovercardHeader = ({ gridObject, object, type, ...props }: Props) => {
|
const HovercardHeader = ({ gridObject, object, type, ...props }: Props) => {
|
||||||
const router = useRouter()
|
const locale = (getCookie('NEXT_LOCALE') as string) || 'en'
|
||||||
const locale =
|
|
||||||
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
|
|
||||||
|
|
||||||
const overlay = () => {
|
const overlay = () => {
|
||||||
if (type === 'character') {
|
if (type === 'character') {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
'use client'
|
||||||
import React, { PropsWithChildren, useEffect, useState } from 'react'
|
import React, { PropsWithChildren, useEffect, useState } from 'react'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/navigation'
|
||||||
|
import { usePathname } from 'next/navigation'
|
||||||
import { setCookie } from 'cookies-next'
|
import { setCookie } from 'cookies-next'
|
||||||
import { retrieveLocaleCookies } from '~utils/retrieveCookies'
|
import { retrieveLocaleCookies } from '~utils/retrieveCookies'
|
||||||
import * as SwitchPrimitive from '@radix-ui/react-switch'
|
import * as SwitchPrimitive from '@radix-ui/react-switch'
|
||||||
|
|
@ -14,6 +16,7 @@ export const LanguageSwitch = React.forwardRef<HTMLButtonElement, Props>(
|
||||||
) {
|
) {
|
||||||
// Router and locale data
|
// Router and locale data
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const pathname = usePathname()
|
||||||
const localeData = retrieveLocaleCookies()
|
const localeData = retrieveLocaleCookies()
|
||||||
|
|
||||||
// State
|
// State
|
||||||
|
|
@ -30,7 +33,7 @@ export const LanguageSwitch = React.forwardRef<HTMLButtonElement, Props>(
|
||||||
expiresAt.setDate(expiresAt.getDate() + 120)
|
expiresAt.setDate(expiresAt.getDate() + 120)
|
||||||
|
|
||||||
setCookie('NEXT_LOCALE', language, { path: '/', expires: expiresAt })
|
setCookie('NEXT_LOCALE', language, { path: '/', expires: expiresAt })
|
||||||
router.push(router.asPath, undefined, { locale: language })
|
router.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
|
'use client'
|
||||||
import { PropsWithChildren, useEffect, useState } from 'react'
|
import { PropsWithChildren, useEffect, useState } from 'react'
|
||||||
import { useRouter } from 'next/router'
|
import { usePathname } from 'next/navigation'
|
||||||
import { add, format } from 'date-fns'
|
import { add, format } from 'date-fns'
|
||||||
import { getCookie } from 'cookies-next'
|
import { getCookie } from 'cookies-next'
|
||||||
|
|
||||||
|
|
@ -11,7 +12,7 @@ import UpdateToast from '~components/toasts/UpdateToast'
|
||||||
interface Props {}
|
interface Props {}
|
||||||
|
|
||||||
const Layout = ({ children }: PropsWithChildren<Props>) => {
|
const Layout = ({ children }: PropsWithChildren<Props>) => {
|
||||||
const router = useRouter()
|
const pathname = usePathname()
|
||||||
const [updateToastOpen, setUpdateToastOpen] = useState(false)
|
const [updateToastOpen, setUpdateToastOpen] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -48,7 +49,7 @@ const Layout = ({ children }: PropsWithChildren<Props>) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateToast = () => {
|
const updateToast = () => {
|
||||||
const path = router.asPath.replaceAll('/', '')
|
const path = pathname.replaceAll('/', '')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
!['about', 'updates', 'roadmap'].includes(path) &&
|
!['about', 'updates', 'roadmap'].includes(path) &&
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
'use client'
|
||||||
import React, {
|
import React, {
|
||||||
forwardRef,
|
forwardRef,
|
||||||
useEffect,
|
useEffect,
|
||||||
|
|
@ -5,7 +6,7 @@ import React, {
|
||||||
useState,
|
useState,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
import { useRouter } from 'next/router'
|
import { getCookie } from 'cookies-next'
|
||||||
import { SuggestionProps } from '@tiptap/suggestion'
|
import { SuggestionProps } from '@tiptap/suggestion'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
|
|
||||||
|
|
@ -34,8 +35,7 @@ interface MentionProps extends SuggestionProps {
|
||||||
|
|
||||||
export const MentionList = forwardRef<MentionRef, Props>(
|
export const MentionList = forwardRef<MentionRef, Props>(
|
||||||
({ items, ...props }: Props, forwardedRef) => {
|
({ items, ...props }: Props, forwardedRef) => {
|
||||||
const router = useRouter()
|
const locale = (getCookie('NEXT_LOCALE') as string) || 'en'
|
||||||
const locale = router.locale || 'en'
|
|
||||||
|
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { useRouter } from 'next/router'
|
'use client'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import { getCookie } from 'cookies-next'
|
||||||
import api from '~utils/api'
|
import api from '~utils/api'
|
||||||
|
|
||||||
import styles from './index.module.scss'
|
import styles from './index.module.scss'
|
||||||
|
|
@ -19,10 +20,8 @@ const defaultProps = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const ChangelogUnit = ({ id, type, image }: Props) => {
|
const ChangelogUnit = ({ id, type, image }: Props) => {
|
||||||
// Router
|
// Locale
|
||||||
const router = useRouter()
|
const locale = (getCookie('NEXT_LOCALE') as string) || 'en'
|
||||||
const locale =
|
|
||||||
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
|
|
||||||
|
|
||||||
// State
|
// State
|
||||||
const [item, setItem] = useState<Character | Weapon | Summon>()
|
const [item, setItem] = useState<Character | Weapon | Summon>()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
|
'use client'
|
||||||
import { ComponentProps, useCallback, useEffect } from 'react'
|
import { ComponentProps, useCallback, useEffect } from 'react'
|
||||||
import { useEditor, EditorContent } from '@tiptap/react'
|
import { useEditor, EditorContent } from '@tiptap/react'
|
||||||
import { useRouter } from 'next/router'
|
import { getCookie } from 'cookies-next'
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
|
|
||||||
import StarterKit from '@tiptap/starter-kit'
|
import StarterKit from '@tiptap/starter-kit'
|
||||||
|
|
@ -44,9 +45,8 @@ const Editor = ({
|
||||||
onUpdate,
|
onUpdate,
|
||||||
...props
|
...props
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
// Hooks: Router
|
// Hooks: Locale
|
||||||
const router = useRouter()
|
const locale = (getCookie('NEXT_LOCALE') as string) || 'en'
|
||||||
const locale = router.locale || 'en'
|
|
||||||
|
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
|
'use client'
|
||||||
// Core dependencies
|
// Core dependencies
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { useRouter } from 'next/router'
|
import { getCookie } from 'cookies-next'
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
|
|
||||||
|
|
@ -39,9 +40,7 @@ const SelectWithInput = ({
|
||||||
sendValidity,
|
sendValidity,
|
||||||
sendValues,
|
sendValues,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const router = useRouter()
|
const locale = (getCookie('NEXT_LOCALE') as string) || 'en'
|
||||||
const locale =
|
|
||||||
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
|
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
|
|
||||||
// UI state
|
// UI state
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
|
'use client'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useRouter } from 'next/router'
|
import { getCookie } from 'cookies-next'
|
||||||
|
|
||||||
import styles from './index.module.scss'
|
import styles from './index.module.scss'
|
||||||
|
|
||||||
|
|
@ -9,9 +10,7 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
const GuidebookResult = (props: Props) => {
|
const GuidebookResult = (props: Props) => {
|
||||||
const router = useRouter()
|
const locale = (getCookie('NEXT_LOCALE') as string) || 'en'
|
||||||
const locale =
|
|
||||||
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
|
|
||||||
|
|
||||||
const guidebook = props.data
|
const guidebook = props.data
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
|
'use client'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { useRouter } from 'next/router'
|
import { getCookie } from 'cookies-next'
|
||||||
import { Trans, useTranslation } from 'next-i18next'
|
import { Trans, useTranslation } from 'next-i18next'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
|
|
||||||
|
|
@ -36,9 +37,7 @@ const GuidebookUnit = ({
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
// Translations and locale
|
// Translations and locale
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
const router = useRouter()
|
const locale = (getCookie('NEXT_LOCALE') as string) || 'en'
|
||||||
const locale =
|
|
||||||
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
|
|
||||||
|
|
||||||
// State: UI
|
// State: UI
|
||||||
const [searchModalOpen, setSearchModalOpen] = useState(false)
|
const [searchModalOpen, setSearchModalOpen] = useState(false)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
|
'use client'
|
||||||
import React, { useEffect, useRef, useState } from 'react'
|
import React, { useEffect, useRef, useState } from 'react'
|
||||||
import { getCookie, setCookie } from 'cookies-next'
|
import { getCookie, setCookie } from 'cookies-next'
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import { Trans, useTranslation } from 'react-i18next'
|
import { Trans, useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
import { Dialog, DialogTrigger } from '~components/common/Dialog'
|
import { Dialog, DialogTrigger } from '~components/common/Dialog'
|
||||||
|
|
@ -33,9 +33,8 @@ const MAX_WEAPONS = 13
|
||||||
const MAX_SUMMONS = 8
|
const MAX_SUMMONS = 8
|
||||||
|
|
||||||
const FilterModal = (props: Props) => {
|
const FilterModal = (props: Props) => {
|
||||||
// Set up router
|
// Set up locale
|
||||||
const router = useRouter()
|
const locale = (getCookie('NEXT_LOCALE') as string) || 'en'
|
||||||
const locale = router.locale
|
|
||||||
|
|
||||||
// Set up translation
|
// Set up translation
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
|
'use client'
|
||||||
import { createRef, useCallback, useEffect, useState } from 'react'
|
import { createRef, useCallback, useEffect, useState } from 'react'
|
||||||
import { useRouter } from 'next/router'
|
import { getCookie } from 'cookies-next'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
|
|
||||||
|
|
@ -66,9 +67,8 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
const RaidCombobox = (props: Props) => {
|
const RaidCombobox = (props: Props) => {
|
||||||
// Set up router for locale
|
// Set up locale from cookie
|
||||||
const router = useRouter()
|
const locale = (getCookie('NEXT_LOCALE') as string) || 'en'
|
||||||
const locale = router.locale || 'en'
|
|
||||||
|
|
||||||
// Set up translations
|
// Set up translations
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
|
'use client'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { useRouter } from 'next/router'
|
import { getCookie } from 'cookies-next'
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
import 'fix-date'
|
import 'fix-date'
|
||||||
|
|
||||||
|
|
@ -17,10 +18,8 @@ const CHARACTERS_COUNT = 3
|
||||||
|
|
||||||
const CharacterRep = (props: Props) => {
|
const CharacterRep = (props: Props) => {
|
||||||
// Localization for alt tags
|
// Localization for alt tags
|
||||||
const router = useRouter()
|
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
const locale =
|
const locale = (getCookie('NEXT_LOCALE') as string) || 'en'
|
||||||
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
|
|
||||||
|
|
||||||
// Component state
|
// Component state
|
||||||
const [characters, setCharacters] = useState<GridArray<Character>>({})
|
const [characters, setCharacters] = useState<GridArray<Character>>({})
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
|
'use client'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import { useSnapshot } from 'valtio'
|
import { useSnapshot } from 'valtio'
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
|
import { getCookie } from 'cookies-next'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import 'fix-date'
|
import 'fix-date'
|
||||||
|
|
||||||
|
|
@ -31,10 +32,8 @@ const GridRep = ({ party, loading, onClick, onSave }: Props) => {
|
||||||
|
|
||||||
const { account } = useSnapshot(accountState)
|
const { account } = useSnapshot(accountState)
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
const locale =
|
const locale = (getCookie('NEXT_LOCALE') as string) || 'en'
|
||||||
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
|
|
||||||
|
|
||||||
const [visible, setVisible] = useState(false)
|
const [visible, setVisible] = useState(false)
|
||||||
const [currentView, setCurrentView] = useState<
|
const [currentView, setCurrentView] = useState<
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
|
'use client'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { useRouter } from 'next/router'
|
import { getCookie } from 'cookies-next'
|
||||||
|
|
||||||
import styles from './index.module.scss'
|
import styles from './index.module.scss'
|
||||||
|
|
||||||
|
|
@ -15,9 +16,7 @@ const SUMMONS_COUNT = 4
|
||||||
|
|
||||||
const SummonRep = (props: Props) => {
|
const SummonRep = (props: Props) => {
|
||||||
// Localization for alt tags
|
// Localization for alt tags
|
||||||
const router = useRouter()
|
const locale = (getCookie('NEXT_LOCALE') as string) || 'en'
|
||||||
const locale =
|
|
||||||
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
|
|
||||||
|
|
||||||
// Component state
|
// Component state
|
||||||
const [mainSummon, setMainSummon] = useState<GridSummon>()
|
const [mainSummon, setMainSummon] = useState<GridSummon>()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
|
'use client'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { useRouter } from 'next/router'
|
import { getCookie } from 'cookies-next'
|
||||||
|
|
||||||
import styles from './index.module.scss'
|
import styles from './index.module.scss'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
|
|
@ -15,9 +16,7 @@ const WEAPONS_COUNT = 9
|
||||||
|
|
||||||
const WeaponRep = (props: Props) => {
|
const WeaponRep = (props: Props) => {
|
||||||
// Localization for alt tags
|
// Localization for alt tags
|
||||||
const router = useRouter()
|
const locale = (getCookie('NEXT_LOCALE') as string) || 'en'
|
||||||
const locale =
|
|
||||||
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
|
|
||||||
|
|
||||||
// Component state
|
// Component state
|
||||||
const [mainhand, setMainhand] = useState<GridWeapon>()
|
const [mainhand, setMainhand] = useState<GridWeapon>()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
|
'use client'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { getCookie, setCookie } from 'cookies-next'
|
import { getCookie, setCookie } from 'cookies-next'
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import InfiniteScroll from 'react-infinite-scroll-component'
|
import InfiniteScroll from 'react-infinite-scroll-component'
|
||||||
import cloneDeep from 'lodash.clonedeep'
|
import cloneDeep from 'lodash.clonedeep'
|
||||||
|
|
@ -38,9 +38,8 @@ interface Props extends DialogProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const SearchModal = (props: Props) => {
|
const SearchModal = (props: Props) => {
|
||||||
// Set up router
|
// Set up locale from cookie
|
||||||
const router = useRouter()
|
const locale = (getCookie('NEXT_LOCALE') as string) || 'en'
|
||||||
const locale = router.locale
|
|
||||||
|
|
||||||
// Set up translation
|
// Set up translation
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
|
'use client'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import { setCookie } from 'cookies-next'
|
import { setCookie } from 'cookies-next'
|
||||||
import { add, format } from 'date-fns'
|
import { add, format } from 'date-fns'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
|
|
|
||||||
209
prd/app-router-migration.md
Normal file
209
prd/app-router-migration.md
Normal file
|
|
@ -0,0 +1,209 @@
|
||||||
|
# App Router Migration PRD
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
Complete migration from Next.js Pages Router to App Router by updating all components to use `next/navigation` instead of `next/router`.
|
||||||
|
|
||||||
|
## Migration Requirements
|
||||||
|
|
||||||
|
### Router API Changes
|
||||||
|
- `useRouter()` from `next/router` → `useRouter()` from `next/navigation`
|
||||||
|
- `router.query` → `useSearchParams()` hook
|
||||||
|
- `router.pathname` → `usePathname()` hook
|
||||||
|
- `router.asPath` → combination of `usePathname()` + `useSearchParams()`
|
||||||
|
- `router.push()` → stays the same
|
||||||
|
- `router.replace()` → stays the same
|
||||||
|
- `router.back()` → stays the same
|
||||||
|
- `router.reload()` → `router.refresh()`
|
||||||
|
- `router.prefetch()` → stays the same
|
||||||
|
- `router.beforePopState()` → not available in App Router (needs alternative)
|
||||||
|
- `router.events` → not available in App Router (needs alternative)
|
||||||
|
|
||||||
|
### Import Updates Required
|
||||||
|
All imports need to change from:
|
||||||
|
```tsx
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
```
|
||||||
|
To:
|
||||||
|
```tsx
|
||||||
|
import { useRouter, usePathname, useSearchParams } from 'next/navigation'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Files to Migrate
|
||||||
|
|
||||||
|
### App Directory Files (Already using next/navigation - 6 files)
|
||||||
|
These files already use `next/navigation` but should be verified:
|
||||||
|
- [ ] `/app/teams/TeamsPageClient.tsx`
|
||||||
|
- [ ] `/app/saved/SavedPageClient.tsx`
|
||||||
|
- [ ] `/app/p/[party]/PartyPageClient.tsx`
|
||||||
|
- [ ] `/app/new/NewPartyClient.tsx`
|
||||||
|
- [ ] `/app/components/Header.tsx`
|
||||||
|
- [ ] `/app/[username]/ProfilePageClient.tsx`
|
||||||
|
|
||||||
|
### Pages Directory Files (1 file)
|
||||||
|
- [ ] `/pages/about.tsx` - Needs migration to app router or update imports
|
||||||
|
|
||||||
|
### Component Files - Auth (3 files)
|
||||||
|
- [x] `/components/auth/LoginModal/index.tsx`
|
||||||
|
- [x] `/components/auth/SignupModal/index.tsx`
|
||||||
|
- [x] `/components/auth/AccountModal/index.tsx`
|
||||||
|
|
||||||
|
### Component Files - Character (5 files)
|
||||||
|
- [x] `/components/character/CharacterUnit/index.tsx`
|
||||||
|
- [x] `/components/character/CharacterModal/index.tsx`
|
||||||
|
- [x] `/components/character/CharacterHovercard/index.tsx`
|
||||||
|
- [x] `/components/character/CharacterConflictModal/index.tsx`
|
||||||
|
- [x] `/components/character/CharacterResult/index.tsx`
|
||||||
|
|
||||||
|
### Component Files - Weapon (7 files)
|
||||||
|
- [x] `/components/weapon/WeaponUnit/index.tsx`
|
||||||
|
- [x] `/components/weapon/WeaponModal/index.tsx`
|
||||||
|
- [x] `/components/weapon/WeaponKeySelect/index.tsx`
|
||||||
|
- [x] `/components/weapon/WeaponHovercard/index.tsx`
|
||||||
|
- [x] `/components/weapon/WeaponConflictModal/index.tsx`
|
||||||
|
- [x] `/components/weapon/WeaponResult/index.tsx`
|
||||||
|
- [x] `/components/weapon/WeaponLabelIcon/index.tsx`
|
||||||
|
|
||||||
|
### Component Files - Summon (3 files)
|
||||||
|
- [x] `/components/summon/SummonUnit/index.tsx`
|
||||||
|
- [x] `/components/summon/SummonHovercard/index.tsx`
|
||||||
|
- [x] `/components/summon/SummonResult/index.tsx`
|
||||||
|
|
||||||
|
### Component Files - Party (5 files)
|
||||||
|
- [x] `/components/party/PartyHeader/index.tsx`
|
||||||
|
- [x] `/components/party/PartyHead/index.tsx`
|
||||||
|
- [x] `/components/party/PartyFooter/index.tsx`
|
||||||
|
- [x] `/components/party/PartyDropdown/index.tsx`
|
||||||
|
- [x] `/components/party/Party/index.tsx`
|
||||||
|
|
||||||
|
### Component Files - Job (7 files)
|
||||||
|
- [x] `/components/job/JobSkillItem/index.tsx`
|
||||||
|
- [x] `/components/job/JobSection/index.tsx`
|
||||||
|
- [x] `/components/job/JobDropdown/index.tsx`
|
||||||
|
- [x] `/components/job/JobAccessoryPopover/index.tsx`
|
||||||
|
- [x] `/components/job/JobAccessoryItem/index.tsx`
|
||||||
|
- [x] `/components/job/JobSkillResult/index.tsx`
|
||||||
|
- [x] `/components/job/JobImage/index.tsx`
|
||||||
|
|
||||||
|
### Component Files - Mastery (3 files)
|
||||||
|
- [x] `/components/mastery/ExtendedMasterySelect/index.tsx`
|
||||||
|
- [x] `/components/mastery/AxSelect/index.tsx`
|
||||||
|
- [x] `/components/mastery/AwakeningSelectWithInput/index.tsx`
|
||||||
|
|
||||||
|
### Component Files - Reps (4 files)
|
||||||
|
- [x] `/components/reps/GridRep/index.tsx`
|
||||||
|
- [x] `/components/reps/CharacterRep/index.tsx`
|
||||||
|
- [x] `/components/reps/WeaponRep/index.tsx`
|
||||||
|
- [x] `/components/reps/SummonRep/index.tsx`
|
||||||
|
|
||||||
|
### Component Files - Common (2 files)
|
||||||
|
- [x] `/components/common/SelectWithInput/index.tsx`
|
||||||
|
- [x] `/components/common/Editor/index.tsx`
|
||||||
|
|
||||||
|
### Component Files - Extra (2 files)
|
||||||
|
- [x] `/components/extra/GuidebookUnit/index.tsx`
|
||||||
|
- [x] `/components/extra/GuidebookResult/index.tsx`
|
||||||
|
|
||||||
|
### Component Files - Other (11 files)
|
||||||
|
- [x] `/components/toasts/UpdateToast/index.tsx`
|
||||||
|
- [x] `/components/search/SearchModal/index.tsx`
|
||||||
|
- [x] `/components/raids/RaidCombobox/index.tsx`
|
||||||
|
- [x] `/components/filters/FilterModal/index.tsx`
|
||||||
|
- [x] `/components/MentionList/index.tsx`
|
||||||
|
- [x] `/components/Layout/index.tsx`
|
||||||
|
- [x] `/components/LanguageSwitch/index.tsx`
|
||||||
|
- [x] `/components/Header/index.tsx`
|
||||||
|
- [x] `/components/ElementToggle/index.tsx`
|
||||||
|
- [x] `/components/about/ChangelogUnit/index.tsx`
|
||||||
|
- [x] `/components/HovercardHeader/index.tsx`
|
||||||
|
|
||||||
|
### Utility Files (1 file)
|
||||||
|
- [x] `/utils/changeLanguage.tsx`
|
||||||
|
|
||||||
|
## Total Files to Migrate: 59
|
||||||
|
|
||||||
|
## Migration Steps for Each File
|
||||||
|
|
||||||
|
1. **Analyze current usage**
|
||||||
|
- Check how `router` is being used
|
||||||
|
- Identify which properties/methods are accessed
|
||||||
|
- Note any router.events listeners
|
||||||
|
|
||||||
|
2. **Update imports**
|
||||||
|
- Change from `next/router` to `next/navigation`
|
||||||
|
- Add additional hooks if needed (`usePathname`, `useSearchParams`)
|
||||||
|
|
||||||
|
3. **Update router usage**
|
||||||
|
- Replace `router.query` with `useSearchParams()`
|
||||||
|
- Replace `router.pathname` with `usePathname()`
|
||||||
|
- Update any other router property access
|
||||||
|
|
||||||
|
4. **Handle edge cases**
|
||||||
|
- Router events (need alternative approach)
|
||||||
|
- beforePopState (need alternative approach)
|
||||||
|
- Dynamic route params (use `useParams()`)
|
||||||
|
|
||||||
|
5. **Test the component**
|
||||||
|
- Ensure navigation still works
|
||||||
|
- Check query parameter handling
|
||||||
|
- Verify dynamic routes
|
||||||
|
|
||||||
|
## Special Considerations
|
||||||
|
|
||||||
|
### Router Events
|
||||||
|
Files using `router.events` will need special handling as this is not available in App Router. Common patterns:
|
||||||
|
- Route change start/complete events → Use loading.tsx or Suspense
|
||||||
|
- Route change error → Use error boundaries
|
||||||
|
|
||||||
|
### Query Parameters
|
||||||
|
When migrating `router.query`:
|
||||||
|
```tsx
|
||||||
|
// Old (Pages Router)
|
||||||
|
const { id, filter } = router.query
|
||||||
|
|
||||||
|
// New (App Router)
|
||||||
|
const searchParams = useSearchParams()
|
||||||
|
const id = searchParams.get('id')
|
||||||
|
const filter = searchParams.get('filter')
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dynamic Route Parameters
|
||||||
|
For dynamic segments like `[id]`:
|
||||||
|
```tsx
|
||||||
|
// Old (Pages Router)
|
||||||
|
const { id } = router.query
|
||||||
|
|
||||||
|
// New (App Router)
|
||||||
|
import { useParams } from 'next/navigation'
|
||||||
|
const params = useParams()
|
||||||
|
const id = params.id
|
||||||
|
```
|
||||||
|
|
||||||
|
### Programmatic Navigation
|
||||||
|
```tsx
|
||||||
|
// Both routers (mostly the same)
|
||||||
|
router.push('/path')
|
||||||
|
router.replace('/path')
|
||||||
|
router.back()
|
||||||
|
|
||||||
|
// Refresh (different)
|
||||||
|
// Old: router.reload()
|
||||||
|
// New: router.refresh()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Post-Migration Cleanup
|
||||||
|
|
||||||
|
After all files are migrated:
|
||||||
|
1. [ ] Remove `/pages` directory (except `/pages/api` if still needed)
|
||||||
|
2. [ ] Remove Pages Router specific configuration
|
||||||
|
3. [ ] Update `_app.tsx` dependencies if any remain
|
||||||
|
4. [ ] Test all routes thoroughly
|
||||||
|
5. [ ] Update any documentation
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
- [x] All components use `next/navigation` imports
|
||||||
|
- [x] No references to `next/router` remain in components (only in pages/about.tsx which still needs migration)
|
||||||
|
- [x] All navigation functionality works as before
|
||||||
|
- [x] No console errors about "NextRouter was not mounted"
|
||||||
|
- [x] App runs successfully with `npm run dev`
|
||||||
|
- [ ] Build completes with `npm run build` (to be tested)
|
||||||
Loading…
Reference in a new issue