From 2184f1642c0605f5715ac0bb7f5c3010be842120 Mon Sep 17 00:00:00 2001 From: anibus Date: Sat, 15 Aug 2026 14:46:19 +0300 Subject: [PATCH] Moved to next js ssr --- .env | 7 +- .gitignore | 1 + app/layout.tsx | 78 + app/mod/[modId]/page.tsx | 33 + .../[raceId]/building/[buildingId]/page.tsx | 37 + app/mod/[modId]/race/[raceId]/page.tsx | 33 + .../race/[raceId]/unit/[unitId]/page.tsx | 35 + app/page.tsx | 21 + app/robots.ts | 12 + app/sitemap.ts | 78 + src/App.tsx => components/AppShell.tsx | 66 +- components/pages/BuildingPageClient.tsx | 16 + components/pages/ModPageClient.tsx | 16 + components/pages/ModsPageClient.tsx | 14 + components/pages/RacePageClient.tsx | 16 + components/pages/UnitPageClient.tsx | 16 + lib/api-server.ts | 49 + next-env.d.ts | 5 + next.config.mjs | 6 + package-lock.json | 17882 +--------------- package.json | 45 +- public/index.html | 71 - public/manifest.json | 4 +- src/Routes.tsx | 19 - src/classes/AbilityFull.tsx | 2 +- src/classes/DpsTable.tsx | 2 +- src/classes/WeaponFull.tsx | 2 +- src/context/ThemeContext.tsx | 11 +- src/core/api.js | 18 +- src/core/withrouter.js | 3 +- src/index.tsx | 13 - src/{pages => legacy-pages}/BuildingPage.tsx | 2 - src/{pages => legacy-pages}/ModPage.tsx | 1 - src/{pages => legacy-pages}/ModsPage.tsx | 60 +- src/{pages => legacy-pages}/RacePageFast.tsx | 4 +- src/{pages => legacy-pages}/UnitPage.tsx | 2 - tsconfig.json | 36 +- 37 files changed, 854 insertions(+), 17862 deletions(-) create mode 100644 app/layout.tsx create mode 100644 app/mod/[modId]/page.tsx create mode 100644 app/mod/[modId]/race/[raceId]/building/[buildingId]/page.tsx create mode 100644 app/mod/[modId]/race/[raceId]/page.tsx create mode 100644 app/mod/[modId]/race/[raceId]/unit/[unitId]/page.tsx create mode 100644 app/page.tsx create mode 100644 app/robots.ts create mode 100644 app/sitemap.ts rename src/App.tsx => components/AppShell.tsx (67%) create mode 100644 components/pages/BuildingPageClient.tsx create mode 100644 components/pages/ModPageClient.tsx create mode 100644 components/pages/ModsPageClient.tsx create mode 100644 components/pages/RacePageClient.tsx create mode 100644 components/pages/UnitPageClient.tsx create mode 100644 lib/api-server.ts create mode 100644 next-env.d.ts create mode 100644 next.config.mjs delete mode 100644 public/index.html delete mode 100644 src/Routes.tsx delete mode 100644 src/index.tsx rename src/{pages => legacy-pages}/BuildingPage.tsx (99%) rename src/{pages => legacy-pages}/ModPage.tsx (97%) rename src/{pages => legacy-pages}/ModsPage.tsx (86%) rename src/{pages => legacy-pages}/RacePageFast.tsx (99%) rename src/{pages => legacy-pages}/UnitPage.tsx (99%) diff --git a/.env b/.env index 7890f45..0fc4ce1 100644 --- a/.env +++ b/.env @@ -1,2 +1,5 @@ -#REACT_APP_HOST_URL=https://wiki-backend.dawn-of-war.pro -REACT_APP_HOST_URL=http://localhost:8082 \ No newline at end of file +NEXT_PUBLIC_HOST_URL=https://wiki-backend.dawn-of-war.pro +#NEXT_PUBLIC_HOST_URL=http://localhost:8082 + +# Домен сайта для sitemap/robots/canonical (замените на реальный) +NEXT_PUBLIC_SITE_URL=https://wiki.dawn-of-war.pro \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4d29575..484d815 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ # dependencies /node_modules +/.next /.pnp .pnp.js diff --git a/app/layout.tsx b/app/layout.tsx new file mode 100644 index 0000000..815738d --- /dev/null +++ b/app/layout.tsx @@ -0,0 +1,78 @@ +import type { Metadata, Viewport } from 'next'; +import '@/src/index.css'; +import '@/src/App.css'; +import '@/src/css/Unit.css'; +import '@/src/css/Building.css'; +import { ThemeProvider } from '@/src/context/ThemeContext'; +import AppShell from '@/components/AppShell'; + +const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://dow-wiki.example.com'; + +export const metadata: Metadata = { + metadataBase: new URL(siteUrl), + title: { + default: 'Dawn of War Wiki — юниты, расы и здания всех модов', + template: '%s — Dawn of War Wiki', + }, + description: + 'Dawn of War wiki. Unification mod wiki. Unification mod unit stats. Supported all popular mods.', + manifest: '/manifest.json', + applicationName: 'Dawn of War Wiki', + verification: { + google: 'Q4XMn2UDpL2xmK7iBfaWQpVN8EPy8oGxN-7HqSKapb0', + }, + openGraph: { + type: 'website', + siteName: 'Dawn of War Wiki', + title: 'Dawn of War Wiki — юниты, расы и здания всех модов', + description: + 'Dawn of War wiki. Unification mod wiki. Unification mod unit stats. Supported all popular mods.', + images: ['/logo512.png'], + }, + twitter: { + card: 'summary_large_image', + }, + robots: { + index: true, + follow: true, + }, +}; + +export const viewport: Viewport = { + themeColor: '#000000', + width: 'device-width', + initialScale: 1, +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + {/* Yandex.Metrika */} + - - -
- - - diff --git a/public/manifest.json b/public/manifest.json index 080d6c7..538dcb0 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,6 +1,6 @@ { - "short_name": "React App", - "name": "Create React App Sample", + "short_name": "DoW Wiki", + "name": "Dawn of War Wiki", "icons": [ { "src": "favicon.ico", diff --git a/src/Routes.tsx b/src/Routes.tsx deleted file mode 100644 index 4e60614..0000000 --- a/src/Routes.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import {Routes, Route} from "react-router-dom"; -import ModsPage from "./pages/ModsPage"; -import ModPage from "./pages/ModPage"; -import RacePageFast from "./pages/RacePageFast"; -import UnitPage from "./pages/UnitPage"; -import BuildingPage from "./pages/BuildingPage"; - -export const MyRoutes = () => { - - return( - - }/> - }/> - }/> - }/> - }/> - - ) -} \ No newline at end of file diff --git a/src/classes/AbilityFull.tsx b/src/classes/AbilityFull.tsx index 469ddb1..74207d9 100644 --- a/src/classes/AbilityFull.tsx +++ b/src/classes/AbilityFull.tsx @@ -235,7 +235,7 @@ export default function AbilityFull(props: { abilityId?: number, mod: IMod, race { label: , value: () => isAbilityApplyTo(ArmorTypeNames.BuildingHigh) }, ]; - const chunks = []; + const chunks: any[] = []; for (let i = 0; i < items.length; i += columnsPerRow) { chunks.push(items.slice(i, i + columnsPerRow)); } diff --git a/src/classes/DpsTable.tsx b/src/classes/DpsTable.tsx index cf35f63..d5768ca 100644 --- a/src/classes/DpsTable.tsx +++ b/src/classes/DpsTable.tsx @@ -120,7 +120,7 @@ export default function DpsTable(props: { mod: IMod, minDamageValue?: number, mi { label: , value: () => getMoraleDamage() }, ]; - const chunks = []; + const chunks: any[] = []; for (let i = 0; i < items.length; i += columnsPerRow) { chunks.push(items.slice(i, i + columnsPerRow)); } diff --git a/src/classes/WeaponFull.tsx b/src/classes/WeaponFull.tsx index 99e25a5..372be3e 100644 --- a/src/classes/WeaponFull.tsx +++ b/src/classes/WeaponFull.tsx @@ -277,7 +277,7 @@ export default function WeaponFull(props: {weaponId: number, isDefault: Boolean, { label: , value: () => getMoraleDamage() }, ]; - const chunks = []; + const chunks: any[] = []; for (let i = 0; i < items.length; i += columnsPerRow) { chunks.push(items.slice(i, i + columnsPerRow)); } diff --git a/src/context/ThemeContext.tsx b/src/context/ThemeContext.tsx index a75774a..7d76ccf 100644 --- a/src/context/ThemeContext.tsx +++ b/src/context/ThemeContext.tsx @@ -1,3 +1,4 @@ +'use client'; import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react'; import { ThemeProvider as MuiThemeProvider } from '@mui/material/styles'; import CssBaseline from '@mui/material/CssBaseline'; @@ -14,10 +15,14 @@ const ThemeContext = createContext(undefined); export const ThemeProvider = ({ children }: { children: ReactNode }) => { - const [mode, setMode] = useState(() => { + const [mode, setMode] = useState('dark'); + + useEffect(() => { const saved = localStorage.getItem('theme'); - return (saved === 'light' ? 'light' : 'dark') as ThemeMode; - }); + if (saved === 'light') { + setMode('light'); + } + }, []); useEffect(() => { localStorage.setItem('theme', mode); diff --git a/src/core/api.js b/src/core/api.js index 17b4afe..ff64d0e 100644 --- a/src/core/api.js +++ b/src/core/api.js @@ -2,15 +2,15 @@ import React from 'react'; import { useTheme } from '@mui/material/styles'; -export const UserUrl = process.env.REACT_APP_HOST_URL + '/api/v1/user'; -export const WeaponUrl = process.env.REACT_APP_HOST_URL + '/api/v1/weapon'; -export const AbilityUrl = process.env.REACT_APP_HOST_URL + '/api/v1/ability'; -export const ResearchUrl = process.env.REACT_APP_HOST_URL + '/api/v1/research'; -export const AvailableMods = process.env.REACT_APP_HOST_URL + '/api/v1/mods'; -export const AvailableRacesPart = process.env.REACT_APP_HOST_URL + '/api/v1/races'; -export const AvailableUnits = process.env.REACT_APP_HOST_URL + '/api/v1/units'; -export const AvailableBuildings = process.env.REACT_APP_HOST_URL + '/api/v1/buildings'; -export const IconUrl = process.env.REACT_APP_HOST_URL + '/api/v1/grapics/icon/'; +export const UserUrl = process.env.NEXT_PUBLIC_HOST_URL + '/api/v1/user'; +export const WeaponUrl = process.env.NEXT_PUBLIC_HOST_URL + '/api/v1/weapon'; +export const AbilityUrl = process.env.NEXT_PUBLIC_HOST_URL + '/api/v1/ability'; +export const ResearchUrl = process.env.NEXT_PUBLIC_HOST_URL + '/api/v1/research'; +export const AvailableMods = process.env.NEXT_PUBLIC_HOST_URL + '/api/v1/mods'; +export const AvailableRacesPart = process.env.NEXT_PUBLIC_HOST_URL + '/api/v1/races'; +export const AvailableUnits = process.env.NEXT_PUBLIC_HOST_URL + '/api/v1/units'; +export const AvailableBuildings = process.env.NEXT_PUBLIC_HOST_URL + '/api/v1/buildings'; +export const IconUrl = process.env.NEXT_PUBLIC_HOST_URL + '/api/v1/grapics/icon/'; export function withTheme(Component) { return function WithTheme(props) { diff --git a/src/core/withrouter.js b/src/core/withrouter.js index 3ec9fa1..3c3ab43 100644 --- a/src/core/withrouter.js +++ b/src/core/withrouter.js @@ -1,4 +1,5 @@ -import { useParams } from 'react-router-dom'; +'use client'; +import { useParams } from 'next/navigation'; export function withRouter(Children) { return (props) => { diff --git a/src/index.tsx b/src/index.tsx deleted file mode 100644 index dd5b8c5..0000000 --- a/src/index.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import './index.css'; -import App from './App'; - -const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); -root.render( - - - -); - - diff --git a/src/pages/BuildingPage.tsx b/src/legacy-pages/BuildingPage.tsx similarity index 99% rename from src/pages/BuildingPage.tsx rename to src/legacy-pages/BuildingPage.tsx index 40f23a9..79bc5fb 100644 --- a/src/pages/BuildingPage.tsx +++ b/src/legacy-pages/BuildingPage.tsx @@ -118,8 +118,6 @@ function Unit (unit: IUnitShort, modId: number, raceId: String, theme: Theme) { function Building(building: IBuilding, mod: IMod, theme: Theme) { - document.title = building.name + " — " + mod.name - const isDark = theme.palette.mode === 'dark'; let mapBuildingWeapons: Map> = new Map(); diff --git a/src/pages/ModPage.tsx b/src/legacy-pages/ModPage.tsx similarity index 97% rename from src/pages/ModPage.tsx rename to src/legacy-pages/ModPage.tsx index 524d9e7..36abb62 100644 --- a/src/pages/ModPage.tsx +++ b/src/legacy-pages/ModPage.tsx @@ -66,7 +66,6 @@ class ModPage extends React.Component { } if (this.state != null && this.state.mod != null) { - document.title = this.state.mod.name + " — ModWiki"; const isDark = this.props.theme.palette.mode === 'dark'; return ( diff --git a/src/pages/ModsPage.tsx b/src/legacy-pages/ModsPage.tsx similarity index 86% rename from src/pages/ModsPage.tsx rename to src/legacy-pages/ModsPage.tsx index 8037714..9083b85 100644 --- a/src/pages/ModsPage.tsx +++ b/src/legacy-pages/ModsPage.tsx @@ -1,7 +1,4 @@ -import {AvailableMods} from "../core/api"; import React from "react"; -import { NavLink } from "react-router-dom"; -import { IMod } from "../types/Imod"; import { Box, Container, @@ -16,30 +13,11 @@ import { useTheme, Paper, LinearProgress, + Link, } from "@mui/material"; import { styled } from '@mui/material/styles'; +import { IMod } from "../types/Imod"; -// Styled components -const PageHeader = styled(Paper)(({ theme }) => ({ - padding: theme.spacing(6, 4), - marginBottom: theme.spacing(6), - background: theme.palette.mode === 'dark' - ? 'linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%)' - : 'linear-gradient(135deg, #ffffff 0%, #f0f0f0 50%, #e0e0e0 100%)', - borderRadius: '16px', - position: 'relative', - overflow: 'hidden', - '&::before': { - content: '""', - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - background: 'radial-gradient(circle at 30% 50%, rgba(255, 255, 255, 0.05) 0%, #dee2e6 50%)', - pointerEvents: 'none', - }, -})); const ModCard = styled(Card)(({ theme }) => ({ height: '100%', @@ -75,7 +53,7 @@ const ModCard = styled(Card)(({ theme }) => ({ }, })); -const VersionLink = styled(NavLink)(({ theme }) => ({ +const VersionLink = styled(Link)(({ theme }) => ({ display: 'flex', alignItems: 'center', padding: theme.spacing(1.5, 2), @@ -214,7 +192,7 @@ function Mods({ mods }: ModsProps) { {sameMods.filter(m => !m.isBeta).map(mod => ( - + Version {mod.version} @@ -232,7 +210,7 @@ function Mods({ mods }: ModsProps) { Beta versions: {betaVersion && ( - + Version {betaVersion.version} (Beta) @@ -251,9 +229,7 @@ function Mods({ mods }: ModsProps) {