mirror of
https://github.com/TheRealOwenRees/chess-scribe-website.git
synced 2026-07-23 01:46:57 +00:00
Compare commits
6 Commits
a5bb7dafd9
...
2d56700dd8
| Author | SHA1 | Date | |
|---|---|---|---|
|
2d56700dd8
|
|||
|
4cd7b0199e
|
|||
|
5ed83a20d8
|
|||
|
00267b05ba
|
|||
|
7e205df26d
|
|||
|
551b07fc42
|
@@ -0,0 +1,4 @@
|
|||||||
|
DISCORD_CONTACT_WEBHOOK=
|
||||||
|
DISCORD_ERROR_LOG_WEBHOOK=
|
||||||
|
LICHESS_CLIENT_ID=
|
||||||
|
VITE_API_BASE_URL=
|
||||||
@@ -3,6 +3,7 @@ FEN string
|
|||||||
Env Vars
|
Env Vars
|
||||||
API base URL
|
API base URL
|
||||||
|
|
||||||
|
- favicon and page titles
|
||||||
- rearrange buttons / header space - collapse on smaller screens
|
- rearrange buttons / header space - collapse on smaller screens
|
||||||
|
|
||||||
- add / edit headers
|
- add / edit headers
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import type { ChangeEvent } from "react";
|
||||||
|
import FormField from "#/components/FormField.tsx";
|
||||||
|
import type { IHeader } from "#/interfaces.ts";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
headers: IHeader;
|
||||||
|
updateHeaders: (e: ChangeEvent<HTMLInputElement>, fieldName: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CustomHeaders = ({ headers, updateHeaders }: IProps) => {
|
||||||
|
return (
|
||||||
|
<div className="mt-4 w-full">
|
||||||
|
<details className="bg-(--neutral-content)/20 p-4 rounded-lg">
|
||||||
|
<summary>
|
||||||
|
Custom Headers (PDF)
|
||||||
|
<p className="text-sm font-normal">
|
||||||
|
Text in these fields will overwrite the PGN headers in the generated
|
||||||
|
PDF
|
||||||
|
</p>
|
||||||
|
</summary>
|
||||||
|
<div>
|
||||||
|
<form className="grid place-items-center gap-4 sm:grid-cols-2 mt-2">
|
||||||
|
<FormField
|
||||||
|
fieldName="title"
|
||||||
|
type="text"
|
||||||
|
headers={headers}
|
||||||
|
updateHeaders={updateHeaders}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
fieldName="subtitle"
|
||||||
|
type="text"
|
||||||
|
headers={headers}
|
||||||
|
updateHeaders={updateHeaders}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
fieldName="date"
|
||||||
|
type="text"
|
||||||
|
headers={headers}
|
||||||
|
updateHeaders={updateHeaders}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
fieldName="author"
|
||||||
|
type="text"
|
||||||
|
headers={headers}
|
||||||
|
updateHeaders={updateHeaders}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CustomHeaders;
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import type { ChangeEvent } from "react";
|
||||||
|
import type { IHeader } from "#/interfaces.ts";
|
||||||
|
|
||||||
|
export interface IFormField {
|
||||||
|
fieldName: string;
|
||||||
|
type: string;
|
||||||
|
headers: IHeader;
|
||||||
|
updateHeaders: (e: ChangeEvent<HTMLInputElement>, fieldName: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FormField = ({ fieldName, type, headers, updateHeaders }: IFormField) => {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<label htmlFor={fieldName} className="text-sm capitalize">
|
||||||
|
{fieldName}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
className="headers__input"
|
||||||
|
type={type}
|
||||||
|
id={fieldName}
|
||||||
|
name={fieldName}
|
||||||
|
value={headers[fieldName]}
|
||||||
|
onChange={(e) => updateHeaders(e, fieldName)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FormField;
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
import type { ChangeEvent } from "react";
|
||||||
|
import FormField from "#/components/FormField.tsx";
|
||||||
|
import type { IHeader } from "#/interfaces.ts";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
headers: IHeader;
|
||||||
|
updateHeaders: (e: ChangeEvent<HTMLInputElement>, fieldName: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const HeaderFields = ({ headers, updateHeaders }: IProps) => {
|
||||||
|
return (
|
||||||
|
<div className="mt-4 w-full">
|
||||||
|
<details className="bg-(--neutral-content)/15 p-4 rounded-lg">
|
||||||
|
<summary>
|
||||||
|
Headers
|
||||||
|
<p className="text-sm font-normal">Edit the game PGN headers</p>
|
||||||
|
</summary>
|
||||||
|
<div>
|
||||||
|
<form className="grid place-items-center gap-4 sm:grid-cols-2 mt-2">
|
||||||
|
<FormField
|
||||||
|
fieldName="event"
|
||||||
|
type="text"
|
||||||
|
headers={headers}
|
||||||
|
updateHeaders={updateHeaders}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
fieldName="site"
|
||||||
|
type="text"
|
||||||
|
headers={headers}
|
||||||
|
updateHeaders={updateHeaders}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
fieldName="date"
|
||||||
|
type="text"
|
||||||
|
headers={headers}
|
||||||
|
updateHeaders={updateHeaders}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
fieldName="round"
|
||||||
|
type="text"
|
||||||
|
headers={headers}
|
||||||
|
updateHeaders={updateHeaders}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
fieldName="white"
|
||||||
|
type="text"
|
||||||
|
headers={headers}
|
||||||
|
updateHeaders={updateHeaders}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
fieldName="black"
|
||||||
|
type="text"
|
||||||
|
headers={headers}
|
||||||
|
updateHeaders={updateHeaders}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
fieldName="result"
|
||||||
|
type="text"
|
||||||
|
headers={headers}
|
||||||
|
updateHeaders={updateHeaders}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
fieldName="eco"
|
||||||
|
type="text"
|
||||||
|
headers={headers}
|
||||||
|
updateHeaders={updateHeaders}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
fieldName="whiteElo"
|
||||||
|
type="text"
|
||||||
|
headers={headers}
|
||||||
|
updateHeaders={updateHeaders}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
fieldName="blackElo"
|
||||||
|
type="text"
|
||||||
|
headers={headers}
|
||||||
|
updateHeaders={updateHeaders}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
fieldName="plyCount"
|
||||||
|
type="text"
|
||||||
|
headers={headers}
|
||||||
|
updateHeaders={updateHeaders}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
fieldName="eventDate"
|
||||||
|
type="text"
|
||||||
|
headers={headers}
|
||||||
|
updateHeaders={updateHeaders}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
fieldName="source"
|
||||||
|
type="text"
|
||||||
|
headers={headers}
|
||||||
|
updateHeaders={updateHeaders}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HeaderFields;
|
||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import type { IPosition } from "#/interfaces.ts";
|
import type { IHeader, IPosition } from "#/interfaces.ts";
|
||||||
import { gameReducer, initialGameState } from "#/reducers/gameReducer.ts";
|
import { gameReducer, initialGameState } from "#/reducers/gameReducer.ts";
|
||||||
import { downloadPDF } from "#/utils/pdfUtils.ts";
|
import { downloadPDF } from "#/utils/pdfUtils.ts";
|
||||||
import { buildPgnString, getHeaders } from "#/utils/pgnUtils.ts";
|
import { buildPgnString, getHeaders } from "#/utils/pgnUtils.ts";
|
||||||
@@ -118,6 +118,7 @@ export const useChessGame = () => {
|
|||||||
...getHeaders(pgnString),
|
...getHeaders(pgnString),
|
||||||
title: "",
|
title: "",
|
||||||
subtitle: "",
|
subtitle: "",
|
||||||
|
author: "",
|
||||||
};
|
};
|
||||||
gameDispatch({
|
gameDispatch({
|
||||||
type: "SET_GAME",
|
type: "SET_GAME",
|
||||||
@@ -153,6 +154,18 @@ export const useChessGame = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updateHeaders = (
|
||||||
|
e: ChangeEvent<HTMLInputElement>,
|
||||||
|
fieldName: string,
|
||||||
|
) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const updatedHeaders = {
|
||||||
|
...gameState.headers,
|
||||||
|
[fieldName]: e.target.value,
|
||||||
|
} as IHeader;
|
||||||
|
gameDispatch({ type: "SET_HEADERS", payload: updatedHeaders });
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
gameState,
|
gameState,
|
||||||
fileInputRef,
|
fileInputRef,
|
||||||
@@ -165,5 +178,6 @@ export const useChessGame = () => {
|
|||||||
handleToggleDiagram,
|
handleToggleDiagram,
|
||||||
currentPosition,
|
currentPosition,
|
||||||
generatingPdf,
|
generatingPdf,
|
||||||
|
updateHeaders,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ export interface IHeader {
|
|||||||
plyCount: string;
|
plyCount: string;
|
||||||
eventDate: string;
|
eventDate: string;
|
||||||
source: string;
|
source: string;
|
||||||
|
title: string;
|
||||||
|
subtitle: string;
|
||||||
|
author: string;
|
||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+31
-25
@@ -1,3 +1,5 @@
|
|||||||
|
import CustomHeaders from "#/components/CustomHeaders.tsx";
|
||||||
|
import HeaderFields from "#/components/HeaderFields.tsx";
|
||||||
import LichessButton from "#/components/LichessButton.tsx";
|
import LichessButton from "#/components/LichessButton.tsx";
|
||||||
import PgnViewer from "#/components/PgnViewer.tsx";
|
import PgnViewer from "#/components/PgnViewer.tsx";
|
||||||
import Section from "#/components/Section.tsx";
|
import Section from "#/components/Section.tsx";
|
||||||
@@ -16,12 +18,13 @@ const Chessboard = () => {
|
|||||||
handleToggleClock,
|
handleToggleClock,
|
||||||
handleToggleDiagram,
|
handleToggleDiagram,
|
||||||
handlePlyChange,
|
handlePlyChange,
|
||||||
|
updateHeaders,
|
||||||
} = useChessGame();
|
} = useChessGame();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="px-4 pb-8 place-self-center min-h-[calc(100vh-226px)]">
|
<main className="px-4 pb-8 place-self-center min-h-[calc(100vh-226px)]">
|
||||||
<Section title="Convert PGN to PDF">
|
<Section title="Convert PGN to PDF">
|
||||||
<LichessButton /> or{" "}
|
<LichessButton onClickHandler={() => console.log("Lichess Login")} /> or{" "}
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
id="file-input"
|
id="file-input"
|
||||||
@@ -78,32 +81,35 @@ const Chessboard = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-4">
|
<div className="flex flex-col gap-4 items-center">
|
||||||
{/*<button*/}
|
<div className="flex justify-center gap-4">
|
||||||
{/* className="btn btn-primary"*/}
|
<button
|
||||||
{/* type="button"*/}
|
className="btn btn-secondary"
|
||||||
{/* onClick={handleClearGame}*/}
|
type="button"
|
||||||
{/*>*/}
|
onClick={handleSavePgn}
|
||||||
{/* Clear Game*/}
|
disabled={!gameState.pgn}
|
||||||
{/*</button>*/}
|
>
|
||||||
|
Save PGN
|
||||||
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleSavePgn}
|
onClick={handleSavePdf}
|
||||||
disabled={!gameState.pgn}
|
disabled={!gameState.pgn || generatingPdf}
|
||||||
>
|
>
|
||||||
Save PGN
|
Save PDF
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button
|
<HeaderFields
|
||||||
className="btn btn-secondary"
|
headers={gameState.headers}
|
||||||
type="button"
|
updateHeaders={updateHeaders}
|
||||||
onClick={handleSavePdf}
|
/>
|
||||||
disabled={!gameState.pgn || generatingPdf}
|
<CustomHeaders
|
||||||
>
|
headers={gameState.headers}
|
||||||
Save PDF
|
updateHeaders={updateHeaders}
|
||||||
</button>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
+19
-4
@@ -1,9 +1,24 @@
|
|||||||
|
/* Headers */
|
||||||
|
.headers__input {
|
||||||
|
height: 3rem;
|
||||||
|
padding: 0 1rem;
|
||||||
|
line-height: 1.5rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
border: 1px solid var(--neutral-content);
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--accent);
|
||||||
|
background-color: var(--header-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Checkbox */
|
/* Checkbox */
|
||||||
.checkbox-accent {
|
.checkbox-accent {
|
||||||
accent-color: var(--accent);
|
accent-color: var(--accent);
|
||||||
width: 18px;
|
width: 18px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Toggle */
|
/* Toggle */
|
||||||
|
|||||||
Reference in New Issue
Block a user