Skip to content

Commit

Permalink
Merge pull request #334 from tgstation/RouterWork
Browse files Browse the repository at this point in the history
Setup Routing
  • Loading branch information
Cyberboss authored Oct 19, 2024
2 parents 9407e7a + 1339477 commit c0e1d32
Show file tree
Hide file tree
Showing 39 changed files with 797 additions and 345 deletions.
10 changes: 6 additions & 4 deletions .storybook/MockRelayEnvironment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ export type WithRelayParameters<TQuery extends OperationType, TResolvers = objec
*/
};

export let MockRelayEnvironment = createMockEnvironment();

const AddMockRelayEnvironment = makeDecorator({
name: "AddMockRelayEnvironment",
parameterName: "relay",
Expand All @@ -75,15 +77,15 @@ const AddMockRelayEnvironment = makeDecorator({
return getStory(context) as any;
};

const environment = createMockEnvironment();
MockRelayEnvironment = createMockEnvironment();

const resolver: OperationMockResolver = operation =>
MockPayloadGenerator.generate(operation, mockResolvers);
environment.mock.queueOperationResolver(resolver);
environment.mock.queuePendingOperation(query, variables);
MockRelayEnvironment.mock.queueOperationResolver(resolver);
MockRelayEnvironment.mock.queuePendingOperation(query, variables);

return (
<RelayEnvironmentProvider environment={environment}>
<RelayEnvironmentProvider environment={MockRelayEnvironment}>
<Renderer />
</RelayEnvironmentProvider>
);
Expand Down
3 changes: 2 additions & 1 deletion .storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Preview } from "@storybook/react";
import { withRouter } from "storybook-addon-remix-react-router";

import AddConfigDark from "./AddConfigDark";
import AddIntlEn from "./AddIntlEn";
Expand All @@ -15,7 +16,7 @@ const preview: Preview = {
}
}
},
decorators: [AddIntlEn, AddConfigDark, AddMockRelayEnvironment]
decorators: [AddIntlEn, AddConfigDark, AddMockRelayEnvironment, withRouter]
};

export default preview;
20 changes: 14 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"react-hook-form": "^7.53.0",
"react-intl": "^6.8.0",
"react-relay": "^18.1.0",
"react-router-dom": "^6.27.0",
"tailwind-merge": "^2.5.4",
"tailwindcss-animate": "^1.0.7",
"uuid": "^10.0.0",
Expand All @@ -70,13 +71,19 @@
"@chromatic-com/storybook": "2.0.2",
"@eslint/js": "^9.12.0",
"@rollup/pluginutils": "^5.1.2",
"@storybook/addon-essentials": "^8.3.5",
"@storybook/addon-interactions": "^8.3.5",
"@storybook/addon-essentials": "^8.3.6",
"@storybook/addon-interactions": "^8.3.6",
"@storybook/addon-links": "^8.3.5",
"@storybook/blocks": "^8.3.5",
"@storybook/react": "^8.3.5",
"@storybook/react-vite": "^8.3.5",
"@storybook/test": "^8.3.5",
"@storybook/channels": "^8.3.6",
"@storybook/components": "^8.3.6",
"@storybook/core-events": "^8.3.6",
"@storybook/manager-api": "^8.3.6",
"@storybook/preview-api": "^8.3.6",
"@storybook/react": "^8.3.6",
"@storybook/react-vite": "^8.3.6",
"@storybook/test": "^8.3.6",
"@storybook/theming": "^8.3.6",
"@types/node": "^22.7.6",
"@types/react": "^18",
"@types/react-dom": "^18.3.1",
Expand All @@ -99,7 +106,8 @@
"relay-compiler": "^18.1.0",
"relay-test-utils": "^18.1.0",
"rollup-plugin-jsx-remove-attributes": "^2.0.3",
"storybook": "^8.3.5",
"storybook": "^8.3.6",
"storybook-addon-remix-react-router": "^3.0.1",
"tailwindcss": "^3.4.14",
"type-fest": "^4.26.1",
"typescript": "^5.6.3",
Expand Down
13 changes: 8 additions & 5 deletions src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ import Locales from "../lib/translations/Locales";
import Loading from "./utils/Loading/Loading";
import icolibrary from "./utils/icolibrary";

