format: file width decreased from 120 to 80
This commit is contained in:
+1
-1
@@ -3,5 +3,5 @@
|
|||||||
"semi": false,
|
"semi": false,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"trailingComma": "es5",
|
"trailingComma": "es5",
|
||||||
"printWidth": 120
|
"printWidth": 80
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,8 @@ export async function redirectRequest(
|
|||||||
|
|
||||||
// Replace '/api/proxy/parserBackend' with backend URL, preserve everything after
|
// Replace '/api/proxy/parserBackend' with backend URL, preserve everything after
|
||||||
// e.g. '/api/proxy/extra/path?id=123' -> 'http://backend-server/extra/path?id=123'
|
// e.g. '/api/proxy/extra/path?id=123' -> 'http://backend-server/extra/path?id=123'
|
||||||
const backendUrl = backendBaseUrl + reqUrl.substring(proxyIndex + proxyUrl.length)
|
const backendUrl =
|
||||||
|
backendBaseUrl + reqUrl.substring(proxyIndex + proxyUrl.length)
|
||||||
// console.log('redirecting', req.url, 'to', backendUrl)
|
// console.log('redirecting', req.url, 'to', backendUrl)
|
||||||
|
|
||||||
const backendRes = await fetch(backendUrl, {
|
const backendRes = await fetch(backendUrl, {
|
||||||
@@ -33,21 +34,26 @@ export async function redirectRequest(
|
|||||||
status: backendRes.status,
|
status: backendRes.status,
|
||||||
headers: {
|
headers: {
|
||||||
'Access-Control-Allow-Origin': '*',
|
'Access-Control-Allow-Origin': '*',
|
||||||
'Content-Type': backendRes.headers.get('content-type') || 'application/octet-stream',
|
'Content-Type':
|
||||||
|
backendRes.headers.get('content-type') ||
|
||||||
|
'application/octet-stream',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Browsers send OPTIONS request sometimes to check webserver's allowed headers and methods.
|
/// Browsers send OPTIONS request sometimes to check webserver's allowed headers and methods.
|
||||||
/// My C# backend doesn't support OPTIONS, so the proxy answer to OPTIONS request that all headers and methods are allowed.
|
/// My C# backend doesn't support OPTIONS, so the proxy answer to OPTIONS request that all headers and methods are allowed.
|
||||||
export async function responseToOPTIONS(req: NextRequest): Promise<NextResponse> {
|
export async function responseToOPTIONS(
|
||||||
|
req: NextRequest
|
||||||
|
): Promise<NextResponse> {
|
||||||
// console.log('proxy responding to CORS OPTIONS', req.url)
|
// console.log('proxy responding to CORS OPTIONS', req.url)
|
||||||
|
|
||||||
return new NextResponse(null, {
|
return new NextResponse(null, {
|
||||||
status: 200,
|
status: 200,
|
||||||
headers: {
|
headers: {
|
||||||
'Access-Control-Allow-Origin': '*',
|
'Access-Control-Allow-Origin': '*',
|
||||||
'Access-Control-Allow-Methods': 'GET, POST, PUT, PATCH, DELETE, HEAD',
|
'Access-Control-Allow-Methods':
|
||||||
|
'GET, POST, PUT, PATCH, DELETE, HEAD',
|
||||||
'Access-Control-Allow-Headers': '*',
|
'Access-Control-Allow-Headers': '*',
|
||||||
// req.headers.get('access-control-request-headers') || 'Content-Type, Authorization'
|
// req.headers.get('access-control-request-headers') || 'Content-Type, Authorization'
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
import type { NextRequest } from 'next/server'
|
import type { NextRequest } from 'next/server'
|
||||||
import { NextResponse } from 'next/server'
|
import { NextResponse } from 'next/server'
|
||||||
import { redirectRequest, responseToOPTIONS as respondToOPTIONS } from '@/app/api/proxy/functions'
|
import {
|
||||||
|
redirectRequest,
|
||||||
|
responseToOPTIONS as respondToOPTIONS,
|
||||||
|
} from '@/app/api/proxy/functions'
|
||||||
import { PROXY_PATHS } from '@/app/api/proxy/paths'
|
import { PROXY_PATHS } from '@/app/api/proxy/paths'
|
||||||
import { appConfig } from '@/lib/configLoader'
|
import { appConfig } from '@/lib/configLoader'
|
||||||
|
|
||||||
// Forwards the incoming request to the save parser backend.
|
// Forwards the incoming request to the save parser backend.
|
||||||
async function redirect(req: NextRequest): Promise<NextResponse> {
|
async function redirect(req: NextRequest): Promise<NextResponse> {
|
||||||
return await redirectRequest(req, PROXY_PATHS.saveParserBackend, appConfig.services.saveParserBackend.url)
|
return await redirectRequest(
|
||||||
|
req,
|
||||||
|
PROXY_PATHS.saveParserBackend,
|
||||||
|
appConfig.services.saveParserBackend.url
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// handlers for each request method on this route
|
// handlers for each request method on this route
|
||||||
|
|||||||
+25
-7
@@ -1,7 +1,11 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { AgGridReact } from 'ag-grid-react'
|
import { AgGridReact } from 'ag-grid-react'
|
||||||
import { AllCommunityModule, themeQuartz, colorSchemeDark } from 'ag-grid-community'
|
import {
|
||||||
|
AllCommunityModule,
|
||||||
|
themeQuartz,
|
||||||
|
colorSchemeDark,
|
||||||
|
} from 'ag-grid-community'
|
||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
|
|
||||||
// TODO: create tabs with general info, tables, plots
|
// TODO: create tabs with general info, tables, plots
|
||||||
@@ -12,16 +16,26 @@ export default function BrowseSaveDataPage() {
|
|||||||
|
|
||||||
// Keep the grid theme in sync with the browser's light/dark color scheme.
|
// Keep the grid theme in sync with the browser's light/dark color scheme.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
|
const prefersDark = window.matchMedia(
|
||||||
setAgTheme(prefersDark ? themeQuartz.withPart(colorSchemeDark) : themeQuartz)
|
'(prefers-color-scheme: dark)'
|
||||||
|
).matches
|
||||||
|
setAgTheme(
|
||||||
|
prefersDark ? themeQuartz.withPart(colorSchemeDark) : themeQuartz
|
||||||
|
)
|
||||||
|
|
||||||
const listener = (e: MediaQueryListEvent) => {
|
const listener = (e: MediaQueryListEvent) => {
|
||||||
setAgTheme(e.matches ? themeQuartz.withPart(colorSchemeDark) : themeQuartz)
|
setAgTheme(
|
||||||
|
e.matches ? themeQuartz.withPart(colorSchemeDark) : themeQuartz
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', listener)
|
window
|
||||||
|
.matchMedia('(prefers-color-scheme: dark)')
|
||||||
|
.addEventListener('change', listener)
|
||||||
return () => {
|
return () => {
|
||||||
window.matchMedia('(prefers-color-scheme: dark)').removeEventListener('change', listener)
|
window
|
||||||
|
.matchMedia('(prefers-color-scheme: dark)')
|
||||||
|
.removeEventListener('change', listener)
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
@@ -35,7 +49,11 @@ export default function BrowseSaveDataPage() {
|
|||||||
{ id: 2, col1: 'Dark Mode', col2: 'Support' },
|
{ id: 2, col1: 'Dark Mode', col2: 'Support' },
|
||||||
]}
|
]}
|
||||||
columnDefs={[
|
columnDefs={[
|
||||||
{ field: 'col1', headerName: 'Column 1', filter: 'agTextColumnFilter' },
|
{
|
||||||
|
field: 'col1',
|
||||||
|
headerName: 'Column 1',
|
||||||
|
filter: 'agTextColumnFilter',
|
||||||
|
},
|
||||||
{ field: 'col2', headerName: 'Column 2' },
|
{ field: 'col2', headerName: 'Column 2' },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+3
-1
@@ -9,7 +9,9 @@ export const metadata: Metadata = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Root layout wrapping every page with the navbar and shared styles.
|
// Root layout wrapping every page with the navbar and shared styles.
|
||||||
export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: Readonly<{ children: React.ReactNode }>) {
|
||||||
// Register the ag-grid community features
|
// Register the ag-grid community features
|
||||||
ModuleRegistry.registerModules([AllCommunityModule])
|
ModuleRegistry.registerModules([AllCommunityModule])
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -30,7 +30,10 @@ export default function LoginPage() {
|
|||||||
setError(null)
|
setError(null)
|
||||||
try {
|
try {
|
||||||
console.log('Log in:', formData)
|
console.log('Log in:', formData)
|
||||||
const result = await authService.login(formData.email, formData.password)
|
const result = await authService.login(
|
||||||
|
formData.email,
|
||||||
|
formData.password
|
||||||
|
)
|
||||||
console.log('Logged in:', result)
|
console.log('Logged in:', result)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error(err.message)
|
console.error(err.message)
|
||||||
|
|||||||
@@ -36,7 +36,11 @@ export default function RegisterPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log('Register', formData)
|
console.log('Register', formData)
|
||||||
const result = await authService.register(formData.name, formData.email, formData.password)
|
const result = await authService.register(
|
||||||
|
formData.name,
|
||||||
|
formData.email,
|
||||||
|
formData.password
|
||||||
|
)
|
||||||
console.log('Registered:', result)
|
console.log('Registered:', result)
|
||||||
router.push('/login') // Redirect after success
|
router.push('/login') // Redirect after success
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import { font_Hack } from '@/lib/myFonts'
|
|||||||
// Top navigation bar with a home link and login/register buttons.
|
// Top navigation bar with a home link and login/register buttons.
|
||||||
export default function Navbar() {
|
export default function Navbar() {
|
||||||
return (
|
return (
|
||||||
<nav className={`navbar navbar-expand-lg navbar-dark bg-dark px-3 ${font_Hack.className}`}>
|
<nav
|
||||||
|
className={`navbar navbar-expand-lg navbar-dark bg-dark px-3 ${font_Hack.className}`}
|
||||||
|
>
|
||||||
<div className="container-fluid">
|
<div className="container-fluid">
|
||||||
{/* Left: Home Link */}
|
{/* Left: Home Link */}
|
||||||
<Link href="/" className="navbar-brand">
|
<Link href="/" className="navbar-brand">
|
||||||
@@ -15,10 +17,16 @@ export default function Navbar() {
|
|||||||
|
|
||||||
{/* Right: Login/Register Buttons */}
|
{/* Right: Login/Register Buttons */}
|
||||||
<div className="d-flex gap-2 ms-auto">
|
<div className="d-flex gap-2 ms-auto">
|
||||||
<Link href="/login" className="btn btn-outline-light rounded-pill px-4">
|
<Link
|
||||||
|
href="/login"
|
||||||
|
className="btn btn-outline-light rounded-pill px-4"
|
||||||
|
>
|
||||||
Log in
|
Log in
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="/register" className="btn btn-primary rounded-pill px-4">
|
<Link
|
||||||
|
href="/register"
|
||||||
|
className="btn btn-primary rounded-pill px-4"
|
||||||
|
>
|
||||||
Register
|
Register
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { saveParserBackendService, SaveStatusResponse, UploadSaveResponse } from '@/services/SaveParserBackendService'
|
import {
|
||||||
|
saveParserBackendService,
|
||||||
|
SaveStatusResponse,
|
||||||
|
UploadSaveResponse,
|
||||||
|
} from '@/services/SaveParserBackendService'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import FormError from './FormError'
|
import FormError from './FormError'
|
||||||
|
|
||||||
@@ -18,8 +22,12 @@ export default function SaveFileUploadingDialog() {
|
|||||||
const [selectedFile, setSelectedFile] = useState<File | null>(null)
|
const [selectedFile, setSelectedFile] = useState<File | null>(null)
|
||||||
const [game, setGame] = useState<GameEnum>(GameEnum.EU4)
|
const [game, setGame] = useState<GameEnum>(GameEnum.EU4)
|
||||||
const [uploadDisabled, setUploadDisabled] = useState(false)
|
const [uploadDisabled, setUploadDisabled] = useState(false)
|
||||||
const [saveStatus, setSaveStatus] = useState<SaveStatusResponse | null>(null)
|
const [saveStatus, setSaveStatus] = useState<SaveStatusResponse | null>(
|
||||||
const [uploadResult, setUploadResult] = useState<UploadSaveResponse | null>(null)
|
null
|
||||||
|
)
|
||||||
|
const [uploadResult, setUploadResult] = useState<UploadSaveResponse | null>(
|
||||||
|
null
|
||||||
|
)
|
||||||
|
|
||||||
// Update the selected game from the dropdown.
|
// Update the selected game from the dropdown.
|
||||||
const handleGameSelect = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
const handleGameSelect = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||||
@@ -42,7 +50,10 @@ export default function SaveFileUploadingDialog() {
|
|||||||
if (!selectedFile) return
|
if (!selectedFile) return
|
||||||
|
|
||||||
console.log('Uploading file:', selectedFile.name)
|
console.log('Uploading file:', selectedFile.name)
|
||||||
const uploadResponse = await saveParserBackendService.uploadSave(game, selectedFile)
|
const uploadResponse = await saveParserBackendService.uploadSave(
|
||||||
|
game,
|
||||||
|
selectedFile
|
||||||
|
)
|
||||||
console.log('Upload response:', uploadResponse)
|
console.log('Upload response:', uploadResponse)
|
||||||
setUploadResult(uploadResponse)
|
setUploadResult(uploadResponse)
|
||||||
|
|
||||||
@@ -50,7 +61,10 @@ export default function SaveFileUploadingDialog() {
|
|||||||
// Poll the backend every second for the parsing status until it reports "done".
|
// Poll the backend every second for the parsing status until it reports "done".
|
||||||
const intervalId = setInterval(async () => {
|
const intervalId = setInterval(async () => {
|
||||||
try {
|
try {
|
||||||
const statusResponse = await saveParserBackendService.getSaveStatus(uploadResponse.id)
|
const statusResponse =
|
||||||
|
await saveParserBackendService.getSaveStatus(
|
||||||
|
uploadResponse.id
|
||||||
|
)
|
||||||
console.log('Save status:', statusResponse.status)
|
console.log('Save status:', statusResponse.status)
|
||||||
setSaveStatus(statusResponse)
|
setSaveStatus(statusResponse)
|
||||||
if (statusResponse.status.toLowerCase() === 'done') {
|
if (statusResponse.status.toLowerCase() === 'done') {
|
||||||
@@ -100,7 +114,12 @@ export default function SaveFileUploadingDialog() {
|
|||||||
<label htmlFor="fileInput" className="form-label">
|
<label htmlFor="fileInput" className="form-label">
|
||||||
Select File
|
Select File
|
||||||
</label>
|
</label>
|
||||||
<input id="fileInput" type="file" className="form-control mb-3" onChange={handleFileChange} />
|
<input
|
||||||
|
id="fileInput"
|
||||||
|
type="file"
|
||||||
|
className="form-control mb-3"
|
||||||
|
onChange={handleFileChange}
|
||||||
|
/>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className="btn btn-primary w-100 mt-1 mb-3"
|
className="btn btn-primary w-100 mt-1 mb-3"
|
||||||
|
|||||||
@@ -36,7 +36,10 @@ export class SaveParserBackendService {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`Failed to upload save: ${response.statusText} ` + (await getBodyTextOrEmpty(response)))
|
throw new Error(
|
||||||
|
`Failed to upload save: ${response.statusText} ` +
|
||||||
|
(await getBodyTextOrEmpty(response))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return await response.json()
|
return await response.json()
|
||||||
@@ -48,7 +51,10 @@ export class SaveParserBackendService {
|
|||||||
const response = await fetch(url)
|
const response = await fetch(url)
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`Failed to get save status: ${response.statusText} ` + (await getBodyTextOrEmpty(response)))
|
throw new Error(
|
||||||
|
`Failed to get save status: ${response.statusText} ` +
|
||||||
|
(await getBodyTextOrEmpty(response))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return await response.json()
|
return await response.json()
|
||||||
@@ -60,7 +66,10 @@ export class SaveParserBackendService {
|
|||||||
const response = await fetch(url)
|
const response = await fetch(url)
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`Failed to get parsed save: ${response.statusText} ` + (await getBodyTextOrEmpty(response)))
|
throw new Error(
|
||||||
|
`Failed to get parsed save: ${response.statusText} ` +
|
||||||
|
(await getBodyTextOrEmpty(response))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return await response.json()
|
return await response.json()
|
||||||
|
|||||||
Reference in New Issue
Block a user