TS conversion 1/2, upgreade logic & state management, react-router-dom, better project structure

This commit is contained in:
Criador_Mod
2026-03-29 02:26:57 +01:00
parent 556ce19725
commit eef6de3ae0
13 changed files with 340 additions and 50 deletions

View File

@@ -70,6 +70,6 @@
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

192
package-lock.json generated
View File

@@ -1,18 +1,26 @@
{
"name": "minecraft-legacy-frontend",
"version": "0.1.0",
"name": "minecraftlegacy-web",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "minecraft-legacy-frontend",
"version": "0.1.0",
"name": "minecraftlegacy-web",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"path": "^0.12.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"typeface-minecraft": "^1.0.0"
"react-router-dom": "^7.13.2",
"typeface-minecraft": "^1.0.0",
"typescript": "^6.0.2",
"zustand": "^5.0.12"
},
"devDependencies": {
"@types/node": "^25.5.0",
"@vitejs/plugin-react": "^4.4.1",
"vite": "^5.4.11"
}
@@ -1149,6 +1157,34 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "25.5.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz",
"integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~7.18.0"
}
},
"node_modules/@types/react": {
"version": "19.2.14",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
"license": "MIT",
"dependencies": {
"csstype": "^3.2.2"
}
},
"node_modules/@types/react-dom": {
"version": "19.2.3",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"license": "MIT",
"peerDependencies": {
"@types/react": "^19.2.0"
}
},
"node_modules/@vitejs/plugin-react": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
@@ -1245,6 +1281,25 @@
"dev": true,
"license": "MIT"
},
"node_modules/cookie": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
"integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/csstype": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"license": "MIT"
},
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@@ -1344,6 +1399,12 @@
"node": ">=6.9.0"
}
},
"node_modules/inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
"license": "ISC"
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -1431,6 +1492,16 @@
"dev": true,
"license": "MIT"
},
"node_modules/path": {
"version": "0.12.7",
"resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz",
"integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==",
"license": "MIT",
"dependencies": {
"process": "^0.11.1",
"util": "^0.10.3"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -1467,6 +1538,15 @@
"node": "^10 || ^12 || >=14"
}
},
"node_modules/process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
"license": "MIT",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/react": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
@@ -1502,6 +1582,44 @@
"node": ">=0.10.0"
}
},
"node_modules/react-router": {
"version": "7.13.2",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.2.tgz",
"integrity": "sha512-tX1Aee+ArlKQP+NIUd7SE6Li+CiGKwQtbS+FfRxPX6Pe4vHOo6nr9d++u5cwg+Z8K/x8tP+7qLmujDtfrAoUJA==",
"license": "MIT",
"dependencies": {
"cookie": "^1.0.1",
"set-cookie-parser": "^2.6.0"
},
"engines": {
"node": ">=20.0.0"
},
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
}
}
},
"node_modules/react-router-dom": {
"version": "7.13.2",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.2.tgz",
"integrity": "sha512-aR7SUORwTqAW0JDeiWF07e9SBE9qGpByR9I8kJT5h/FrBKxPMS6TiC7rmVO+gC0q52Bx7JnjWe8Z1sR9faN4YA==",
"license": "MIT",
"dependencies": {
"react-router": "7.13.2"
},
"engines": {
"node": ">=20.0.0"
},
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18"
}
},
"node_modules/rollup": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
@@ -1566,6 +1684,12 @@
"semver": "bin/semver.js"
}
},
"node_modules/set-cookie-parser": {
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
"integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
"license": "MIT"
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -1582,6 +1706,26 @@
"integrity": "sha512-EuS7/PEPe7D9Z9zi0Hnn+uwgkGHG/Drkufo9IUjUzPUwW0+EdAepaHUmtmfbBBnhFu7r3JbnK5EczngBQ3ZQLw==",
"license": "MIT"
},
"node_modules/typescript": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz",
"integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==",
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "7.18.2",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
"dev": true,
"license": "MIT"
},
"node_modules/update-browserslist-db": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
@@ -1613,6 +1757,15 @@
"browserslist": ">= 4.21.0"
}
},
"node_modules/util": {
"version": "0.10.4",
"resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
"integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
"license": "MIT",
"dependencies": {
"inherits": "2.0.3"
}
},
"node_modules/vite": {
"version": "5.4.21",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
@@ -1679,6 +1832,35 @@
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true,
"license": "ISC"
},
"node_modules/zustand": {
"version": "5.0.12",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.12.tgz",
"integrity": "sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==",
"license": "MIT",
"engines": {
"node": ">=12.20.0"
},
"peerDependencies": {
"@types/react": ">=18.0.0",
"immer": ">=9.0.6",
"react": ">=18.0.0",
"use-sync-external-store": ">=1.2.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"immer": {
"optional": true
},
"react": {
"optional": true
},
"use-sync-external-store": {
"optional": true
}
}
}
}
}

View File

@@ -20,12 +20,19 @@
"preview": "vite preview"
},
"dependencies": {
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"path": "^0.12.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"typeface-minecraft": "^1.0.0"
"react-router-dom": "^7.13.2",
"typeface-minecraft": "^1.0.0",
"typescript": "^6.0.2",
"zustand": "^5.0.12"
},
"devDependencies": {
"@types/node": "^25.5.0",
"@vitejs/plugin-react": "^4.4.1",
"vite": "^5.4.11"
}
}
}

View File

@@ -1,5 +1,7 @@
import { useEffect, useMemo, useRef, useState } from "react";
// Assets
import dirtBg from "../assets/images/backgrounds/Dirt_background_JE1.webp";
import endPoemBg from "../assets/images/backgrounds/End_Poem_Background_JE.webp";
import endStoneBg from "../assets/images/backgrounds/End_Stone_background_BE2.webp";
@@ -31,6 +33,9 @@ import sweden from "../assets/audio/music/Sweden.mp3";
import taswell from "../assets/audio/music/Taswell.mp3";
import wetHands from "../assets/audio/music/Wet Hands.mp3";
// Zustand
import useRepoStore from "@/zustand/ReposStore";
const logoSet = {
small: logoSmall,
medium: logoMedium,
@@ -68,11 +73,9 @@ function randomItem(list) {
}
function App() {
const [repoConfig, setRepoConfig] = useState([]);
const { projects, loading, error } = useRepoStore();
const [query, setQuery] = useState("");
const [page, setPage] = useState(1);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
const [loaderFadingOut, setLoaderFadingOut] = useState(false);
const [showLoader, setShowLoader] = useState(true);
@@ -91,26 +94,6 @@ function App() {
}, []);
const isCodLoadingScreen = loadingImage === codLoader;
useEffect(() => {
const loadRepos = async () => {
try {
const jsonFile = window.location.hostname === "localhost" ? "placeholder.json" : "projects.json";
const response = await fetch(`https://raw.githubusercontent.com/MinecraftConsole/json/refs/heads/main/${jsonFile}`);
if (!response.ok) {
throw new Error(`Could not load project list (${response.status})`);
}
const data = await response.json();
setRepoConfig(Array.isArray(data.repos) ? data.repos : []);
} catch (err) {
setError(err instanceof Error ? err.message : "Unknown error loading repos");
} finally {
setLoading(false);
}
};
loadRepos();
}, []);
useEffect(() => {
if (loading) return;
@@ -208,13 +191,13 @@ function App() {
};
const sortedRepos = useMemo(() => {
return [...repoConfig].sort((a, b) => {
return [...projects].sort((a, b) => {
const aPriority = Number.isFinite(a.priority) ? a.priority : 9999;
const bPriority = Number.isFinite(b.priority) ? b.priority : 9999;
if (aPriority !== bPriority) return aPriority - bPriority;
return String(a.name || "").localeCompare(String(b.name || ""));
});
}, [repoConfig]);
}, [projects]);
const filteredRepos = useMemo(() => {
const normalized = query.trim().toLowerCase();

View File

@@ -1,11 +0,0 @@
import React from "react";
import ReactDOM from "react-dom/client";
import "typeface-minecraft";
import App from "./App.jsx";
import "./styles.css";
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<App />
</React.StrictMode>
);

35
src/main.tsx Normal file
View File

@@ -0,0 +1,35 @@
import ReactDOM from "react-dom/client";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import "typeface-minecraft";
import "@/css/styles.css";
// Utils
import FetchProjects from "@/utils/FetchProjects";
// Pages
import Main from "./App";
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
const App = () => {
const Fetch = async () => {
await FetchProjects.SetProjects();
};
Fetch();
return (
<>
<Routes>
<Route path="/" element={<Main />} />
</Routes>
</>
);
};
document.addEventListener("contextmenu", (e) => e.preventDefault());
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);

7
src/types/projects.ts Normal file
View File

@@ -0,0 +1,7 @@
export interface IProjects {
name: string;
url: string;
priority: number;
tag: string;
mirror: string;
}

View File

@@ -0,0 +1,27 @@
// Types
import { IProjects } from "../types/projects";
// Zustand
import useReposStore from "../zustand/ReposStore";
type ReposResponse = {
repos: IProjects[];
};
export default new (class {
public async SetProjects(): Promise<void> {
try {
const jsonFile = window.location.hostname === "localhost" ? "placeholder.json" : "projects.json";
const res = await fetch(`https://raw.githubusercontent.com/MinecraftConsole/json/refs/heads/main/${jsonFile}`);
if (!res.ok) throw new Error("Request failed");
const data: ReposResponse = await res.json();
useReposStore.setState({ projects: data.repos ?? [] });
} catch (err) {
useReposStore.setState({ error: err instanceof Error ? err.message : "An unknown error occurred" });
} finally {
useReposStore.setState({ loading: false });
}
}
})();

28
src/zustand/ReposStore.ts Normal file
View File

@@ -0,0 +1,28 @@
import { create } from "zustand";
// Types
import { IProjects } from "../types/projects";
type State = {
loading: boolean;
projects: IProjects[];
error: string;
};
type Actions = {
setLoading: (x: boolean) => void;
setProjects: (x: IProjects[]) => void;
setError: (x: string) => void;
};
const useRepoStore = create<State & Actions>((set) => ({
projects: [],
loading: true,
error: "",
setLoading: (loading) => set(() => ({ loading })),
setProjects: (projects) => set(() => ({ projects })),
setError: (error) => set(() => ({ error })),
}));
export default useRepoStore;

24
tsconfig.json Normal file
View File

@@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"jsx": "react-jsx",
"moduleResolution": "node",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"allowJs": true,
// "checkJs": true,
"noEmit": true,
"outDir": "dist",
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
},
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}

View File

@@ -1,6 +0,0 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()]
});

14
vite.config.ts Normal file
View File

@@ -0,0 +1,14 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path from "path";
export default defineConfig({
plugins: [
react()
],
resolve: {
alias: {
"@": path.resolve("src"),
},
},
});