Skip to content

Add simple routing in frontend #49#111

Open
rl-86 wants to merge 6 commits intomainfrom
49-add-react-router
Open

Add simple routing in frontend #49#111
rl-86 wants to merge 6 commits intomainfrom
49-add-react-router

Conversation

@rl-86
Copy link

@rl-86 rl-86 commented Sep 15, 2025

This PR implements simple routing for frontend navigation.

  • Home, Login, and Users pages now accessible via routing
  • Navigation links in Header (desktop and mobile)
  • Shared Header/Footer layout across all routes

Tested locally, all pages working as expected.

Summary by CodeRabbit

  • New Features
    • Added a localized Home page with language selector and nested routes for Home, Login, and Users.
  • Refactor
    • App now renders content via client-side nested routing with a persistent header and footer; new route structure and Not Found handling.
  • Style
    • Header menu updated to Home, Login, and Users; menu navigation now uses client-side links.
  • Chores
    • Added an HTTP backend for i18n translation loading.

@rl-86 rl-86 linked an issue Sep 15, 2025 that may be closed by this pull request
@coderabbitai
Copy link

coderabbitai bot commented Sep 15, 2025

Walkthrough

Adds i18next HTTP backend dependency. Refactors App into a router-driven layout using React Router Outlet. Introduces nested routing for Home, Login, and Users; updates Header and menu components to client-side Links. Adds a new HomePage component that renders localized content.

Changes

Cohort / File(s) Summary of Changes
Dependency addition
frontend/package.json
Added dependency i18next-http-backend@^3.0.1.
App layout container
frontend/src/App.tsx
Converted App to a layout component with persistent Header/Footer and <Outlet />; removed inline content/LanguageSelector; preserved i18n language-change handling and loading/error guards.
Routing restructuring
frontend/src/main.tsx
Replaced top-level routes with nested children under /: index → HomePage, /login, /users; added redirect loader for /auth/login/login; added catch-all not-found.
Navigation components
frontend/src/components/common/Header.tsx, frontend/src/components/common/MenuItems.tsx, frontend/src/components/common/MenuItemsMobile.tsx
Header menu updated to Home, Login, Users. Replaced <a> with react-router-dom Link; Login now navigates via Link-wrapped button. Removed some i18n usage points and changed loading/error guard placement.
New page
frontend/src/pages/HomePage.tsx
Added HomePage component using useTranslation and LanguageSelector, renders localized heading and exported as default.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as User
  participant R as React Router
  participant A as App (Layout)
  participant H as Header/Menu
  participant P as Child Page (Home/Login/Users)

  U->>R: Navigate to URL
  R->>A: Mount layout (Header, Outlet, Footer)
  A->>H: Render navigation (Links)
  R->>P: Render matched child into Outlet
  Note over A,P: i18n loading/error gates before rendering content
  U->>H: Click Link
  H->>R: Client-side route change
  R->>P: Render new child route
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • MatsJoh50
  • MikaelaMathisson
  • FredrickKristoffersson
✨ Finishing touches
  • 📝 Docstrings were successfully generated.
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 49-add-react-router

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (20)
frontend/src/components/common/MenuItemsMobile.tsx (3)

17-21: Avoid nesting a button inside a Link and keep valid

    /
  • structure.

    Anchor-with-button is an a11y anti‑pattern, and placing a Link directly under ul breaks list semantics. Style the Link as the button and wrap it in an li.

    Apply:

    -            <Link to="/login" className="block">
    -                <button className="p-1 w-full bg-blue-600 text-white rounded-md shadow-md hover:bg-blue-700">
    -                    Sign in
    -                </button>
    -            </Link>
    +            <li>
    +                <Link
    +                    to="/login"
    +                    className="p-1 w-full inline-flex items-center justify-center bg-blue-600 text-white rounded-md shadow-md hover:bg-blue-700"
    +                >
    +                    Sign in
    +                </Link>
    +            </li>

    11-14: Use a stable key instead of array index.

    Index keys hinder reconciliation; use menu.url.

    -            {menuItemsMobile.map((menu, index) => (
    -                <li key={index}>
    +            {menuItemsMobile.map((menu) => (
    +                <li key={menu.url}>
                         <Link to={menu.url}>{menu.title}</Link>
                     </li>
                 ))}

    4-6: Rename the props interface for clarity.

    interface MenuItemsMobile shadows the component name; prefer MenuItemsMobileProps to reduce confusion in tooling and stack traces.

    Example:

    type MenuItem = { title: string; url: string };
    interface MenuItemsMobileProps { menuItemsMobile: MenuItem[] }
    export default function MenuItemsMobile({ menuItemsMobile }: MenuItemsMobileProps) { ... }
frontend/src/components/common/MenuItems.tsx (3)

22-26: Don’t nest a button inside a Link; also ensure list semantics.

Make the Link look like a button and keep it inside an li.

-                <Link to="/login">
-                    <button className="p-1 w-24 h-2/3 self-center bg-blue-600 text-white rounded-md shadow-md hover:bg-blue-700">
-                        <p className="text-lg">Login</p>
-                    </button>
-                </Link>
+                <li className="h-full flex items-center">
+                    <Link
+                        to="/login"
+                        className="p-1 w-24 h-2/3 inline-flex items-center justify-center bg-blue-600 text-white rounded-md shadow-md hover:bg-blue-700"
+                    >
+                        <span className="text-lg">Login</span>
+                    </Link>
+                </li>

12-15: Prefer stable keys over index.

-                {menuItems.map((menu, index) => (
+                {menuItems.map((menu) => (
                     <li
-                        key={index}
+                        key={menu.url}
                         className="h-full flex items-center border-b-2 border-white hover:border-purple-800"

23-25: Consider i18n for “Login” label.

For consistency with other translated labels, either pass a translated title via props or use t('loginkey', 'Login') here.

frontend/src/main.tsx (2)

15-28: Add 404 and legacy redirect for smoother UX.

Optional: handle stale /auth/login bookmarks and unknown paths.

             {
                 path: 'users',
                 element: <UsersPage />
-            }
+            },
+            {
+                path: 'auth/login',
+                loader: () => redirect('/login'),
+            },
+            {
+                path: '*',
+                element: <div>Not found</div>
+            }

Also update imports to include redirect:

import { createBrowserRouter, RouterProvider, redirect } from 'react-router-dom';

4-7: Import extensions are optional — consider consistency.

Vite resolves .tsx; consider dropping extensions across imports for consistency.

frontend/src/pages/HomePage.tsx (2)

11-13: Use Trans instead of concatenating tokens for i18n correctness.

Concatenation breaks grammar in many locales; let translators control order and punctuation while you inject markup.

-import { useTranslation } from 'react-i18next';
+import { Trans } from 'react-i18next';
@@
-    const { t } = useTranslation();
+    // no local i18n state needed here
@@
-            <h1 className="my-80 text-center text-4xl">
-                {t('jollykey')} <strong className="color1">{t('christmaskey')}</strong>
-                {t('andakey')} <strong className="color2">{t('happykey')} </strong>!
-            </h1>
+            <h1 className="my-80 text-center text-4xl">
+                <Trans
+                    i18nKey="home.heading"
+                    components={{ strong1: <strong className="color1" />, strong2: <strong className="color2" /> }}
+                />
+            </h1>

Translation example (for reference):

home.heading: "{{jolly}} <strong1>{{christmas}}</strong1> {{and}} <strong2>{{happy}}</strong2>!"

12-12: Stray whitespace inside strong tag.

There’s a trailing space in {t('happykey')} ; drop it to avoid odd spacing.

-                {t('andakey')} <strong className="color2">{t('happykey')} </strong>!
+                {t('andakey')} <strong className="color2">{t('happykey')}</strong>!
frontend/src/components/common/Header.tsx (7)

15-17: Use functional state update to avoid stale toggle

setIsMobile(!isMobile) can flip incorrectly under batched updates. Use the functional form.

-    const handleMobileMenu = () => {
-        setIsMobile(!isMobile);
-    };
+    const handleMobileMenu = () => {
+        setIsMobile(prev => !prev);
+    };

19-21: Remove debug effect and stray translation key

The console.log with t("Home") is a dev artifact and uses a key that differs from "homekey". Drop the effect before shipping.

-    useEffect(() => {
-        console.log(t("Home")); // Log to check if translation is correctly loaded
-    }, [i18n.language]);
+    // Removed debug effect that logged translations on language change.

31-44: Add fallback for home label; keep labels consistent across desktop/mobile

Provide a default for home to match the other items. Also ensure MenuItemsMobile uses the same translation for Login (see frontend/src/components/common/MenuItemsMobile.tsx lines 7-23 where it renders literal "Sign in").

-            title: t("homekey"),
+            title: t("homekey", "Home"),

Please update MenuItemsMobile/MenuItems to use t("loginkey", "Login") consistently for the button text.


53-53: Invalid Tailwind class ‘align-center’

Tailwind uses items-center, not align-center. This class currently has no effect.

-                    <div className="flex flex-row align-center h-full">
+                    <div className="flex flex-row items-center h-full">

64-67: Add ARIA to hamburger button

Improve accessibility by wiring aria-expanded, aria-controls, and a label.

-                            <button onClick={handleMobileMenu} className="md:hidden mx-5">
+                            <button
+                                onClick={handleMobileMenu}
+                                className="md:hidden mx-5"
+                                aria-expanded={isMobile}
+                                aria-controls="mobile-menu"
+                                aria-label={isMobile ? "Close menu" : "Open menu"}
+                            >

72-79: Identify and describe the mobile menu for assistive tech

Add id to pair with aria-controls, role, and aria-hidden.

-            <div
+            <div
+                id="mobile-menu"
+                role="menu"
+                aria-hidden={!isMobile}
                 className={`${

23-29: Avoid replacing the entire header during i18n load/error

Returning early replaces the header with a paragraph and causes layout shift. Prefer keeping the header and rendering a lightweight message/skeleton inside the menu area.

frontend/src/App.tsx (3)

15-22: Remove languageChanged + key remount hack

react-i18next already triggers re-renders on language changes. Forcing remount resets state across the app on every language switch.

-    const [key, setKey] = useState(0);
+    // No need for a remount key; rely on react-i18next reactivity.

-    useEffect(() => {
-        const handleLanguageChange = () => {
-            setKey(prevKey => prevKey + 1);
-        };
-        i18n.on('languageChanged', handleLanguageChange);
-        return () => {
-            i18n.off('languageChanged', handleLanguageChange);
-        };
-    }, [i18n]);
+    // Removed manual languageChanged listener and remount logic.

33-41: Render layout consistently; show i18n fallbacks inside

Keep Header/Footer mounted to avoid layout jumps; place loading/error inside main. Also add role="main" for a11y.

-    return (
-        <div className="w-full" key={key}>
-            <Header />
-            <main className="w-full">
-                <Outlet />
-            </main>
-            <Footer/>
-        </div>
-    );
+    return (
+        <div className="w-full">
+            <Header />
+            <main className="w-full" role="main">
+                {loading ? (
+                    <p>Loading translations...</p>
+                ) : error ? (
+                    <p className="text-red-600">{error}</p>
+                ) : (
+                    <Outlet />
+                )}
+            </main>
+            <Footer />
+        </div>
+    );

25-31: Consolidate i18n loading/error handling

Given Header and Footer also use useTranslationStatus, centralize handling in App to prevent duplicated "Loading..." messages. After applying the previous diff, consider removing the early returns from Header/Footer.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 69a7e9d and 479f7f9.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (7)
  • frontend/package.json (1 hunks)
  • frontend/src/App.tsx (2 hunks)
  • frontend/src/components/common/Header.tsx (1 hunks)
  • frontend/src/components/common/MenuItems.tsx (1 hunks)
  • frontend/src/components/common/MenuItemsMobile.tsx (1 hunks)
  • frontend/src/main.tsx (2 hunks)
  • frontend/src/pages/HomePage.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
frontend/src/App.tsx (3)
frontend/src/hooks/useTranslationStatus.tsx (1)
  • useTranslationStatus (5-30)
frontend/src/components/common/Header.tsx (1)
  • Header (10-83)
frontend/src/components/common/Footer.tsx (1)
  • Footer (8-64)
frontend/src/components/common/Header.tsx (3)
frontend/src/hooks/useTranslationStatus.tsx (1)
  • useTranslationStatus (5-30)
frontend/src/components/common/MenuItems.tsx (1)
  • MenuItems (8-30)
frontend/src/components/common/MenuItemsMobile.tsx (1)
  • MenuItemsMobile (8-24)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (java-kotlin)
🔇 Additional comments (3)
frontend/src/pages/HomePage.tsx (1)

1-3: Verify translation keys exist.

Ensure jollykey, christmaskey, andakey, happykey (or home.heading if adopting Trans) are present in all supported locales to avoid runtime fallbacks.

frontend/src/main.tsx (1)

15-28: Confirm is rendered in App

Search returned no matches — ensure your App layout component (e.g. frontend/src/App.tsx or src/App.tsx) includes so the nested routes (index, login, users) mount.

frontend/package.json (1)

18-18: Backend registered and configured in i18n init.

frontend/src/i18n.ts imports Backend, calls .use(Backend), and sets backend.loadPath ('/locale/{{lng}}/{{ns}}.json') — no further changes required.

@sonarqubecloud
Copy link

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
frontend/src/main.tsx (4)

4-4: Prefer extensionless imports for consistency

Use extensionless paths to avoid bundler coupling; align with typical TS/ESM style.

-import HomePage from './pages/HomePage.tsx'
+import HomePage from './pages/HomePage'

Consider doing the same for UsersPage/LoginPage for consistency.

Is the rest of the codebase using extensionless imports? If yes, I’ll prep a quick follow-up PR to normalize these.


33-35: Not Found: consider a dedicated component with i18n

Replace the inline div with a NotFound page to keep layout/i18n consistent.

-                element: <div>Not found</div>
+                element: <NotFoundPage />

If you want, I can scaffold NotFoundPage with translations.


40-44: Add a fallbackElement to RouterProvider (enables smooth lazy-loading later)

Pre-wire a loading state for future route-level code-splitting.

-        <RouterProvider router={router} />
+        <RouterProvider router={router} fallbackElement={<div className="p-4">Loading…</div>} />

If you’d like, I can also convert routes to use Router “lazy” for code-splitting in a follow-up.


15-36: Add basename for subpath deployments (Outlet present)

Routing looks good — Outlet is present in frontend/src/App.tsx (line 37). Add a basename so the app works under a subpath:

-    }
-]);
+    }
+], { basename: import.meta.env.BASE_URL });

Ensure your web server rewrites unknown routes to index.html for SPA deep-link support.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 479f7f9 and 2d3a6a5.

📒 Files selected for processing (3)
  • frontend/src/components/common/Header.tsx (1 hunks)
  • frontend/src/components/common/MenuItemsMobile.tsx (1 hunks)
  • frontend/src/main.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/src/components/common/MenuItemsMobile.tsx
  • frontend/src/components/common/Header.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (java-kotlin)
🔇 Additional comments (1)
frontend/src/main.tsx (1)

8-8: Loader redirect usage approved — react-router-dom >= 6.4 confirmed

frontend/package.json declares react-router-dom as ^7.0.2 (parsed 7.0.0); this is >= 6.4, so using redirect() in loaders is valid.

@coderabbitai
Copy link

coderabbitai bot commented Nov 2, 2025

Note

Docstrings generation - SUCCESS
Generated docstrings for this pull request at #112

coderabbitai bot added a commit that referenced this pull request Nov 2, 2025
Docstrings generation was requested by @rl-86.

* #111 (comment)

The following files were modified:

* `frontend/src/App.tsx`
* `frontend/src/components/common/Header.tsx`
* `frontend/src/components/common/MenuItems.tsx`
* `frontend/src/components/common/MenuItemsMobile.tsx`
* `frontend/src/pages/HomePage.tsx`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add simple routing in frontend

1 participant