format: file width decreased from 120 to 80
This commit is contained in:
@@ -18,7 +18,8 @@ export async function redirectRequest(
|
||||
|
||||
// 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'
|
||||
const backendUrl = backendBaseUrl + reqUrl.substring(proxyIndex + proxyUrl.length)
|
||||
const backendUrl =
|
||||
backendBaseUrl + reqUrl.substring(proxyIndex + proxyUrl.length)
|
||||
// console.log('redirecting', req.url, 'to', backendUrl)
|
||||
|
||||
const backendRes = await fetch(backendUrl, {
|
||||
@@ -33,21 +34,26 @@ export async function redirectRequest(
|
||||
status: backendRes.status,
|
||||
headers: {
|
||||
'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.
|
||||
/// 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)
|
||||
|
||||
return new NextResponse(null, {
|
||||
status: 200,
|
||||
headers: {
|
||||
'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': '*',
|
||||
// req.headers.get('access-control-request-headers') || 'Content-Type, Authorization'
|
||||
},
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
import type { NextRequest } 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 { appConfig } from '@/lib/configLoader'
|
||||
|
||||
// Forwards the incoming request to the save parser backend.
|
||||
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
|
||||
|
||||
+25
-7
@@ -1,7 +1,11 @@
|
||||
'use client'
|
||||
|
||||
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'
|
||||
|
||||
// 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.
|
||||
useEffect(() => {
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
setAgTheme(prefersDark ? themeQuartz.withPart(colorSchemeDark) : themeQuartz)
|
||||
const prefersDark = window.matchMedia(
|
||||
'(prefers-color-scheme: dark)'
|
||||
).matches
|
||||
setAgTheme(
|
||||
prefersDark ? themeQuartz.withPart(colorSchemeDark) : themeQuartz
|
||||
)
|
||||
|
||||
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 () => {
|
||||
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' },
|
||||
]}
|
||||
columnDefs={[
|
||||
{ field: 'col1', headerName: 'Column 1', filter: 'agTextColumnFilter' },
|
||||
{
|
||||
field: 'col1',
|
||||
headerName: 'Column 1',
|
||||
filter: 'agTextColumnFilter',
|
||||
},
|
||||
{ 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.
|
||||
export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{ children: React.ReactNode }>) {
|
||||
// Register the ag-grid community features
|
||||
ModuleRegistry.registerModules([AllCommunityModule])
|
||||
return (
|
||||
|
||||
@@ -14,7 +14,7 @@ export default function LoginPage() {
|
||||
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
// Update the form data with new field value
|
||||
// Update the form data with new field value
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { name, value, type, checked } = e.target
|
||||
setFormData((prev) => ({
|
||||
@@ -30,7 +30,10 @@ export default function LoginPage() {
|
||||
setError(null)
|
||||
try {
|
||||
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)
|
||||
} catch (err: any) {
|
||||
console.error(err.message)
|
||||
|
||||
@@ -36,7 +36,11 @@ export default function RegisterPage() {
|
||||
}
|
||||
|
||||
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)
|
||||
router.push('/login') // Redirect after success
|
||||
} catch (err: any) {
|
||||
|
||||
@@ -6,7 +6,9 @@ import { font_Hack } from '@/lib/myFonts'
|
||||
// Top navigation bar with a home link and login/register buttons.
|
||||
export default function Navbar() {
|
||||
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">
|
||||
{/* Left: Home Link */}
|
||||
<Link href="/" className="navbar-brand">
|
||||
@@ -15,10 +17,16 @@ export default function Navbar() {
|
||||
|
||||
{/* Right: Login/Register Buttons */}
|
||||
<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
|
||||
</Link>
|
||||
<Link href="/register" className="btn btn-primary rounded-pill px-4">
|
||||
<Link
|
||||
href="/register"
|
||||
className="btn btn-primary rounded-pill px-4"
|
||||
>
|
||||
Register
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
'use client'
|
||||
|
||||
import { saveParserBackendService, SaveStatusResponse, UploadSaveResponse } from '@/services/SaveParserBackendService'
|
||||
import {
|
||||
saveParserBackendService,
|
||||
SaveStatusResponse,
|
||||
UploadSaveResponse,
|
||||
} from '@/services/SaveParserBackendService'
|
||||
import { useState } from 'react'
|
||||
import FormError from './FormError'
|
||||
|
||||
@@ -18,8 +22,12 @@ export default function SaveFileUploadingDialog() {
|
||||
const [selectedFile, setSelectedFile] = useState<File | null>(null)
|
||||
const [game, setGame] = useState<GameEnum>(GameEnum.EU4)
|
||||
const [uploadDisabled, setUploadDisabled] = useState(false)
|
||||
const [saveStatus, setSaveStatus] = useState<SaveStatusResponse | null>(null)
|
||||
const [uploadResult, setUploadResult] = useState<UploadSaveResponse | null>(null)
|
||||
const [saveStatus, setSaveStatus] = useState<SaveStatusResponse | null>(
|
||||
null
|
||||
)
|
||||
const [uploadResult, setUploadResult] = useState<UploadSaveResponse | null>(
|
||||
null
|
||||
)
|
||||
|
||||
// Update the selected game from the dropdown.
|
||||
const handleGameSelect = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
@@ -42,7 +50,10 @@ export default function SaveFileUploadingDialog() {
|
||||
if (!selectedFile) return
|
||||
|
||||
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)
|
||||
setUploadResult(uploadResponse)
|
||||
|
||||
@@ -50,7 +61,10 @@ export default function SaveFileUploadingDialog() {
|
||||
// Poll the backend every second for the parsing status until it reports "done".
|
||||
const intervalId = setInterval(async () => {
|
||||
try {
|
||||
const statusResponse = await saveParserBackendService.getSaveStatus(uploadResponse.id)
|
||||
const statusResponse =
|
||||
await saveParserBackendService.getSaveStatus(
|
||||
uploadResponse.id
|
||||
)
|
||||
console.log('Save status:', statusResponse.status)
|
||||
setSaveStatus(statusResponse)
|
||||
if (statusResponse.status.toLowerCase() === 'done') {
|
||||
@@ -100,7 +114,12 @@ export default function SaveFileUploadingDialog() {
|
||||
<label htmlFor="fileInput" className="form-label">
|
||||
Select File
|
||||
</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
|
||||
className="btn btn-primary w-100 mt-1 mb-3"
|
||||
|
||||
@@ -36,7 +36,10 @@ export class SaveParserBackendService {
|
||||
})
|
||||
|
||||
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()
|
||||
@@ -48,7 +51,10 @@ export class SaveParserBackendService {
|
||||
const response = await fetch(url)
|
||||
|
||||
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()
|
||||
@@ -60,7 +66,10 @@ export class SaveParserBackendService {
|
||||
const response = await fetch(url)
|
||||
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user