# Frontend Navigation with Re-frame

This document covers how to handle client-side navigation in the application using Re-frame events.

## Overview

The application uses [Reitit](https://github.com/metosin/reitit) for routing in combination with [Re-frame](https://day8.github.io/re-frame/) for state management. This provides a powerful and flexible way to handle navigation in your Single Page Application (SPA).

## Navigation Events

There are two main navigation events available:

1. `:router/navigate` - A regular event that triggers navigation
2. `:router/navigate!` - An effect that performs immediate navigation
3. `:router/redirect` - An event triggered when a route has a `:redirect-to` property

## Basic Navigation

### Using `:router/navigate`

The most common way to trigger navigation is with the `:router/navigate` event:

```clojure
(rf/dispatch [:router/navigate {:route ::routes/dashboard}])
```

This will navigate the user to the dashboard route defined in your routes configuration.

### Navigation with Parameters

You can include path parameters and query parameters in your navigation:

```clojure
(rf/dispatch [:router/navigate
              {:route ::routes/user-profile
               :path-params {:user-id "123"}
               :query-params {:tab "settings"}}])
```

This will navigate to a URL like `/user/123?tab=settings` (assuming your route is defined as something like `/user/:user-id`).

## Direct Navigation Effect

In some cases, you might want to use the navigation effect directly in a custom event handler:

```clojure
(rf/reg-event-fx
  ::my-custom-event
  (fn [{:keys [db]} [_ some-data]]
    ;; Do something with the data
    {:db (assoc db :some-key some-data)
     :router/navigate! {:route ::routes/success-page}}))
```

The `:router/navigate!` effect performs the navigation immediately as a side effect.

## Handling Navigation Results

When a navigation occurs, the `:router/navigated` event is dispatched with the new route details. This event updates the application state with the current route information and sets any SEO-related metadata.

The route information is stored in the app db under `:router/current-route` and can be accessed with a subscription:

```clojure
(rf/subscribe [:router/current-route])
```

## Common Navigation Patterns

### Navigation after Success

A common pattern is to navigate after an API call succeeds:

```clojure
(rf/dispatch
  [:http/api
   {:method :post
    :url :some/endpoint
    :body data
    :on-success [:router/navigate {:route ::routes/success-page}]}])
```

### Conditional Navigation in an Event Handler

```clojure
(rf/reg-event-fx
  ::process-auth
  (fn [{:keys [db]} [_ auth-result]]
    (if (:success auth-result)
      {:db (assoc db :auth auth-result)
       :router/navigate! {:route ::routes/dashboard}}
      {:db (assoc db :auth-error (:error auth-result))})))
```

### Redirecting to Login

When an unauthorized action is attempted, it's common to redirect to the login page:

```clojure
(rf/reg-event-fx
  ::handle-unauthorized
  (fn [{:keys [db]} _]
    {:db (dissoc db :auth)
     :router/navigate! {:route ::routes/login}}))
```

## Route Definition

Routes are defined in `src/cljc/saas/common/routes.cljc` using Reitit's routing syntax. Here's a simplified example:

```clojure
(defn app-routes []
  [""
   ["/" {:name ::routes/index
         :seo/title "Home"}]

   ["/dashboard" {:name ::routes/dashboard
                  :seo/title "Dashboard"
                  :view dashboard/dashboard-page}]

   ["/user/:user-id" {:name ::routes/user-profile
                      :seo/title "User Profile"
                      :view user/profile-page
                      :parameters {:path {:user-id string?}
                                  :query {:tab string?}}}]])
```

## SEO Considerations

When navigating to a new route, the `:router/navigated` event will automatically update the page title and meta description based on the `:seo/title` and `:seo/description` properties defined in your route data.

## Route Redirects

The application supports automatic redirects through the `:redirect-to` route property. This allows you to define routes that automatically redirect to another route when accessed.

### How It Works

1. Define a route with the `:redirect-to` property in `src/cljc/saas/common/routes.cljc`:

```clojure
["/settings"
 ["" (merge {:name :routes.settings/index
             :redirect-to :routes.settings/profile}
            #?(:cljs {:view #($ :div)}))]
 ["/profile" ...]]
```

2. When a user navigates to `/settings`, the router detects the `:redirect-to` property and automatically redirects to the specified route (`:routes.settings/profile` in this example).
3. The redirection is handled by the `:router/redirect` event, which is dispatched when a route with `:redirect-to` is matched.

### Common Use Cases

* Default pages for sections (e.g., redirecting `/settings` to `/settings/profile`)
* Legacy URLs that should redirect to new locations
* Creating logical groupings of routes with a default landing page

## Implementation Details

The navigation system is implemented in:

* `src/cljs/saas/core.cljs`: Sets up the router and initializes route handling
* `src/cljs/saas/db/events.cljs`: Defines the navigation events and effects
* `src/cljc/saas/common/routes.cljc`: Defines the application routes
* `src/cljs/saas/routing.cljs`: Handles route matching and redirection logic

## Example Usage

### Basic Navigation Button

```clojure
(defui nav-button []
  ($ ui/button
     {:on-click #(rf/dispatch [:router/navigate {:route ::routes/dashboard}])}
     "Go to Dashboard"))
```

### Navigation After Form Submission

```clojure
(defui signup-form []
  (let [form-data (use-state {:email "" :password ""})
        handle-submit (fn [e]
                        (.preventDefault e)
                        (rf/dispatch [:auth/register
                                      @form-data
                                      ;; Navigate after successful registration
                                      {:on-success [:router/navigate {:route ::routes/onboarding}]}]))]
    ($ :form {:on-submit handle-submit}
       ;; Form elements here
       )))
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://shipclojure.gitbook.io/shipclojure-docs/frontend/navigation.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
