Routes added
This commit is contained in:
24
electron/package-lock.json
generated
24
electron/package-lock.json
generated
@@ -64,8 +64,10 @@
|
|||||||
"eslint-plugin-react-compiler": "^19.1.0-rc.2",
|
"eslint-plugin-react-compiler": "^19.1.0-rc.2",
|
||||||
"globals": "^16.3.0",
|
"globals": "^16.3.0",
|
||||||
"jsdom": "^26.1.0",
|
"jsdom": "^26.1.0",
|
||||||
|
"next-themes": "^0.4.6",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.6.2",
|
||||||
"prettier-plugin-tailwindcss": "^0.6.14",
|
"prettier-plugin-tailwindcss": "^0.6.14",
|
||||||
|
"sonner": "^2.0.7",
|
||||||
"tailwindcss": "^4.1.11",
|
"tailwindcss": "^4.1.11",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.9.2",
|
"typescript": "^5.9.2",
|
||||||
@@ -9690,6 +9692,17 @@
|
|||||||
"node": ">= 0.6"
|
"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": {
|
"node_modules/nice-try": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
|
||||||
@@ -11679,6 +11692,17 @@
|
|||||||
"seroval-plugins": "~1.3.0"
|
"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": {
|
"node_modules/source-map": {
|
||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
|
|||||||
@@ -55,8 +55,10 @@
|
|||||||
"eslint-plugin-react-compiler": "^19.1.0-rc.2",
|
"eslint-plugin-react-compiler": "^19.1.0-rc.2",
|
||||||
"globals": "^16.3.0",
|
"globals": "^16.3.0",
|
||||||
"jsdom": "^26.1.0",
|
"jsdom": "^26.1.0",
|
||||||
|
"next-themes": "^0.4.6",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.6.2",
|
||||||
"prettier-plugin-tailwindcss": "^0.6.14",
|
"prettier-plugin-tailwindcss": "^0.6.14",
|
||||||
|
"sonner": "^2.0.7",
|
||||||
"tailwindcss": "^4.1.11",
|
"tailwindcss": "^4.1.11",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.9.2",
|
"typescript": "^5.9.2",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import "./localization/i18n";
|
|||||||
import { updateAppLanguage } from "./helpers/language_helpers";
|
import { updateAppLanguage } from "./helpers/language_helpers";
|
||||||
import { router } from "./routes/router";
|
import { router } from "./routes/router";
|
||||||
import { RouterProvider } from "@tanstack/react-router";
|
import { RouterProvider } from "@tanstack/react-router";
|
||||||
|
import { Toaster } from "./components/ui/sonner";
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const { i18n } = useTranslation();
|
const { i18n } = useTranslation();
|
||||||
@@ -22,5 +23,6 @@ const root = createRoot(document.getElementById("app")!);
|
|||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
|
<Toaster />
|
||||||
</React.StrictMode>,
|
</React.StrictMode>,
|
||||||
);
|
);
|
||||||
|
|||||||
41
electron/src/components/Icon.tsx
Normal file
41
electron/src/components/Icon.tsx
Normal 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;
|
||||||
|
};
|
||||||
40
electron/src/components/OutsideCorner.tsx
Normal file
40
electron/src/components/OutsideCorner.tsx
Normal 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>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
44
electron/src/components/SidebarLayout.tsx
Normal file
44
electron/src/components/SidebarLayout.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
||||||
0
electron/src/components/Topbar.tsx
Normal file
0
electron/src/components/Topbar.tsx
Normal file
32
electron/src/components/mxicon.tsx
Normal file
32
electron/src/components/mxicon.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
17
electron/src/components/ui/sonner.tsx
Normal file
17
electron/src/components/ui/sonner.tsx
Normal 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 };
|
||||||
7
electron/src/lib/useOnSubpath.tsx
Normal file
7
electron/src/lib/useOnSubpath.tsx
Normal 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;
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import BaseLayout from "@/layouts/BaseLayout";
|
|
||||||
import { Outlet, createRootRoute } from "@tanstack/react-router";
|
import { Outlet, createRootRoute } from "@tanstack/react-router";
|
||||||
|
|
||||||
export const RootRoute = createRootRoute({
|
export const RootRoute = createRootRoute({
|
||||||
@@ -8,8 +7,10 @@ export const RootRoute = createRootRoute({
|
|||||||
|
|
||||||
function Root() {
|
function Root() {
|
||||||
return (
|
return (
|
||||||
<BaseLayout>
|
<>
|
||||||
|
<main className="h-screen pb-20">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</BaseLayout>
|
</main>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,6 @@ declare module "@tanstack/react-router" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const history = createMemoryHistory({
|
const history = createMemoryHistory({
|
||||||
initialEntries: ["/"],
|
initialEntries: ["/_sidebar/"],
|
||||||
});
|
});
|
||||||
export const router = createRouter({ routeTree: rootTree, history: history });
|
export const router = createRouter({ routeTree: rootTree, history: history });
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { createRoute } from "@tanstack/react-router";
|
import { createRoute } from "@tanstack/react-router";
|
||||||
import { RootRoute } from "./__root";
|
import { RootRoute } from "./__root";
|
||||||
import HomePage from "../pages/HomePage";
|
import { SidebarLayout } from "@/components/SidebarLayout";
|
||||||
import SecondPage from "@/pages/SecondPage";
|
import React from "react";
|
||||||
|
|
||||||
|
|
||||||
// TODO: Steps to add a new route:
|
// TODO: Steps to add a new route:
|
||||||
// 1. Create a new page component in the '../pages/' directory (e.g., NewPage.tsx)
|
// 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, ...])
|
// 4. Add to routeTree: RootRoute.addChildren([HomeRoute, NewRoute, ...])
|
||||||
// 5. Add Link: <Link to="/new">New Page</Link>
|
// 5. Add Link: <Link to="/new">New Page</Link>
|
||||||
|
|
||||||
export const HomeRoute = createRoute({
|
export const sidebarRoute = createRoute({
|
||||||
getParentRoute: () => RootRoute,
|
getParentRoute: () => RootRoute,
|
||||||
path: "/",
|
path: "_sidebar",
|
||||||
component: HomePage,
|
component:() => <SidebarLayout />,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const SecondPageRoute = createRoute({
|
export const rootTree = RootRoute.addChildren([sidebarRoute]);
|
||||||
getParentRoute: () => RootRoute,
|
|
||||||
path: "/second-page",
|
|
||||||
component: SecondPage,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const rootTree = RootRoute.addChildren([HomeRoute, SecondPageRoute]);
|
|
||||||
|
|||||||
Reference in New Issue
Block a user