Migrated to react router v7

This commit is contained in:
jackiettran
2026-01-19 22:50:53 -05:00
parent 1923ffc251
commit 28554acc2d
35 changed files with 180 additions and 284 deletions

View File

@@ -20,14 +20,13 @@
"@types/node": "^20.0.0", "@types/node": "^20.0.0",
"@types/react": "^19.1.8", "@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6", "@types/react-dom": "^19.1.6",
"@types/react-router-dom": "^5.3.3",
"axios": "^1.10.0", "axios": "^1.10.0",
"bootstrap": "^5.3.7", "bootstrap": "^5.3.7",
"browser-image-compression": "^2.0.2", "browser-image-compression": "^2.0.2",
"react": "^19.1.0", "react": "^19.1.0",
"react-datepicker": "^9.1.0", "react-datepicker": "^9.1.0",
"react-dom": "^19.1.0", "react-dom": "^19.1.0",
"react-router-dom": "^6.30.1", "react-router": "^7.12.0",
"socket.io-client": "^4.8.1", "socket.io-client": "^4.8.1",
"stripe": "^18.4.0", "stripe": "^18.4.0",
"typescript": "^4.9.5" "typescript": "^4.9.5"
@@ -1271,15 +1270,6 @@
"url": "https://opencollective.com/popperjs" "url": "https://opencollective.com/popperjs"
} }
}, },
"node_modules/@remix-run/router": {
"version": "1.23.2",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz",
"integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==",
"license": "MIT",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@rolldown/pluginutils": { "node_modules/@rolldown/pluginutils": {
"version": "1.0.0-beta.27", "version": "1.0.0-beta.27",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
@@ -1860,12 +1850,6 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/history": {
"version": "4.7.11",
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz",
"integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==",
"license": "MIT"
},
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.19.30", "version": "20.19.30",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz",
@@ -1893,27 +1877,6 @@
"@types/react": "^19.2.0" "@types/react": "^19.2.0"
} }
}, },
"node_modules/@types/react-router": {
"version": "5.1.20",
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz",
"integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==",
"license": "MIT",
"dependencies": {
"@types/history": "^4.7.11",
"@types/react": "*"
}
},
"node_modules/@types/react-router-dom": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz",
"integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==",
"license": "MIT",
"dependencies": {
"@types/history": "^4.7.11",
"@types/react": "*",
"@types/react-router": "*"
}
},
"node_modules/@types/statuses": { "node_modules/@types/statuses": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.6.tgz", "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.6.tgz",
@@ -2384,7 +2347,6 @@
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
"integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=18" "node": ">=18"
@@ -3692,35 +3654,25 @@
} }
}, },
"node_modules/react-router": { "node_modules/react-router": {
"version": "6.30.3", "version": "7.12.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz", "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.12.0.tgz",
"integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==", "integrity": "sha512-kTPDYPFzDVGIIGNLS5VJykK0HfHLY5MF3b+xj0/tTyNYL1gF1qs7u67Z9jEhQk2sQ98SUaHxlG31g1JtF7IfVw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@remix-run/router": "1.23.2" "cookie": "^1.0.1",
"set-cookie-parser": "^2.6.0"
}, },
"engines": { "engines": {
"node": ">=14.0.0" "node": ">=20.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"react": ">=16.8" "react": ">=18",
} "react-dom": ">=18"
},
"node_modules/react-router-dom": {
"version": "6.30.3",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz",
"integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==",
"license": "MIT",
"dependencies": {
"@remix-run/router": "1.23.2",
"react-router": "6.30.3"
}, },
"engines": { "peerDependenciesMeta": {
"node": ">=14.0.0" "react-dom": {
}, "optional": true
"peerDependencies": { }
"react": ">=16.8",
"react-dom": ">=16.8"
} }
}, },
"node_modules/redent": { "node_modules/redent": {
@@ -3837,6 +3789,12 @@
"semver": "bin/semver.js" "semver": "bin/semver.js"
} }
}, },
"node_modules/set-cookie-parser": {
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
"integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
"license": "MIT"
},
"node_modules/side-channel": { "node_modules/side-channel": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",

View File

@@ -16,14 +16,13 @@
"@types/node": "^20.0.0", "@types/node": "^20.0.0",
"@types/react": "^19.1.8", "@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6", "@types/react-dom": "^19.1.6",
"@types/react-router-dom": "^5.3.3",
"axios": "^1.10.0", "axios": "^1.10.0",
"bootstrap": "^5.3.7", "bootstrap": "^5.3.7",
"browser-image-compression": "^2.0.2", "browser-image-compression": "^2.0.2",
"react": "^19.1.0", "react": "^19.1.0",
"react-datepicker": "^9.1.0", "react-datepicker": "^9.1.0",
"react-dom": "^19.1.0", "react-dom": "^19.1.0",
"react-router-dom": "^6.30.1", "react-router": "^7.12.0",
"socket.io-client": "^4.8.1", "socket.io-client": "^4.8.1",
"stripe": "^18.4.0", "stripe": "^18.4.0",
"typescript": "^4.9.5" "typescript": "^4.9.5"

View File

@@ -1,13 +1,10 @@
import React, { useState, useEffect, useCallback } from 'react'; import React, { useState, useEffect } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; import { createBrowserRouter, RouterProvider } from 'react-router';
import { AuthProvider, useAuth } from './contexts/AuthContext'; import { AuthProvider, useAuth } from './contexts/AuthContext';
import { SocketProvider } from './contexts/SocketContext'; import { SocketProvider } from './contexts/SocketContext';
import Navbar from './components/Navbar'; import RootLayout from './components/RootLayout';
import Footer from './components/Footer'; import ProtectedLayout from './components/ProtectedLayout';
import AuthModal from './components/AuthModal';
import AlphaGate from './components/AlphaGate'; import AlphaGate from './components/AlphaGate';
import FeedbackButton from './components/FeedbackButton';
import { TwoFactorVerifyModal } from './components/TwoFactor';
import Home from './pages/Home'; import Home from './pages/Home';
import GoogleCallback from './pages/GoogleCallback'; import GoogleCallback from './pages/GoogleCallback';
import VerifyEmail from './pages/VerifyEmail'; import VerifyEmail from './pages/VerifyEmail';
@@ -30,50 +27,56 @@ import EarningsDashboard from './pages/EarningsDashboard';
import CompletePayment from './pages/CompletePayment'; import CompletePayment from './pages/CompletePayment';
import FAQ from './pages/FAQ'; import FAQ from './pages/FAQ';
import NotFound from './pages/NotFound'; import NotFound from './pages/NotFound';
import PrivateRoute from './components/PrivateRoute';
import axios from 'axios'; import axios from 'axios';
import './App.css'; import './App.css';
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:5001'; const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:5001';
const router = createBrowserRouter([
{
element: <RootLayout />,
children: [
// Public routes
{ path: '/', element: <Home /> },
{ path: '/auth/google/callback', element: <GoogleCallback /> },
{ path: '/verify-email', element: <VerifyEmail /> },
{ path: '/reset-password', element: <ResetPassword /> },
{ path: '/items', element: <ItemList /> },
{ path: '/items/:id', element: <ItemDetail /> },
{ path: '/users/:id', element: <PublicProfile /> },
{ path: '/forum', element: <ForumPosts /> },
{ path: '/forum/:id', element: <ForumPostDetail /> },
{ path: '/faq', element: <FAQ /> },
// Protected routes group
{
element: <ProtectedLayout />,
children: [
{ path: '/items/:id/edit', element: <EditItem /> },
{ path: '/items/:id/rent', element: <RentItem /> },
{ path: '/create-item', element: <CreateItem /> },
{ path: '/renting', element: <Renting /> },
{ path: '/complete-payment/:rentalId', element: <CompletePayment /> },
{ path: '/owning', element: <Owning /> },
{ path: '/profile', element: <Profile /> },
{ path: '/messages', element: <Messages /> },
{ path: '/forum/create', element: <CreateForumPost /> },
{ path: '/forum/:id/edit', element: <CreateForumPost /> },
{ path: '/my-posts', element: <MyPosts /> },
{ path: '/earnings', element: <EarningsDashboard /> },
],
},
// Catch-all route
{ path: '*', element: <NotFound /> },
],
},
]);
const AppContent: React.FC = () => { const AppContent: React.FC = () => {
const { showAuthModal, authModalMode, closeAuthModal, user } = useAuth();
const [hasAlphaAccess, setHasAlphaAccess] = useState<boolean | null>(null); const [hasAlphaAccess, setHasAlphaAccess] = useState<boolean | null>(null);
const [checkingAccess, setCheckingAccess] = useState(true); const [checkingAccess, setCheckingAccess] = useState(true);
// Step-up authentication state
const [showStepUpModal, setShowStepUpModal] = useState(false);
const [stepUpAction, setStepUpAction] = useState<string | undefined>();
const [stepUpMethods, setStepUpMethods] = useState<("totp" | "email" | "recovery")[]>([]);
// Listen for step-up authentication required events
useEffect(() => {
const handleStepUpRequired = (event: CustomEvent) => {
const { action, methods } = event.detail;
setStepUpAction(action);
setStepUpMethods(methods || ["totp", "email", "recovery"]);
setShowStepUpModal(true);
};
window.addEventListener("stepUpRequired", handleStepUpRequired as EventListener);
return () => {
window.removeEventListener("stepUpRequired", handleStepUpRequired as EventListener);
};
}, []);
const handleStepUpSuccess = useCallback(() => {
setShowStepUpModal(false);
setStepUpAction(undefined);
// Dispatch event so pending actions can auto-retry
window.dispatchEvent(new CustomEvent("stepUpSuccess"));
}, []);
const handleStepUpClose = useCallback(() => {
setShowStepUpModal(false);
setStepUpAction(undefined);
}, []);
useEffect(() => { useEffect(() => {
const checkAlphaAccess = async () => { const checkAlphaAccess = async () => {
// Bypass alpha access check if feature is disabled // Bypass alpha access check if feature is disabled
@@ -115,145 +118,7 @@ const AppContent: React.FC = () => {
return <AlphaGate />; return <AlphaGate />;
} }
return ( return <RouterProvider router={router} />;
<>
<Router>
<div className="d-flex flex-column min-vh-100">
<Navbar />
<main className="flex-grow-1">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/auth/google/callback" element={<GoogleCallback />} />
<Route path="/verify-email" element={<VerifyEmail />} />
<Route path="/reset-password" element={<ResetPassword />} />
<Route path="/items" element={<ItemList />} />
<Route path="/items/:id" element={<ItemDetail />} />
<Route path="/users/:id" element={<PublicProfile />} />
<Route
path="/items/:id/edit"
element={
<PrivateRoute>
<EditItem />
</PrivateRoute>
}
/>
<Route
path="/items/:id/rent"
element={
<PrivateRoute>
<RentItem />
</PrivateRoute>
}
/>
<Route
path="/create-item"
element={
<PrivateRoute>
<CreateItem />
</PrivateRoute>
}
/>
<Route
path="/renting"
element={
<PrivateRoute>
<Renting />
</PrivateRoute>
}
/>
<Route
path="/complete-payment/:rentalId"
element={
<PrivateRoute>
<CompletePayment />
</PrivateRoute>
}
/>
<Route
path="/owning"
element={
<PrivateRoute>
<Owning />
</PrivateRoute>
}
/>
<Route
path="/profile"
element={
<PrivateRoute>
<Profile />
</PrivateRoute>
}
/>
<Route
path="/messages"
element={
<PrivateRoute>
<Messages />
</PrivateRoute>
}
/>
<Route path="/forum" element={<ForumPosts />} />
<Route path="/forum/:id" element={<ForumPostDetail />} />
<Route
path="/forum/create"
element={
<PrivateRoute>
<CreateForumPost />
</PrivateRoute>
}
/>
<Route
path="/forum/:id/edit"
element={
<PrivateRoute>
<CreateForumPost />
</PrivateRoute>
}
/>
<Route
path="/my-posts"
element={
<PrivateRoute>
<MyPosts />
</PrivateRoute>
}
/>
<Route
path="/earnings"
element={
<PrivateRoute>
<EarningsDashboard />
</PrivateRoute>
}
/>
<Route path="/faq" element={<FAQ />} />
<Route path="*" element={<NotFound />} />
</Routes>
</main>
<Footer />
</div>
</Router>
<AuthModal
show={showAuthModal}
onHide={closeAuthModal}
initialMode={authModalMode}
/>
{/* Show feedback button for authenticated users */}
{user && <FeedbackButton />}
{/* Global Step-Up Authentication Modal */}
<TwoFactorVerifyModal
show={showStepUpModal}
onHide={handleStepUpClose}
onSuccess={handleStepUpSuccess}
action={stepUpAction}
methods={stepUpMethods}
/>
</>
);
}; };
const AppWithSocket: React.FC = () => { const AppWithSocket: React.FC = () => {

View File

@@ -7,7 +7,7 @@
import React from 'react'; import React from 'react';
import { render, screen } from '@testing-library/react'; import { render, screen } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom'; import { BrowserRouter } from 'react-router';
import { vi, type MockedFunction } from 'vitest'; import { vi, type MockedFunction } from 'vitest';
import ItemCard from '../../components/ItemCard'; import ItemCard from '../../components/ItemCard';
import { Item } from '../../types'; import { Item } from '../../types';

View File

@@ -7,7 +7,7 @@
import React from 'react'; import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react'; import { render, screen, fireEvent } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom'; import { BrowserRouter } from 'react-router';
import { vi, type Mock } from 'vitest'; import { vi, type Mock } from 'vitest';
import Navbar from '../../components/Navbar'; import Navbar from '../../components/Navbar';
import { rentalAPI, messageAPI } from '../../services/api'; import { rentalAPI, messageAPI } from '../../services/api';
@@ -45,8 +45,8 @@ vi.mock('../../contexts/AuthContext', () => ({
// Mock useNavigate // Mock useNavigate
const mockNavigate = vi.fn(); const mockNavigate = vi.fn();
vi.mock('react-router-dom', async () => { vi.mock('react-router', async () => {
const actual = await vi.importActual('react-router-dom'); const actual = await vi.importActual('react-router');
return { return {
...actual, ...actual,
useNavigate: () => mockNavigate, useNavigate: () => mockNavigate,

View File

@@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router';
const Footer: React.FC = () => { const Footer: React.FC = () => {
return ( return (

View File

@@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router";
import { ForumPost } from "../types"; import { ForumPost } from "../types";
import CategoryBadge from "./CategoryBadge"; import CategoryBadge from "./CategoryBadge";
import PostStatusBadge from "./PostStatusBadge"; import PostStatusBadge from "./PostStatusBadge";

View File

@@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router';
import { Item } from '../types'; import { Item } from '../types';
import { getImageUrl } from '../services/uploadService'; import { getImageUrl } from '../services/uploadService';

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router";
import { Rental } from "../types"; import { Rental } from "../types";
import { itemAPI } from "../services/api"; import { itemAPI } from "../services/api";
import Avatar from "./Avatar"; import Avatar from "./Avatar";

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect, useRef, useCallback } from "react"; import React, { useState, useEffect, useRef, useCallback } from "react";
import { Link, useNavigate, useLocation } from "react-router-dom"; import { Link, useNavigate, useLocation } from "react-router";
import { useAuth } from "../contexts/AuthContext"; import { useAuth } from "../contexts/AuthContext";
import { useSocket } from "../contexts/SocketContext"; import { useSocket } from "../contexts/SocketContext";
import { rentalAPI, messageAPI } from "../services/api"; import { rentalAPI, messageAPI } from "../services/api";

View File

@@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router";
interface PricingFormProps { interface PricingFormProps {
pricePerHour: number | string; pricePerHour: number | string;

View File

@@ -1,11 +1,8 @@
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { Outlet } from "react-router";
import { useAuth } from "../contexts/AuthContext"; import { useAuth } from "../contexts/AuthContext";
interface PrivateRouteProps { const ProtectedLayout: React.FC = () => {
children: React.ReactNode;
}
const PrivateRoute: React.FC<PrivateRouteProps> = ({ children }) => {
const { user, loading, openAuthModal } = useAuth(); const { user, loading, openAuthModal } = useAuth();
useEffect(() => { useEffect(() => {
@@ -41,7 +38,7 @@ const PrivateRoute: React.FC<PrivateRouteProps> = ({ children }) => {
); );
} }
return <>{children}</>; return <Outlet />;
}; };
export default PrivateRoute; export default ProtectedLayout;

View File

@@ -0,0 +1,77 @@
import React, { useState, useEffect, useCallback } from "react";
import { Outlet } from "react-router";
import { useAuth } from "../contexts/AuthContext";
import Navbar from "./Navbar";
import Footer from "./Footer";
import AuthModal from "./AuthModal";
import FeedbackButton from "./FeedbackButton";
import { TwoFactorVerifyModal } from "./TwoFactor";
const RootLayout: React.FC = () => {
const { showAuthModal, authModalMode, closeAuthModal, user } = useAuth();
// Step-up authentication state
const [showStepUpModal, setShowStepUpModal] = useState(false);
const [stepUpAction, setStepUpAction] = useState<string | undefined>();
const [stepUpMethods, setStepUpMethods] = useState<("totp" | "email" | "recovery")[]>([]);
// Listen for step-up authentication required events
useEffect(() => {
const handleStepUpRequired = (event: CustomEvent) => {
const { action, methods } = event.detail;
setStepUpAction(action);
setStepUpMethods(methods || ["totp", "email", "recovery"]);
setShowStepUpModal(true);
};
window.addEventListener("stepUpRequired", handleStepUpRequired as EventListener);
return () => {
window.removeEventListener("stepUpRequired", handleStepUpRequired as EventListener);
};
}, []);
const handleStepUpSuccess = useCallback(() => {
setShowStepUpModal(false);
setStepUpAction(undefined);
// Dispatch event so pending actions can auto-retry
window.dispatchEvent(new CustomEvent("stepUpSuccess"));
}, []);
const handleStepUpClose = useCallback(() => {
setShowStepUpModal(false);
setStepUpAction(undefined);
}, []);
return (
<>
<div className="d-flex flex-column min-vh-100">
<Navbar />
<main className="flex-grow-1">
<Outlet />
</main>
<Footer />
</div>
<AuthModal
show={showAuthModal}
onHide={closeAuthModal}
initialMode={authModalMode}
/>
{/* Show feedback button for authenticated users */}
{user && <FeedbackButton />}
{/* Global Step-Up Authentication Modal */}
<TwoFactorVerifyModal
show={showStepUpModal}
onHide={handleStepUpClose}
onSuccess={handleStepUpSuccess}
action={stepUpAction}
methods={stepUpMethods}
/>
</>
);
};
export default RootLayout;

View File

@@ -1,5 +1,5 @@
import React, { useEffect, useState, useCallback, useRef } from "react"; import React, { useEffect, useState, useCallback, useRef } from "react";
import { useParams, useNavigate, Link } from "react-router-dom"; import { useParams, useNavigate, Link } from "react-router";
import { loadStripe } from "@stripe/stripe-js"; import { loadStripe } from "@stripe/stripe-js";
import { rentalAPI } from "../services/api"; import { rentalAPI } from "../services/api";
import { useAuth } from "../contexts/AuthContext"; import { useAuth } from "../contexts/AuthContext";

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { useNavigate, Link, useParams } from "react-router-dom"; import { useNavigate, Link, useParams } from "react-router";
import { useAuth } from "../contexts/AuthContext"; import { useAuth } from "../contexts/AuthContext";
import { forumAPI, addressAPI } from "../services/api"; import { forumAPI, addressAPI } from "../services/api";
import { uploadImagesWithVariants, getImageUrl } from "../services/uploadService"; import { uploadImagesWithVariants, getImageUrl } from "../services/uploadService";

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect, useRef } from "react"; import React, { useState, useEffect, useRef } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router";
import { useAuth } from "../contexts/AuthContext"; import { useAuth } from "../contexts/AuthContext";
import api, { addressAPI, userAPI, itemAPI } from "../services/api"; import api, { addressAPI, userAPI, itemAPI } from "../services/api";
import { uploadImagesWithVariants } from "../services/uploadService"; import { uploadImagesWithVariants } from "../services/uploadService";

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router";
import { rentalAPI, userAPI, stripeAPI } from "../services/api"; import { rentalAPI, userAPI, stripeAPI } from "../services/api";
import { Rental, User } from "../types"; import { Rental, User } from "../types";
import StripeConnectOnboarding from "../components/StripeConnectOnboarding"; import StripeConnectOnboarding from "../components/StripeConnectOnboarding";

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect, useRef } from "react"; import React, { useState, useEffect, useRef } from "react";
import { useParams, useNavigate } from "react-router-dom"; import { useParams, useNavigate } from "react-router";
import { Item, Rental, Address } from "../types"; import { Item, Rental, Address } from "../types";
import { useAuth } from "../contexts/AuthContext"; import { useAuth } from "../contexts/AuthContext";
import { itemAPI, rentalAPI, addressAPI, userAPI } from "../services/api"; import { itemAPI, rentalAPI, addressAPI, userAPI } from "../services/api";

View File

@@ -1,5 +1,5 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router";
interface FAQItem { interface FAQItem {
question: string; question: string;

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { useParams, useNavigate, Link, useSearchParams } from 'react-router-dom'; import { useParams, useNavigate, Link, useSearchParams } from 'react-router';
import { useAuth } from '../contexts/AuthContext'; import { useAuth } from '../contexts/AuthContext';
import { forumAPI } from '../services/api'; import { forumAPI } from '../services/api';
import { uploadImagesWithVariants, getImageUrl } from '../services/uploadService'; import { uploadImagesWithVariants, getImageUrl } from '../services/uploadService';

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Link, useSearchParams } from 'react-router-dom'; import { Link, useSearchParams } from 'react-router';
import { useAuth } from '../contexts/AuthContext'; import { useAuth } from '../contexts/AuthContext';
import { forumAPI } from '../services/api'; import { forumAPI } from '../services/api';
import { ForumPost } from '../types'; import { ForumPost } from '../types';

View File

@@ -1,5 +1,5 @@
import React, { useEffect, useState, useRef } from 'react'; import React, { useEffect, useState, useRef } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom'; import { useNavigate, useSearchParams } from 'react-router';
import { useAuth } from '../contexts/AuthContext'; import { useAuth } from '../contexts/AuthContext';
import { fetchCSRFToken } from '../services/api'; import { fetchCSRFToken } from '../services/api';

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router";
import { useAuth } from "../contexts/AuthContext"; import { useAuth } from "../contexts/AuthContext";
import { itemAPI } from "../services/api"; import { itemAPI } from "../services/api";
import { Item } from "../types"; import { Item } from "../types";

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { useParams, useNavigate } from "react-router-dom"; import { useParams, useNavigate } from "react-router";
import DatePicker from "react-datepicker"; import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css"; import "react-datepicker/dist/react-datepicker.css";
import { Item, Rental } from "../types"; import { Item, Rental } from "../types";

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect, useRef } from "react"; import React, { useState, useEffect, useRef } from "react";
import { useSearchParams, useNavigate } from "react-router-dom"; import { useSearchParams, useNavigate } from "react-router";
import { Item } from "../types"; import { Item } from "../types";
import { itemAPI } from "../services/api"; import { itemAPI } from "../services/api";
import ItemCard from "../components/ItemCard"; import ItemCard from "../components/ItemCard";

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router';
import { useAuth } from '../contexts/AuthContext'; import { useAuth } from '../contexts/AuthContext';
import { forumAPI } from '../services/api'; import { forumAPI } from '../services/api';
import { ForumPost } from '../types'; import { ForumPost } from '../types';

View File

@@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router";
const NotFound: React.FC = () => { const NotFound: React.FC = () => {
return ( return (

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { Link, useNavigate } from "react-router-dom"; import { Link, useNavigate } from "react-router";
import { useAuth } from "../contexts/AuthContext"; import { useAuth } from "../contexts/AuthContext";
import api from "../services/api"; import api from "../services/api";
import { Item, Rental, ConditionCheck } from "../types"; import { Item, Rental, ConditionCheck } from "../types";

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect, useCallback, useMemo } from "react"; import React, { useState, useEffect, useCallback, useMemo } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router";
import { useAuth } from "../contexts/AuthContext"; import { useAuth } from "../contexts/AuthContext";
import { userAPI, itemAPI, rentalAPI, addressAPI, conditionCheckAPI } from "../services/api"; import { userAPI, itemAPI, rentalAPI, addressAPI, conditionCheckAPI } from "../services/api";
import { User, Item, Rental, Address, ConditionCheck } from "../types"; import { User, Item, Rental, Address, ConditionCheck } from "../types";

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom'; import { useParams, useNavigate } from 'react-router';
import { User, Item } from '../types'; import { User, Item } from '../types';
import { userAPI, itemAPI } from '../services/api'; import { userAPI, itemAPI } from '../services/api';
import { getImageUrl } from '../services/uploadService'; import { getImageUrl } from '../services/uploadService';

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { useParams, useNavigate, useSearchParams } from "react-router-dom"; import { useParams, useNavigate, useSearchParams } from "react-router";
import { Item } from "../types"; import { Item } from "../types";
import { useAuth } from "../contexts/AuthContext"; import { useAuth } from "../contexts/AuthContext";
import { itemAPI, rentalAPI } from "../services/api"; import { itemAPI, rentalAPI } from "../services/api";

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { Link, useNavigate } from "react-router-dom"; import { Link, useNavigate } from "react-router";
import { useAuth } from "../contexts/AuthContext"; import { useAuth } from "../contexts/AuthContext";
import { rentalAPI, conditionCheckAPI } from "../services/api"; import { rentalAPI, conditionCheckAPI } from "../services/api";
import { getImageUrl } from "../services/uploadService"; import { getImageUrl } from "../services/uploadService";

View File

@@ -1,5 +1,5 @@
import React, { useEffect, useState, useRef } from 'react'; import React, { useEffect, useState, useRef } from 'react';
import { useNavigate, useSearchParams, Link } from 'react-router-dom'; import { useNavigate, useSearchParams, Link } from 'react-router';
import { useAuth } from '../contexts/AuthContext'; import { useAuth } from '../contexts/AuthContext';
import { authAPI } from '../services/api'; import { authAPI } from '../services/api';
import PasswordInput from '../components/PasswordInput'; import PasswordInput from '../components/PasswordInput';

View File

@@ -1,5 +1,5 @@
import React, { useEffect, useState, useRef } from "react"; import React, { useEffect, useState, useRef } from "react";
import { useNavigate, useSearchParams, Link } from "react-router-dom"; import { useNavigate, useSearchParams, Link } from "react-router";
import { useAuth } from "../contexts/AuthContext"; import { useAuth } from "../contexts/AuthContext";
import { authAPI } from "../services/api"; import { authAPI } from "../services/api";

View File

@@ -160,7 +160,7 @@ api.interceptors.response.use(
isRefreshing = false; isRefreshing = false;
processQueue(refreshError as AxiosError); processQueue(refreshError as AxiosError);
// Refresh failed - let React Router handle redirects via PrivateRoute // Refresh failed - let React Router handle redirects via ProtectedLayout
return Promise.reject(refreshError); return Promise.reject(refreshError);
} }
} }