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:
parent
c1cc810a39
commit
04b2c0a6b2
8 changed files with 153 additions and 43 deletions
29
app/api/characters/[id]/route.ts
Normal file
29
app/api/characters/[id]/route.ts
Normal 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 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
29
app/api/raids/[id]/route.ts
Normal file
29
app/api/raids/[id]/route.ts
Normal 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 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
29
app/api/summons/[id]/route.ts
Normal file
29
app/api/summons/[id]/route.ts
Normal 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 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
29
app/api/weapons/[id]/route.ts
Normal file
29
app/api/weapons/[id]/route.ts
Normal 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 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
switch (type) {
|
||||||
break
|
case 'character':
|
||||||
|
endpoint = `/api/characters/${id}`
|
||||||
case 'weapon':
|
break
|
||||||
const weapon = await fetchWeapon()
|
case 'weapon':
|
||||||
setItem(weapon.data)
|
endpoint = `/api/weapons/${id}`
|
||||||
break
|
break
|
||||||
|
case 'summon':
|
||||||
case 'summon':
|
endpoint = `/api/summons/${id}`
|
||||||
const summon = await fetchSummon()
|
break
|
||||||
setItem(summon.data)
|
case 'raid':
|
||||||
break
|
endpoint = `/api/raids/${id}`
|
||||||
|
break
|
||||||
case 'raid':
|
default:
|
||||||
const raid = await fetchRaid()
|
return
|
||||||
setItem(raid.data)
|
}
|
||||||
break
|
|
||||||
|
const response = await fetch(endpoint)
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json()
|
||||||
|
setItem(data)
|
||||||
|
}
|
||||||
|
} 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 = ''
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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": "アップデート",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue