diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 113022f4..acb33816 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -59,6 +59,7 @@ "react-markdown": "^9.0.1", "react-masonry-css": "^1.0.16", "react-router-dom": "6.25.1", + "react-router-typesafe-routes": "^1.2.2", "react-use-websocket": "^4.8.1", "remark-gfm": "^4.0.0", "tailwind-merge": "^2.5.2", @@ -10001,6 +10002,36 @@ "react-dom": ">=16.8" } }, + "node_modules/react-router-typesafe-routes": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/react-router-typesafe-routes/-/react-router-typesafe-routes-1.2.2.tgz", + "integrity": "sha512-aUTUO0XzEhevQTNACv82SJVRbUMnGCrQIecRwKWLRNt2oKXs01kW4nIJbRV8xwAvv9PR4WKM2Iifd84DiOr73w==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": ">=16.8", + "react-router-dom": "^6.7.0", + "react-router-native": "^6.7.0", + "yup": "^1.0.0", + "zod": "^3.0.0" + }, + "peerDependenciesMeta": { + "react-router-dom": { + "optional": true + }, + "react-router-native": { + "optional": true + }, + "yup": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, "node_modules/react-side-effect": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 35aa2bb7..17849b3b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -74,6 +74,7 @@ "react-markdown": "^9.0.1", "react-masonry-css": "^1.0.16", "react-router-dom": "6.25.1", + "react-router-typesafe-routes": "^1.2.2", "react-use-websocket": "^4.8.1", "remark-gfm": "^4.0.0", "tailwind-merge": "^2.5.2", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 5807698b..040e3452 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -8,6 +8,7 @@ import PendoInitializer from "@/components/PendoInitializer"; import { ScrollToTop } from "@/components/ScrollToTop"; import SprigInitializer from "@/components/SprigInitializer"; import Footer from "@/components/footer/Footer"; +import GDPRBanner from "@/components/gdpr/gdprbanner"; import { FeaturedListingsProvider } from "@/components/listing/FeaturedListings"; import Navbar from "@/components/nav/Navbar"; import APIKeys from "@/components/pages/APIKeys"; @@ -15,6 +16,8 @@ import About from "@/components/pages/About"; import Account from "@/components/pages/Account"; import Browse from "@/components/pages/Browse"; import Create from "@/components/pages/Create"; +import DeleteConnect from "@/components/pages/DeleteConnect"; +import DownloadsPage from "@/components/pages/Download"; import EmailSignup from "@/components/pages/EmailSignup"; import FileBrowser from "@/components/pages/FileBrowser"; import Home from "@/components/pages/Home"; @@ -22,23 +25,20 @@ import Listing from "@/components/pages/Listing"; import Login from "@/components/pages/Login"; import Logout from "@/components/pages/Logout"; import NotFound from "@/components/pages/NotFound"; +import OrderSuccess from "@/components/pages/OrderSuccess"; +import OrdersPage from "@/components/pages/Orders"; +import Playground from "@/components/pages/Playground"; +import PrivacyPolicy from "@/components/pages/PrivacyPolicy"; import Profile from "@/components/pages/Profile"; +import ResearchPage from "@/components/pages/ResearchPage"; import SellerDashboard from "@/components/pages/SellerDashboard"; +import SellerOnboarding from "@/components/pages/SellerOnboarding"; import Signup from "@/components/pages/Signup"; +import Terminal from "@/components/pages/Terminal"; +import TermsOfService from "@/components/pages/TermsOfService"; import { AlertQueue, AlertQueueProvider } from "@/hooks/useAlertQueue"; import { AuthenticationProvider } from "@/hooks/useAuth"; - -import GDPRBanner from "./components/gdpr/gdprbanner"; -import DeleteConnect from "./components/pages/DeleteConnect"; -import DownloadsPage from "./components/pages/Download"; -import OrderSuccess from "./components/pages/OrderSuccess"; -import OrdersPage from "./components/pages/Orders"; -import Playground from "./components/pages/Playground"; -import PrivacyPolicy from "./components/pages/PrivacyPolicy"; -import ResearchPage from "./components/pages/ResearchPage"; -import SellerOnboarding from "./components/pages/SellerOnboarding"; -import Terminal from "./components/pages/Terminal"; -import TermsOfService from "./components/pages/TermsOfService"; +import ROUTES from "@/lib/types/routes"; const App = () => { return ( @@ -56,55 +56,113 @@ const App = () => {
- } /> + } /> - } /> + {/* Playground */} + } + /> - } /> - } /> - } /> - } /> + {/* General pages */} + } /> } + path={ROUTES.DOWNLOADS.path} + element={} + /> + } + /> + } + /> + } + /> + + {/* Account */} + } + /> + } /> + } /> + } /> + } + /> + } /> + } /> - } /> - } /> - } /> - } /> - } /> - } /> + {/* Listings */} + + } + /> + } + /> + } /> - } /> - } /> + } + /> - } /> - } /> + {/* Seller */} + } + > + } + /> + } + /> + + {/* Orders */} + + } + /> + } + path={ROUTES.ORDERS.path} + element={} /> + + {/* Terminal */} } + path={ROUTES.TERMINAL.path} + element={} /> } + path={ROUTES.TERMINAL.WITH_ID.path} + element={} /> - } /> - } /> - - } /> - } /> - - } /> + {/* Not found */} + } + /> } /> diff --git a/frontend/src/components/auth/SignupForm.tsx b/frontend/src/components/auth/SignupForm.tsx index f0654457..d002d06b 100644 --- a/frontend/src/components/auth/SignupForm.tsx +++ b/frontend/src/components/auth/SignupForm.tsx @@ -8,6 +8,7 @@ import { Button } from "@/components/ui/button"; import { useAlertQueue } from "@/hooks/useAlertQueue"; import { useAuthentication } from "@/hooks/useAuth"; import { SignUpSchema, SignupType } from "@/lib/types"; +import ROUTES from "@/lib/types/routes"; import { zodResolver } from "@hookform/resolvers/zod"; import zxcvbn from "zxcvbn"; @@ -56,7 +57,7 @@ const SignupForm: React.FC = ({ signupTokenId }) => { addErrorAlert(error); } else { addAlert("Registration successful! You can now log in.", "success"); - navigate("/login"); + navigate(ROUTES.LOGIN.path); // Sign user in automatically? } } catch (err) { @@ -93,11 +94,11 @@ const SignupForm: React.FC = ({ signupTokenId }) => { {/* TOS Text */}
By signing up, you agree to our
- + terms and conditions {" "} and{" "} - + privacy policy . diff --git a/frontend/src/components/footer/Footer.tsx b/frontend/src/components/footer/Footer.tsx index 7c0ed791..3c607e74 100644 --- a/frontend/src/components/footer/Footer.tsx +++ b/frontend/src/components/footer/Footer.tsx @@ -4,6 +4,7 @@ import { Link, useLocation } from "react-router-dom"; import Logo from "@/components/Logo"; import SocialLink from "@/components/footer/SocialLink"; +import ROUTES from "@/lib/types/routes"; const Footer = () => { const location = useLocation(); @@ -64,19 +65,19 @@ const Footer = () => {

Company

- + About - + Blog

Legal

- + Terms of Service - + Privacy Policy
diff --git a/frontend/src/components/listing/ListingArtifactRenderer.tsx b/frontend/src/components/listing/ListingArtifactRenderer.tsx index 6cebbc49..1f316614 100644 --- a/frontend/src/components/listing/ListingArtifactRenderer.tsx +++ b/frontend/src/components/listing/ListingArtifactRenderer.tsx @@ -3,6 +3,7 @@ import { Link } from "react-router-dom"; import placeholder from "@/components/listing/pics/placeholder.jpg"; import { Artifact } from "@/components/listing/types"; +import ROUTES from "@/lib/types/routes"; interface Props { artifact: Artifact; @@ -22,7 +23,7 @@ const ListingArtifactRenderer = ({ artifact }: Props) => { return (
diff --git a/frontend/src/components/listing/ListingDeleteButton.tsx b/frontend/src/components/listing/ListingDeleteButton.tsx index 43f1a0fd..8919c43a 100644 --- a/frontend/src/components/listing/ListingDeleteButton.tsx +++ b/frontend/src/components/listing/ListingDeleteButton.tsx @@ -6,6 +6,7 @@ import Modal from "@/components/ui/Modal"; import { Button } from "@/components/ui/button"; import { useAlertQueue } from "@/hooks/useAlertQueue"; import { useAuthentication } from "@/hooks/useAuth"; +import ROUTES from "@/lib/types/routes"; interface Props { listingId: string; @@ -37,7 +38,7 @@ const ListingDeleteButton = (props: Props) => { setDeleting(false); } else { addAlert("Listing was deleted successfully", "success"); - navigate("/browse"); + navigate(ROUTES.LISTINGS.BROWSE.path); } }; diff --git a/frontend/src/components/listing/ListingMetadata.tsx b/frontend/src/components/listing/ListingMetadata.tsx index a86858a6..45c6ffff 100644 --- a/frontend/src/components/listing/ListingMetadata.tsx +++ b/frontend/src/components/listing/ListingMetadata.tsx @@ -10,6 +10,7 @@ import { useNavigate } from "react-router-dom"; import { useAlertQueue } from "@/hooks/useAlertQueue"; import { useAuthentication } from "@/hooks/useAuth"; +import ROUTES from "@/lib/types/routes"; interface Props { listingId: string; @@ -110,7 +111,7 @@ const ListingMetadata = ({
diff --git a/frontend/src/components/pages/Profile.tsx b/frontend/src/components/pages/Profile.tsx index a4460c14..0720e951 100644 --- a/frontend/src/components/pages/Profile.tsx +++ b/frontend/src/components/pages/Profile.tsx @@ -1,6 +1,8 @@ import { useEffect, useState } from "react"; -import { useNavigate, useParams } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; +import { useTypedParams } from "react-router-typesafe-routes/dom"; +import MyListingGrid from "@/components/listings/MyListingGrid"; import UpvotedGrid from "@/components/listings/UpvotedGrid"; import { Card, CardContent, CardHeader } from "@/components/ui/Card"; import { Input, TextArea } from "@/components/ui/Input/Input"; @@ -11,12 +13,11 @@ import { paths } from "@/gen/api"; import { useAlertQueue } from "@/hooks/useAlertQueue"; import { useAuthentication } from "@/hooks/useAuth"; import { useDebounce } from "@/hooks/useDebounce"; +import ROUTES from "@/lib/types/routes"; import { isValidUsername } from "@/lib/utils/validation"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@radix-ui/react-tabs"; import { format } from "date-fns"; -import MyListingGrid from "../listings/MyListingGrid"; - type UserResponse = paths["/users/public/{id}"]["get"]["responses"][200]["content"]["application/json"]; @@ -214,7 +215,10 @@ export const RenderProfile = (props: RenderProfileProps) => {
{!isEditing && canEdit && (
-
- {!user.stripe_connect_account_id ? (