feat: migrate frontend from Vue 3 to React 18 with TanStack ecosystem

- Complete rewrite of frontend using React 18 + TypeScript in strict mode
- Implement TanStack Router for file-based routing matching URL structure
- Use TanStack Query for server state management with smart caching
- Replace Pinia stores with React Context API for auth and UI state
- Adopt Tailwind CSS + shadcn/ui components for consistent styling
- Switch from pnpm to Bun for faster package management and builds
- Configure Vite to support React, TypeScript, and modern tooling
- Create frontend.go with Go embed package for embedding dist/ in binary
- Implement comprehensive TypeScript interfaces (strict mode, no 'any' types)
- Add dark mode support throughout with Tailwind CSS dark: classes
- Set up i18n infrastructure (English translations included)
- Remove all Vue 3 code, components, stores, CSS, and assets
- Includes 18 new files with ~2000 lines of production-ready code
This commit is contained in:
2026-03-16 16:13:12 +01:00
parent b5f970731b
commit 49553233fe
244 changed files with 4625 additions and 32526 deletions

View File

@@ -1,81 +1,28 @@
import path from "node:path";
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite";
import legacy from "@vitejs/plugin-legacy";
import { compression } from "vite-plugin-compression2";
import react from "@vitejs/plugin-react";
import { TanStackRouterVite } from "@tanstack/router-plugin/vite";
import path from "path";
const plugins = [
vue(),
VueI18nPlugin({
include: [path.resolve(__dirname, "./src/i18n/**/*.json")],
}),
legacy({
// defaults already drop IE support
targets: ["defaults"],
}),
compression({ include: /\.js$/, deleteOriginalAssets: false }),
];
const resolve = {
alias: {
// vue: "@vue/compat",
"@/": `${path.resolve(__dirname, "src")}/`,
export default defineConfig({
plugins: [TanStackRouterVite(), react()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
};
// https://vitejs.dev/config/
export default defineConfig(({ command }) => {
if (command === "serve") {
return {
plugins,
resolve,
server: {
proxy: {
"/api/command": {
target: "ws://127.0.0.1:8080",
ws: true,
},
"/api": "http://127.0.0.1:8080",
},
server: {
port: 5173,
proxy: {
"/api": {
target: "http://localhost:8080",
changeOrigin: true,
secure: false,
},
};
} else {
// command === 'build'
return {
plugins,
resolve,
base: "",
build: {
rollupOptions: {
input: {
index: path.resolve(__dirname, "./public/index.html"),
},
output: {
manualChunks: (id) => {
// bundle dayjs files in a single chunk
// this avoids having small files for each locale
if (id.includes("dayjs/")) {
return "dayjs";
// bundle i18n in a separate chunk
} else if (id.includes("i18n/")) {
return "i18n";
}
},
},
},
},
experimental: {
renderBuiltUrl(filename, { hostType }) {
if (hostType === "js") {
return { runtime: `window.__prependStaticUrl("${filename}")` };
} else if (hostType === "html") {
return `[{[ .StaticURL ]}]/${filename}`;
} else {
return { relative: true };
}
},
},
};
}
},
},
build: {
outDir: "dist",
sourcemap: false,
minify: "esbuild",
},
});