Routes added

This commit is contained in:
Damian Wessels
2025-09-08 13:43:02 +02:00
parent fd33b5ceba
commit 6d1b65890d
13 changed files with 221 additions and 16 deletions

View File

@@ -64,8 +64,10 @@
"eslint-plugin-react-compiler": "^19.1.0-rc.2",
"globals": "^16.3.0",
"jsdom": "^26.1.0",
"next-themes": "^0.4.6",
"prettier": "^3.6.2",
"prettier-plugin-tailwindcss": "^0.6.14",
"sonner": "^2.0.7",
"tailwindcss": "^4.1.11",
"ts-node": "^10.9.2",
"typescript": "^5.9.2",
@@ -9690,6 +9692,17 @@
"node": ">= 0.6"
}
},
"node_modules/next-themes": {
"version": "0.4.6",
"resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz",
"integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc"
}
},
"node_modules/nice-try": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
@@ -11679,6 +11692,17 @@
"seroval-plugins": "~1.3.0"
}
},
"node_modules/sonner": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz",
"integrity": "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc",
"react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc"
}
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",

View File

@@ -55,8 +55,10 @@
"eslint-plugin-react-compiler": "^19.1.0-rc.2",
"globals": "^16.3.0",
"jsdom": "^26.1.0",
"next-themes": "^0.4.6",
"prettier": "^3.6.2",
"prettier-plugin-tailwindcss": "^0.6.14",
"sonner": "^2.0.7",
"tailwindcss": "^4.1.11",
"ts-node": "^10.9.2",
"typescript": "^5.9.2",

View File

@@ -6,6 +6,7 @@ import "./localization/i18n";
import { updateAppLanguage } from "./helpers/language_helpers";
import { router } from "./routes/router";
import { RouterProvider } from "@tanstack/react-router";
import { Toaster } from "./components/ui/sonner";
export default function App() {
const { i18n } = useTranslation();
@@ -22,5 +23,6 @@ const root = createRoot(document.getElementById("app")!);
root.render(
<React.StrictMode>
<App />
<Toaster />
</React.StrictMode>,
);

View File

@@ -0,0 +1,41 @@
import {icons as lucideIcons} from "lucide-react";
import React from "react";
import { qitechIcons } from "./mxicon";
type QitechIconName = `qi:${keyof typeof qitechIcons}`;
type LucideIconName = `lu:${keyof typeof lucideIcons}`;
// prefix keys with library to avoid conflicts with other icon libraries
export type IconName = QitechIconName | LucideIconName;
type Props = {
name?: IconName;
className?: string;
};
export const Icon = ({ name, className }: Props) => {
if (!name) {
return null;
}
const library = name.split(":")[0];
const rawIcon = name.split(":")[1];
if (library === "lu" && rawIcon in lucideIcons) {
const LucideIcon = lucideIcons[rawIcon as keyof typeof lucideIcons];
return <LucideIcon className={"size-6 " + className} />;
}
if (library === "qi" && rawIcon in qitechIcons) {
const QitechIcon = qitechIcons[rawIcon as keyof typeof qitechIcons];
return <QitechIcon className={"size-6 " + className} />;
}
console.error(`Icon ${name} not found`, library, rawIcon, lucideIcons);
return null;
};
export type IconNameMap = {
[key: string]: IconName;
};

View File

@@ -0,0 +1,40 @@
import React from "react";
export type OutsideCornerProps = {
rightTop?: boolean;
rightBottom?: boolean;
bottomLeft?: boolean;
bottomRight?: boolean;
};
export function OutsideCorner({
rightTop,
rightBottom,
bottomLeft,
bottomRight,
}: OutsideCornerProps) {
return (
<>
{rightTop && (
<div className="absolute -top-4 -right-0 z-0 bg-white">
<div className="h-4 w-4 rounded-br-full bg-neutral-200"></div>
</div>
)}
{rightBottom && (
<div className="absolute -right-0 -bottom-4 z-0 bg-white">
<div className="h-4 w-4 rounded-tr-full bg-neutral-200"></div>
</div>
)}
{bottomLeft && (
<div className="absolute bottom-0 -left-4 z-0 bg-white">
<div className="h-4 w-4 rounded-br-full bg-neutral-200"></div>
</div>
)}
{bottomRight && (
<div className="absolute -right-4 bottom-0 z-0 bg-white">
<div className="h-4 w-4 rounded-bl-full bg-neutral-200"></div>
</div>
)}
</>
);
}

View File

@@ -0,0 +1,44 @@
import {Icon, IconName} from "./Icon";
import { useOnSubpath } from "@/lib/useOnSubpath";
import { OutsideCorner } from "./OutsideCorner";
import {Link} from "@tanstack/react-router";
import React from "react";
type SideBarItemContent = {
link: string;
activeLink: string;
icon?: IconName;
title: string;
}
type SidebarItemProps = SideBarItemContent & {
isFirst: boolean;
};
export function SidebarItem({
link,
icon,
title,
isFirst,
activeLink
}: SidebarItemProps) {
const isActive = useOnSubpath(activeLink);
return (
<Link
to={link}
classname={`relative h-18 w-full ${isActive ? "pl-2": "px-2"}`}
>
<div className={`text-md relative z-10 flex h-full w-full items-center justify-center gap-2 ${isActive ? "rounded-l-lg bg-white pr-2": "rounded-lg bg-neutral-100"}`}>
{icon && <Icon name={icon} />}
{title}
</div>
{isActive && <OutsideCorner rightTop={!isFirst} rightBottom={true} />}
</Link>
);
}
export function SidebarLayout() {
return (
<h1>Hello World</h1>
)
}

View File

View File

@@ -0,0 +1,32 @@
import React from "react";
export const qitechIcons = {
Extruder: Extruder,
};
function Extruder({ className }: { className?: string }) {
return (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M2 17V15L6.97707 12.2495C7.27322 12.0859 7.60606 12 7.94444 12H20C21.1046 12 22 12.8954 22 14V18C22 19.1046 21.1046 20 20 20H7.94444C7.60606 20 7.27322 19.9141 6.97707 19.7505L2 17Z"
stroke="black"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M14 12V9.16205C14 8.92811 13.918 8.70158 13.7682 8.52187L11.3668 5.64018C10.824 4.98886 11.2872 4 12.135 4H19.865C20.7128 4 21.176 4.98886 20.6332 5.64018L18.2318 8.52187C18.082 8.70158 18 8.92811 18 9.16205V12"
stroke="black"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}

View File

@@ -0,0 +1,17 @@
import React from "react";
import { useTheme } from "next-themes";
import {Toaster as Sonner, ToasterProps } from "sonner";
const Toaster = ({...props}: ToasterProps) => {
const { theme = "system" } = useTheme();
return (
<Sonner
theme={theme as ToasterProps["theme"]}
className="toaster group"
{...props}
/>
);
};
export { Toaster };

View File

@@ -0,0 +1,7 @@
import { useRouterState } from "@tanstack/react-router";
export function useOnSubpath(path: string) {
const {location } = useRouterState();
const onSubpath = location.pathname.startsWith(path);
return onSubpath;
}

View File

@@ -1,5 +1,4 @@
import React from "react";
import BaseLayout from "@/layouts/BaseLayout";
import { Outlet, createRootRoute } from "@tanstack/react-router";
export const RootRoute = createRootRoute({
@@ -8,8 +7,10 @@ export const RootRoute = createRootRoute({
function Root() {
return (
<BaseLayout>
<>
<main className="h-screen pb-20">
<Outlet />
</BaseLayout>
</main>
</>
);
}

View File

@@ -8,6 +8,6 @@ declare module "@tanstack/react-router" {
}
const history = createMemoryHistory({
initialEntries: ["/"],
initialEntries: ["/_sidebar/"],
});
export const router = createRouter({ routeTree: rootTree, history: history });

View File

@@ -1,7 +1,8 @@
import { createRoute } from "@tanstack/react-router";
import { RootRoute } from "./__root";
import HomePage from "../pages/HomePage";
import SecondPage from "@/pages/SecondPage";
import { SidebarLayout } from "@/components/SidebarLayout";
import React from "react";
// TODO: Steps to add a new route:
// 1. Create a new page component in the '../pages/' directory (e.g., NewPage.tsx)
@@ -22,16 +23,10 @@ import SecondPage from "@/pages/SecondPage";
// 4. Add to routeTree: RootRoute.addChildren([HomeRoute, NewRoute, ...])
// 5. Add Link: <Link to="/new">New Page</Link>
export const HomeRoute = createRoute({
export const sidebarRoute = createRoute({
getParentRoute: () => RootRoute,
path: "/",
component: HomePage,
path: "_sidebar",
component:() => <SidebarLayout />,
});
export const SecondPageRoute = createRoute({
getParentRoute: () => RootRoute,
path: "/second-page",
component: SecondPage,
});
export const rootTree = RootRoute.addChildren([HomeRoute, SecondPageRoute]);
export const rootTree = RootRoute.addChildren([sidebarRoute]);