const RelayEnvironment = lazy(async () => {
await devDelay();
return await import("@/components/core/RelayEnvironment/RelayEnvironment");
});
const Environment = lazy(
async () =>
await devDelay(
() => import("@/components/core/Environment/Environment"),
"Component Load: Environment"
)
);

interface IProps {
preferredLocales: readonly string[];
Expand Down Expand Up @@ -83,7 +86,7 @@ const App = (props: IProps) => {
</div>
</div>
}>
<RelayEnvironment />
<Environment />
</Suspense>
</IntlProvider>
) : (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { useContext, useEffect, useMemo } from "react";
import { useEffect, useMemo } from "react";
import { useIntl } from "react-intl";
import { RelayEnvironmentProvider } from "react-relay";

import Layout from "../Layout/Layout";
import Router from "../Router/Router";

import Pkg from "@/../package.json";
import ConfigContext from "@/context/config/Context";
import useConfig from "@/context/config/useConfig";
import ErrorsProvider from "@/context/errors/Provider";
import SessionProvider from "@/context/session/Provider";
import CreateRelayEnvironment from "@/lib/CreateRelayEnvironment";

const RelayEnvironment = () => {
const Environment = () => {
const version = Pkg.version;

const intl = useIntl();
Expand All @@ -19,7 +19,7 @@ const RelayEnvironment = () => {
document.title = intl.formatMessage({ id: "title" }, { version });
});

const config = useContext(ConfigContext);
const config = useConfig();

const { relayEnviroment, setCredentials } = useMemo(
() => CreateRelayEnvironment(config.ApiPath.value),
Expand All @@ -30,7 +30,7 @@ const RelayEnvironment = () => {
<RelayEnvironmentProvider environment={relayEnviroment}>
<SessionProvider setCredentials={credentials => setCredentials(credentials, false)}>
<ErrorsProvider>
<Layout
<Router
setTemporaryCredentials={credentials => setCredentials(credentials, true)}
/>
</ErrorsProvider>
Expand All @@ -39,4 +39,4 @@ const RelayEnvironment = () => {
);
};

export default RelayEnvironment;
export default Environment;
6 changes: 3 additions & 3 deletions src/components/core/ErrorViewer/ErrorViewer.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { Meta, StoryObj } from "@storybook/react";
import { expect, userEvent, waitFor, within } from "@storybook/test";
import { useContext, useEffect } from "react";
import { useEffect } from "react";

import ErrorViewer from "./ErrorViewer";

import { ErrorMessageSingleFragment$data } from "@/components/graphql/__generated__/ErrorMessageSingleFragment.graphql";
import ErrorsContext from "@/context/errors/Context";
import ErrorsProvider from "@/context/errors/Provider";
import useErrors from "@/context/errors/useErrors";
import sleep from "@/lib/sleep";

interface IArgs {
errors?: (ErrorMessageSingleFragment$data | Error)[];
}

const InnerTestComponent = (props: IArgs) => {
const context = useContext(ErrorsContext);
const context = useErrors();

useEffect(() => {
context.addErrors(props.errors ?? []);
Expand Down
6 changes: 2 additions & 4 deletions src/components/core/ErrorViewer/ErrorViewer.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { useContext } from "react";

import ErrorCard from "@/components/utils/ErrorCard/ErrorCard";
import ErrorsContext from "@/context/errors/Context";
import useErrors from "@/context/errors/useErrors";

const ErrorViewer = () => {
const context = useContext(ErrorsContext);
const context = useErrors();

return (
<>
Expand Down
27 changes: 14 additions & 13 deletions src/components/core/Layout/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,32 @@
import { Suspense } from "react";
import { Outlet } from "react-router-dom";

import ErrorViewer from "../ErrorViewer/ErrorViewer";
import Logo from "../Logo/Logo";
import Navbar from "../Navbar/Navbar";
import ReportIssue from "../ReportIssue/ReportIssue";
import Router from "../Router/Router";

import Loading from "@/components/utils/Loading/Loading";
import ErrorBoundary from "@/components/utils/ErrorBoundary/ErrorBoundary";
import { ICredentials } from "@/lib/Credentials";

interface IProps {
setTemporaryCredentials: (credentials: ICredentials) => void;
}

const Layout = (props: IProps) => {
const Layout = () => {
return (
<ErrorBoundary>
<>
<Navbar />
<div className="mt-20">
<div className="grid grid-cols-8">
<div className="col-start-3 col-end-7">
<div className="mt-20 grid grid-cols-8">
<div className="lg:col-start-2 lg:col-end-8">
<ErrorBoundary>
<ErrorViewer />
</div>
<Suspense fallback={<Loading />}>
<Outlet />
</Suspense>
</ErrorBoundary>
</div>
<Router setTemporaryCredentials={props.setTemporaryCredentials} />
</div>
<ReportIssue />
<Logo />
</ErrorBoundary>
</>
);
};

Expand Down
24 changes: 10 additions & 14 deletions src/components/core/Navbar/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,34 @@

import { Menu, X } from "lucide-react";
import { useState } from "react";
import { Link } from "react-router-dom";

import { Button } from "@/components/ui/button";
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";

const Navbar = () => {
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);

const navItems = [
{ name: "Home", href: "/" },
{ name: "About", href: "/about" },
{ name: "Services", href: "/services" },
{ name: "Contact", href: "/contact" }
];
const navItems = [{ name: "Configuration", href: "/config" }];

return (
<nav className="fixed top-0 left-0 right-0 z-50 bg-primary shadow-md">
<div className="container mx-1 px-4 sm:px-6 bg-primary shadow-md">
<div className="flex items-center justify-between h-16">
<div className="flex items-center">
<a href="/" className="text-xl font-bold text-white">
<Link to="/" className="text-xl font-bold text-white">
tgstation-server
</a>
</Link>
</div>
<div className="hidden md:block">
<div className="ml-10 flex items-baseline space-x-4">
{navItems.map(item => (
<a
<Link
key={item.name}
href={item.href}
to={item.href}
className="text-white hover:bg-primary hover:text-primary-foreground px-3 py-2 rounded-md text-sm font-medium transition-colors duration-200">
{item.name}
</a>
</Link>
))}
</div>
</div>
Expand All @@ -57,13 +53,13 @@ const Navbar = () => {
className="w-[240px] sm:w-[300px] bg-primary">
<nav className="flex flex-col space-y-4 mt-6">
{navItems.map(item => (
<a
<Link
key={item.name}
href={item.href}
to={item.href}
className="text-white hover:bg-primary hover:text-primary-foreground px-3 py-2 rounded-md text-base font-medium transition-colors duration-200"
onClick={() => setIsMobileMenuOpen(false)}>
{item.name}
</a>
</Link>
))}
</nav>
</SheetContent>
Expand Down
13 changes: 13 additions & 0 deletions src/components/core/NotFound/NotFound.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Meta, StoryObj } from "@storybook/react";

import NotFound from "./NotFound";
const config: Meta<typeof NotFound> = {
component: NotFound,
title: "Core/Not Found"
};

export default config;

type Story = StoryObj<typeof config>;

export const Default: Story = {};
28 changes: 28 additions & 0 deletions src/components/core/NotFound/NotFound.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { FormattedMessage } from "react-intl";

import Pkg from "@/../package.json";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";

const NotFound = () => (
<Card className="bg-transparent text-transparent-foreground mb-4">
<CardHeader>
<CardTitle>
<FormattedMessage id="error.somethingwentwrong" />
</CardTitle>
<CardDescription>
<FormattedMessage id="error.notfound" />
</CardDescription>
</CardHeader>
<CardContent>
<div className="bg-transparent text-danger">
<code className="block whitespace-pre-wrap" data-chromatic="ignore">
{`Webpanel Version: ${Pkg.version}\nWebpanel Mode: ${
import.meta.env.VITE_DEV_MODE ? "DEV" : "PROD"
}\nCurrent route: ${window.location.toString()}`}
</code>
</div>
</CardContent>
</Card>
);

export default NotFound;
17 changes: 17 additions & 0 deletions src/components/core/Router/ProtectedRoute/ProtectedRoute.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Navigate, Outlet, useLocation } from "react-router-dom";

import ILocationState from "@/components/routed/Login/LocationState";
import useSession from "@/context/session/useSession";

const ProtectedRoute = () => {
const session = useSession();
const location = useLocation();
if (session.currentSession == null) {
const state: ILocationState = { from: location };
return <Navigate to="/login" state={state} />;
}

return <Outlet />;
};

export default ProtectedRoute;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { useRouteError } from "react-router-dom";

const RethrowRouteError = () => {
const error = useRouteError();
throw error;
};

export default RethrowRouteError;
Loading

0 comments on commit c0e1d32

Please sign in to comment.