Fix updates page translation errors and data fetching

- Fix translation interpolation format from double to single curly braces for next-intl
- Create API route handlers for characters, weapons, summons, and raids
- Fix infinite recursion in ChangelogUnit by renaming fetch function to fetchItem
- Simplify fetch logic and add proper dependencies to useEffect
- Fix activeYear defaulting to 2024 instead of current year (2025)

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Justin Edmund 2025-09-02 22:36:40 -07:00
parent c1cc810a39
commit 04b2c0a6b2
8 changed files with 153 additions and 43 deletions

View file

@ -0,0 +1,29 @@
import { NextRequest, NextResponse } from 'next/server';
import { fetchFromApi } from '~/app/lib/api-utils';
// GET handler for fetching a single character
export async function GET(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const { id } = params;
if (!id) {
return NextResponse.json(
{ error: 'Character ID is required' },
{ status: 400 }
);
}
const data = await fetchFromApi(`/characters/${id}`);
return NextResponse.json(data);
} catch (error: any) {
console.error(`Error fetching character ${params.id}`, error);
return NextResponse.json(
{ error: error.message || 'Failed to fetch character' },
{ status: error.response?.status || 500 }
);
}
}

View file

@ -0,0 +1,29 @@
import { NextRequest, NextResponse } from 'next/server';
import { fetchFromApi } from '~/app/lib/api-utils';
// GET handler for fetching a single raid
export async function GET(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const { id } = params;
if (!id) {
return NextResponse.json(
{ error: 'Raid ID is required' },
{ status: 400 }
);
}
const data = await fetchFromApi(`/raids/${id}`);
return NextResponse.json(data);
} catch (error: any) {
console.error(`Error fetching raid ${params.id}`, error);
return NextResponse.json(
{ error: error.message || 'Failed to fetch raid' },
{ status: error.response?.status || 500 }
);
}
}

View file

@ -0,0 +1,29 @@
import { NextRequest, NextResponse } from 'next/server';
import { fetchFromApi } from '~/app/lib/api-utils';
// GET handler for fetching a single summon
export async function GET(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const { id } = params;
if (!id) {
return NextResponse.json(
{ error: 'Summon ID is required' },
{ status: 400 }
);
}
const data = await fetchFromApi(`/summons/${id}`);
return NextResponse.json(data);
} catch (error: any) {
console.error(`Error fetching summon ${params.id}`, error);
return NextResponse.json(
{ error: error.message || 'Failed to fetch summon' },
{ status: error.response?.status || 500 }
);
}
}

View file

@ -0,0 +1,29 @@
import { NextRequest, NextResponse } from 'next/server';
import { fetchFromApi } from '~/app/lib/api-utils';
// GET handler for fetching a single weapon
export async function GET(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const { id } = params;
if (!id) {
return NextResponse.json(
{ error: 'Weapon ID is required' },
{ status: 400 }
);
}
const data = await fetchFromApi(`/weapons/${id}`);
return NextResponse.json(data);
} catch (error: any) {
console.error(`Error fetching weapon ${params.id}`, error);
return NextResponse.json(
{ error: error.message || 'Failed to fetch weapon' },
{ status: error.response?.status || 500 }
);
}
}

View file

@ -1,7 +1,6 @@
'use client' 'use client'
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { getCookie } from 'cookies-next' import { getCookie } from 'cookies-next'
import api from '~utils/api'
import styles from './index.module.scss' import styles from './index.module.scss'
@ -28,49 +27,41 @@ const ChangelogUnit = ({ id, type, image }: Props) => {
// Hooks // Hooks
useEffect(() => { useEffect(() => {
fetch() fetchItem()
}, []) }, [id, type])
async function fetch() { async function fetchItem() {
switch (type) { try {
case 'character': let endpoint = ''
const character = await fetchCharacter()
setItem(character.data)
break
case 'weapon': switch (type) {
const weapon = await fetchWeapon() case 'character':
setItem(weapon.data) endpoint = `/api/characters/${id}`
break break
case 'weapon':
endpoint = `/api/weapons/${id}`
break
case 'summon':
endpoint = `/api/summons/${id}`
break
case 'raid':
endpoint = `/api/raids/${id}`
break
default:
return
}
case 'summon': const response = await fetch(endpoint)
const summon = await fetchSummon()
setItem(summon.data)
break
case 'raid': if (response.ok) {
const raid = await fetchRaid() const data = await response.json()
setItem(raid.data) setItem(data)
break }
} catch (error) {
console.error(`Error fetching ${type} ${id}:`, error)
} }
} }
async function fetchCharacter() {
return api.endpoints.characters.getOne({ id: id })
}
async function fetchWeapon() {
return api.endpoints.weapons.getOne({ id: id })
}
async function fetchSummon() {
return api.endpoints.summons.getOne({ id: id })
}
async function fetchRaid() {
return api.endpoints.raids.getOne({ id: id })
}
const imageUrl = () => { const imageUrl = () => {
let src = '' let src = ''

View file

@ -14,7 +14,8 @@ const UpdatesPage = () => {
const classes = classNames(styles.updates, 'PageContent') const classes = classNames(styles.updates, 'PageContent')
const [activeYear, setActiveYear] = useState(new Date().getFullYear()) // Default to most recent year with content (2024)
const [activeYear, setActiveYear] = useState(2024)
const getYearButtonClass = (year: number) => const getYearButtonClass = (year: number) =>
classNames({ classNames({
[styles.yearButton]: true, [styles.yearButton]: true,

View file

@ -1,4 +1,5 @@
{ {
"noUpdates": "No updates available for this year",
"labels": { "labels": {
"characters": "New characters", "characters": "New characters",
"weapons": "New weapons", "weapons": "New weapons",
@ -22,7 +23,7 @@
"updates": "Other updates" "updates": "Other updates"
}, },
"events": { "events": {
"date": "{{month}}/{{year}}", "date": "{month}/{year}",
"legfest": "Legend Festival", "legfest": "Legend Festival",
"flash": "Flash Gala", "flash": "Flash Gala",
"content": "Content Update", "content": "Content Update",

View file

@ -1,4 +1,5 @@
{ {
"noUpdates": "この年のアップデートはありません",
"labels": { "labels": {
"characters": "新キャラクター", "characters": "新キャラクター",
"weapons": "新武器", "weapons": "新武器",
@ -22,7 +23,7 @@
"updates": "その他の更新" "updates": "その他の更新"
}, },
"events": { "events": {
"date": "{{year}}年{{month}}月", "date": "{year}年{month}月",
"legfest": "レジェンドフェス", "legfest": "レジェンドフェス",
"flash": "グランデフェス", "flash": "グランデフェス",
"content": "アップデート", "content": "アップデート",