diff --git a/project/frontend/.dockerignore b/project/frontend/.dockerignore
new file mode 100644
index 0000000..f965aed
--- /dev/null
+++ b/project/frontend/.dockerignore
@@ -0,0 +1,15 @@
+node_modules
+Dockerfile*
+docker-compose*
+.dockerignore
+.git
+.gitignore
+README.md
+LICENSE
+.vscode
+Makefile
+helm-charts
+.env
+.editorconfig
+.idea
+coverage*
diff --git a/project/frontend/.gitignore b/project/frontend/.gitignore
new file mode 100644
index 0000000..d451ff1
--- /dev/null
+++ b/project/frontend/.gitignore
@@ -0,0 +1,5 @@
+node_modules
+.DS_Store
+dist
+dist-ssr
+*.local
diff --git a/project/frontend/.vscode/settings.json b/project/frontend/.vscode/settings.json
new file mode 100644
index 0000000..b001961
--- /dev/null
+++ b/project/frontend/.vscode/settings.json
@@ -0,0 +1,35 @@
+{
+ "files.watcherExclude": {
+ "**/routeTree.gen.ts": true
+ },
+ "search.exclude": {
+ "**/routeTree.gen.ts": true
+ },
+ "files.readonlyInclude": {
+ "**/routeTree.gen.ts": true
+ },
+ "[javascript]": {
+ "editor.defaultFormatter": "biomejs.biome"
+ },
+ "[javascriptreact]": {
+ "editor.defaultFormatter": "biomejs.biome"
+ },
+ "[typescript]": {
+ "editor.defaultFormatter": "biomejs.biome"
+ },
+ "[typescriptreact]": {
+ "editor.defaultFormatter": "biomejs.biome"
+ },
+ "[json]": {
+ "editor.defaultFormatter": "biomejs.biome"
+ },
+ "[jsonc]": {
+ "editor.defaultFormatter": "biomejs.biome"
+ },
+ "[css]": {
+ "editor.defaultFormatter": "biomejs.biome"
+ },
+ "editor.codeActionsOnSave": {
+ "source.organizeImports.biome": "explicit"
+ }
+}
diff --git a/project/frontend/Dockerfile b/project/frontend/Dockerfile
new file mode 100644
index 0000000..617e964
--- /dev/null
+++ b/project/frontend/Dockerfile
@@ -0,0 +1,15 @@
+FROM oven/bun:1 AS base
+WORKDIR /usr/src/app
+
+# install dependencies into temp directory
+# this will cache them and speed up future builds
+COPY package.json bun.lockb ./
+RUN bun install --frozen-lockfile
+
+COPY . .
+
+RUN bun run build
+
+FROM nginx:1.28.0-alpine3.21
+COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
+COPY --from=base /usr/src/app/dist /usr/share/nginx/html
diff --git a/project/frontend/README.md b/project/frontend/README.md
new file mode 100644
index 0000000..9c45ff3
--- /dev/null
+++ b/project/frontend/README.md
@@ -0,0 +1,295 @@
+Welcome to your new TanStack app!
+
+# Getting Started
+
+To run this application:
+
+```bash
+bun install
+bunx --bun run start
+```
+
+# Building For Production
+
+To build this application for production:
+
+```bash
+bunx --bun run build
+```
+
+## Testing
+
+This project uses [Vitest](https://vitest.dev/) for testing. You can run the tests with:
+
+```bash
+bunx --bun run test
+```
+
+## Linting & Formatting
+
+This project uses [Biome](https://biomejs.dev/) for linting and formatting. The following scripts are available:
+
+
+```bash
+bunx --bun run lint
+bunx --bun run format
+bunx --bun run check
+```
+
+
+## Routing
+This project uses [TanStack Router](https://tanstack.com/router). The initial setup is a file based router. Which means that the routes are managed as files in `src/routes`.
+
+### Adding A Route
+
+To add a new route to your application just add another a new file in the `./src/routes` directory.
+
+TanStack will automatically generate the content of the route file for you.
+
+Now that you have two routes you can use a `Link` component to navigate between them.
+
+### Adding Links
+
+To use SPA (Single Page Application) navigation you will need to import the `Link` component from `@tanstack/react-router`.
+
+```tsx
+import { Link } from "@tanstack/react-router";
+```
+
+Then anywhere in your JSX you can use it like so:
+
+```tsx
+About
+```
+
+This will create a link that will navigate to the `/about` route.
+
+More information on the `Link` component can be found in the [Link documentation](https://tanstack.com/router/v1/docs/framework/react/api/router/linkComponent).
+
+### Using A Layout
+
+In the File Based Routing setup the layout is located in `src/routes/__root.tsx`. Anything you add to the root route will appear in all the routes. The route content will appear in the JSX where you use the `` component.
+
+Here is an example layout that includes a header:
+
+```tsx
+import { Outlet, createRootRoute } from '@tanstack/react-router'
+import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
+
+import { Link } from "@tanstack/react-router";
+
+export const Route = createRootRoute({
+ component: () => (
+ <>
+
+
+
+
+
+ >
+ ),
+})
+```
+
+The `` component is not required so you can remove it if you don't want it in your layout.
+
+More information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/react/guide/routing-concepts#layouts).
+
+
+## Data Fetching
+
+There are multiple ways to fetch data in your application. You can use TanStack Query to fetch data from a server. But you can also use the `loader` functionality built into TanStack Router to load the data for a route before it's rendered.
+
+For example:
+
+```tsx
+const peopleRoute = createRoute({
+ getParentRoute: () => rootRoute,
+ path: "/people",
+ loader: async () => {
+ const response = await fetch("https://swapi.dev/api/people");
+ return response.json() as Promise<{
+ results: {
+ name: string;
+ }[];
+ }>;
+ },
+ component: () => {
+ const data = peopleRoute.useLoaderData();
+ return (
+
+ {data.results.map((person) => (
+
{person.name}
+ ))}
+
+ );
+ },
+});
+```
+
+Loaders simplify your data fetching logic dramatically. Check out more information in the [Loader documentation](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#loader-parameters).
+
+### React-Query
+
+React-Query is an excellent addition or alternative to route loading and integrating it into you application is a breeze.
+
+First add your dependencies:
+
+```bash
+bun install @tanstack/react-query @tanstack/react-query-devtools
+```
+
+Next we'll need to create a query client and provider. We recommend putting those in `main.tsx`.
+
+```tsx
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+
+// ...
+
+const queryClient = new QueryClient();
+
+// ...
+
+if (!rootElement.innerHTML) {
+ const root = ReactDOM.createRoot(rootElement);
+
+ root.render(
+
+
+
+ );
+}
+```
+
+You can also add TanStack Query Devtools to the root route (optional).
+
+```tsx
+import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
+
+const rootRoute = createRootRoute({
+ component: () => (
+ <>
+
+
+
+ >
+ ),
+});
+```
+
+Now you can use `useQuery` to fetch your data.
+
+```tsx
+import { useQuery } from "@tanstack/react-query";
+
+import "./App.css";
+
+function App() {
+ const { data } = useQuery({
+ queryKey: ["people"],
+ queryFn: () =>
+ fetch("https://swapi.dev/api/people")
+ .then((res) => res.json())
+ .then((data) => data.results as { name: string }[]),
+ initialData: [],
+ });
+
+ return (
+
+
+ {data.map((person) => (
+
{person.name}
+ ))}
+
+
+ );
+}
+
+export default App;
+```
+
+You can find out everything you need to know on how to use React-Query in the [React-Query documentation](https://tanstack.com/query/latest/docs/framework/react/overview).
+
+## State Management
+
+Another common requirement for React applications is state management. There are many options for state management in React. TanStack Store provides a great starting point for your project.
+
+First you need to add TanStack Store as a dependency:
+
+```bash
+bun install @tanstack/store
+```
+
+Now let's create a simple counter in the `src/App.tsx` file as a demonstration.
+
+```tsx
+import { useStore } from "@tanstack/react-store";
+import { Store } from "@tanstack/store";
+import "./App.css";
+
+const countStore = new Store(0);
+
+function App() {
+ const count = useStore(countStore);
+ return (
+
+
+
+ );
+}
+
+export default App;
+```
+
+One of the many nice features of TanStack Store is the ability to derive state from other state. That derived state will update when the base state updates.
+
+Let's check this out by doubling the count using derived state.
+
+```tsx
+import { useStore } from "@tanstack/react-store";
+import { Store, Derived } from "@tanstack/store";
+import "./App.css";
+
+const countStore = new Store(0);
+
+const doubledStore = new Derived({
+ fn: () => countStore.state * 2,
+ deps: [countStore],
+});
+doubledStore.mount();
+
+function App() {
+ const count = useStore(countStore);
+ const doubledCount = useStore(doubledStore);
+
+ return (
+
+
+
Doubled - {doubledCount}
+
+ );
+}
+
+export default App;
+```
+
+We use the `Derived` class to create a new store that is derived from another store. The `Derived` class has a `mount` method that will start the derived store updating.
+
+Once we've created the derived store we can use it in the `App` component just like we would any other store using the `useStore` hook.
+
+You can find out everything you need to know on how to use TanStack Store in the [TanStack Store documentation](https://tanstack.com/store/latest).
+
+# Demo files
+
+Files prefixed with `demo` can be safely deleted. They are there to provide a starting point for you to play around with the features you've installed.
+
+# Learn More
+
+You can learn more about all of the offerings from TanStack in the [TanStack documentation](https://tanstack.com).
diff --git a/project/frontend/biome.json b/project/frontend/biome.json
new file mode 100644
index 0000000..55240aa
--- /dev/null
+++ b/project/frontend/biome.json
@@ -0,0 +1,31 @@
+{
+ "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
+ "vcs": {
+ "enabled": false,
+ "clientKind": "git",
+ "useIgnoreFile": false
+ },
+ "files": {
+ "ignoreUnknown": false,
+ "ignore": ["src/routeTree.gen.ts"],
+ "include": ["src/*", ".vscode/*", "index.html", "vite.config.js"]
+ },
+ "formatter": {
+ "enabled": true,
+ "indentStyle": "space"
+ },
+ "organizeImports": {
+ "enabled": true
+ },
+ "linter": {
+ "enabled": true,
+ "rules": {
+ "recommended": true
+ }
+ },
+ "javascript": {
+ "formatter": {
+ "quoteStyle": "double"
+ }
+ }
+}
diff --git a/project/frontend/bun.lockb b/project/frontend/bun.lockb
new file mode 100755
index 0000000..776a922
Binary files /dev/null and b/project/frontend/bun.lockb differ
diff --git a/project/frontend/docker/nginx.conf b/project/frontend/docker/nginx.conf
new file mode 100644
index 0000000..4bd5b3b
--- /dev/null
+++ b/project/frontend/docker/nginx.conf
@@ -0,0 +1,23 @@
+server {
+ listen 80;
+ server_name localhost;
+ root /usr/share/nginx/html;
+ index index.html;
+
+ # Caching configuration for static assets
+ location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
+ expires 30d;
+ add_header Cache-Control "public, no-transform";
+ }
+
+ # Always serve index.html for any request
+ location / {
+ try_files $uri $uri/ /index.html;
+ }
+
+ # Error handling
+ error_page 500 502 503 504 /50x.html;
+ location = /50x.html {
+ root /usr/share/nginx/html;
+ }
+}
\ No newline at end of file
diff --git a/project/frontend/index.html b/project/frontend/index.html
new file mode 100644
index 0000000..2f632de
--- /dev/null
+++ b/project/frontend/index.html
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+ Create TanStack App - frontend
+
+
+
+
+
+
diff --git a/project/frontend/package.json b/project/frontend/package.json
new file mode 100644
index 0000000..cb88c9a
--- /dev/null
+++ b/project/frontend/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "frontend",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "start": "vite",
+ "build": "vite build && tsc",
+ "serve": "vite preview",
+ "test": "vitest run",
+ "format": "biome format --write",
+ "lint": "biome lint",
+ "check": "biome check"
+ },
+ "dependencies": {
+ "@emotion/react": "^11.14.0",
+ "@emotion/styled": "^11.14.0",
+ "@fontsource/roboto": "^5.2.5",
+ "@mui/icons-material": "^7.1.0",
+ "@mui/material": "^7.1.0",
+ "@tanstack/react-query": "^5.66.5",
+ "@tanstack/react-query-devtools": "^5.66.5",
+ "@tanstack/react-router": "^1.114.3",
+ "@tanstack/react-router-devtools": "^1.114.3",
+ "@tanstack/router-plugin": "^1.114.3",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0",
+ "react-pdf": "^9.2.1"
+ },
+ "devDependencies": {
+ "@biomejs/biome": "1.9.4",
+ "@testing-library/dom": "^10.4.0",
+ "@testing-library/react": "^16.2.0",
+ "@types/react": "^19.0.8",
+ "@types/react-dom": "^19.0.3",
+ "@vitejs/plugin-react": "^4.3.4",
+ "jsdom": "^26.0.0",
+ "typescript": "^5.7.2",
+ "vite": "^6.1.0",
+ "vitest": "^3.0.5"
+ }
+}
diff --git a/project/frontend/public/favicon.ico b/project/frontend/public/favicon.ico
new file mode 100644
index 0000000..a11777c
Binary files /dev/null and b/project/frontend/public/favicon.ico differ
diff --git a/project/frontend/public/logo192.png b/project/frontend/public/logo192.png
new file mode 100644
index 0000000..fc44b0a
Binary files /dev/null and b/project/frontend/public/logo192.png differ
diff --git a/project/frontend/public/logo512.png b/project/frontend/public/logo512.png
new file mode 100644
index 0000000..a4e47a6
Binary files /dev/null and b/project/frontend/public/logo512.png differ
diff --git a/project/frontend/public/manifest.json b/project/frontend/public/manifest.json
new file mode 100644
index 0000000..078ef50
--- /dev/null
+++ b/project/frontend/public/manifest.json
@@ -0,0 +1,25 @@
+{
+ "short_name": "TanStack App",
+ "name": "Create TanStack App Sample",
+ "icons": [
+ {
+ "src": "favicon.ico",
+ "sizes": "64x64 32x32 24x24 16x16",
+ "type": "image/x-icon"
+ },
+ {
+ "src": "logo192.png",
+ "type": "image/png",
+ "sizes": "192x192"
+ },
+ {
+ "src": "logo512.png",
+ "type": "image/png",
+ "sizes": "512x512"
+ }
+ ],
+ "start_url": ".",
+ "display": "standalone",
+ "theme_color": "#000000",
+ "background_color": "#ffffff"
+}
diff --git a/project/frontend/public/robots.txt b/project/frontend/public/robots.txt
new file mode 100644
index 0000000..e9e57dc
--- /dev/null
+++ b/project/frontend/public/robots.txt
@@ -0,0 +1,3 @@
+# https://www.robotstxt.org/robotstxt.html
+User-agent: *
+Disallow:
diff --git a/project/frontend/src/components/Header.tsx b/project/frontend/src/components/Header.tsx
new file mode 100644
index 0000000..383cf9e
--- /dev/null
+++ b/project/frontend/src/components/Header.tsx
@@ -0,0 +1,17 @@
+import { Link } from "@tanstack/react-router";
+
+export default function Header() {
+ return (
+
+
+
+ );
+}
diff --git a/project/frontend/src/integrations/tanstack-query/layout.tsx b/project/frontend/src/integrations/tanstack-query/layout.tsx
new file mode 100644
index 0000000..68b9cdd
--- /dev/null
+++ b/project/frontend/src/integrations/tanstack-query/layout.tsx
@@ -0,0 +1,5 @@
+import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
+
+export default function LayoutAddition() {
+ return ;
+}
diff --git a/project/frontend/src/integrations/tanstack-query/root-provider.tsx b/project/frontend/src/integrations/tanstack-query/root-provider.tsx
new file mode 100644
index 0000000..2fb2aa9
--- /dev/null
+++ b/project/frontend/src/integrations/tanstack-query/root-provider.tsx
@@ -0,0 +1,15 @@
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+
+const queryClient = new QueryClient();
+
+export function getContext() {
+ return {
+ queryClient,
+ };
+}
+
+export function Provider({ children }: { children: React.ReactNode }) {
+ return (
+ {children}
+ );
+}
diff --git a/project/frontend/src/main.tsx b/project/frontend/src/main.tsx
new file mode 100644
index 0000000..ddffcc6
--- /dev/null
+++ b/project/frontend/src/main.tsx
@@ -0,0 +1,63 @@
+import CssBaseline from "@mui/material/CssBaseline";
+import { ThemeProvider, createTheme } from "@mui/material/styles";
+import { RouterProvider, createRouter } from "@tanstack/react-router";
+import { StrictMode } from "react";
+import ReactDOM from "react-dom/client";
+
+import "@fontsource/roboto/300.css";
+import "@fontsource/roboto/400.css";
+import "@fontsource/roboto/500.css";
+import "@fontsource/roboto/700.css";
+
+import * as TanStackQueryProvider from "./integrations/tanstack-query/root-provider.tsx";
+
+import { pdfjs } from "react-pdf";
+// Import the generated route tree
+import { routeTree } from "./routeTree.gen";
+
+// Create a new router instance
+const router = createRouter({
+ routeTree,
+ context: {
+ ...TanStackQueryProvider.getContext(),
+ },
+ defaultPreload: "intent",
+ scrollRestoration: true,
+ defaultStructuralSharing: true,
+ defaultPreloadStaleTime: 0,
+});
+
+// Register the router instance for type safety
+declare module "@tanstack/react-router" {
+ interface Register {
+ router: typeof router;
+ }
+}
+
+// Initialize PDF.js worker
+pdfjs.GlobalWorkerOptions.workerSrc = new URL(
+ "pdfjs-dist/build/pdf.worker.min.mjs",
+ import.meta.url,
+).toString();
+
+const darkTheme = createTheme({
+ palette: {
+ mode: "dark",
+ },
+});
+
+// Render the app
+const rootElement = document.getElementById("app");
+if (rootElement && !rootElement.innerHTML) {
+ const root = ReactDOM.createRoot(rootElement);
+ root.render(
+
+
+
+
+
+
+
+ ,
+ );
+}
diff --git a/project/frontend/src/routeTree.gen.ts b/project/frontend/src/routeTree.gen.ts
new file mode 100644
index 0000000..b19f273
--- /dev/null
+++ b/project/frontend/src/routeTree.gen.ts
@@ -0,0 +1,111 @@
+/* eslint-disable */
+
+// @ts-nocheck
+
+// noinspection JSUnusedGlobalSymbols
+
+// This file was automatically generated by TanStack Router.
+// You should NOT make any changes in this file as it will be overwritten.
+// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
+
+// Import Routes
+
+import { Route as rootRoute } from './routes/__root'
+import { Route as DemoImport } from './routes/demo'
+import { Route as IndexImport } from './routes/index'
+
+// Create/Update Routes
+
+const DemoRoute = DemoImport.update({
+ id: '/demo',
+ path: '/demo',
+ getParentRoute: () => rootRoute,
+} as any)
+
+const IndexRoute = IndexImport.update({
+ id: '/',
+ path: '/',
+ getParentRoute: () => rootRoute,
+} as any)
+
+// Populate the FileRoutesByPath interface
+
+declare module '@tanstack/react-router' {
+ interface FileRoutesByPath {
+ '/': {
+ id: '/'
+ path: '/'
+ fullPath: '/'
+ preLoaderRoute: typeof IndexImport
+ parentRoute: typeof rootRoute
+ }
+ '/demo': {
+ id: '/demo'
+ path: '/demo'
+ fullPath: '/demo'
+ preLoaderRoute: typeof DemoImport
+ parentRoute: typeof rootRoute
+ }
+ }
+}
+
+// Create and export the route tree
+
+export interface FileRoutesByFullPath {
+ '/': typeof IndexRoute
+ '/demo': typeof DemoRoute
+}
+
+export interface FileRoutesByTo {
+ '/': typeof IndexRoute
+ '/demo': typeof DemoRoute
+}
+
+export interface FileRoutesById {
+ __root__: typeof rootRoute
+ '/': typeof IndexRoute
+ '/demo': typeof DemoRoute
+}
+
+export interface FileRouteTypes {
+ fileRoutesByFullPath: FileRoutesByFullPath
+ fullPaths: '/' | '/demo'
+ fileRoutesByTo: FileRoutesByTo
+ to: '/' | '/demo'
+ id: '__root__' | '/' | '/demo'
+ fileRoutesById: FileRoutesById
+}
+
+export interface RootRouteChildren {
+ IndexRoute: typeof IndexRoute
+ DemoRoute: typeof DemoRoute
+}
+
+const rootRouteChildren: RootRouteChildren = {
+ IndexRoute: IndexRoute,
+ DemoRoute: DemoRoute,
+}
+
+export const routeTree = rootRoute
+ ._addFileChildren(rootRouteChildren)
+ ._addFileTypes()
+
+/* ROUTE_MANIFEST_START
+{
+ "routes": {
+ "__root__": {
+ "filePath": "__root.tsx",
+ "children": [
+ "/",
+ "/demo"
+ ]
+ },
+ "/": {
+ "filePath": "index.tsx"
+ },
+ "/demo": {
+ "filePath": "demo.tsx"
+ }
+ }
+}
+ROUTE_MANIFEST_END */
diff --git a/project/frontend/src/routes/__root.tsx b/project/frontend/src/routes/__root.tsx
new file mode 100644
index 0000000..0816bdd
--- /dev/null
+++ b/project/frontend/src/routes/__root.tsx
@@ -0,0 +1,25 @@
+import { Outlet, createRootRouteWithContext } from "@tanstack/react-router";
+import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
+
+// import Header from "../components/Header";
+
+import TanStackQueryLayout from "../integrations/tanstack-query/layout.tsx";
+
+import type { QueryClient } from "@tanstack/react-query";
+
+interface MyRouterContext {
+ queryClient: QueryClient;
+}
+
+export const Route = createRootRouteWithContext()({
+ component: () => (
+ <>
+ {/* */}
+
+
+
+
+
+ >
+ ),
+});
diff --git a/project/frontend/src/routes/demo.tsx b/project/frontend/src/routes/demo.tsx
new file mode 100644
index 0000000..8d1e906
--- /dev/null
+++ b/project/frontend/src/routes/demo.tsx
@@ -0,0 +1,26 @@
+import { useQuery } from "@tanstack/react-query";
+import { createFileRoute } from "@tanstack/react-router";
+
+export const Route = createFileRoute("/demo")({
+ component: TanStackQueryDemo,
+});
+
+function TanStackQueryDemo() {
+ const { data } = useQuery({
+ queryKey: ["people"],
+ queryFn: () =>
+ Promise.resolve([{ name: "John Doe" }, { name: "Jane Doe" }]),
+ initialData: [],
+ });
+
+ return (
+