Building a User Dashboard with Redwood
Tags: javascript, graphql, prisma, react
Published: 2021-07-23 11:52:26

---
title: Building a User Dashboard with Redwood
published: true
description: Dashboards are a common part of many apps, but they are so complex with the amount of data they handle. We're going to use Redwood to make a user dashboard with a database, back-end, and front-end all connected.
tags: #javascript, #graphql, #prisma, #react
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/udz9ingqk8mu6ic6fea5.png
---

There's a dashboard for just about every application you log in to these days. Most of them are built with modern frameworks like React or Angular and they have some kind of back-end to support them. Users depend on these dashboards to check their account info, make updates to their businesses, and handle a number of other actions.

In this tutorial, we'll be making a user dashboard for a management system with Redwood. It'll have a few actions that a user can take to add other users to the account with different roles. The dashboard will also let a user add new items to their inventory and the items will be attached to different locations.

Let's kick it off with a little background on Redwood.

## What is Redwood

Redwood is a full-stack framework built with Prisma, React, and GraphQL. It comes with a lot of features with a few commands that we'll be using to make this app quickly. If you're interested in learning more about Redwood, make sure to [check out their docs](https://redwoodjs.com/docs/introduction).

## Set up the project

Now let's jump into getting our dashboard set up. We'll bootstrap this app by running the following command. Just a heads up, it might take a few minutes for it to finish, but it'll be worth the wait.

`yarn create redwood-app ./redwood-dashboard`

When the command has finished executing, you'll notice you have quite a few folders and files. The most important folders are the `api` directory and the `web` directory. The `api` directory holds all of the code for the back-end and database while the `web` directory holds everything for the front-end.

Before we start working on the app, you should run it to see how it works first. Use the following command to start both the front-end and back-end.

`yarn rw dev`

Here's what you should see in your browser.

![base Redwood app](https://res.cloudinary.com/jesse-thisdot/image/upload/v1624049189/e-603fc55d218a650069f5228b/qeck1tbl8odgdkh3m1bc.png)

## Defining the dashboard model

Since you've seen the app in action, let's start taking advantage of some Redwood commands to work on the back-end. We'll start by making a Prisma model for the dashboard. This is how our app will connect to a Postgres database. Inside of the `api > db`, open the `schema.prisma` file.

This is where we'll make the schema for the tables we want in our database using a few different kinds of relations. You'll see that Redwood already has a few configs set and an example of a user model.

We'll be changing the `datasource db provider` to `postgresql` which you can see in the code snippet below.

```javascript
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}
```

Then we'll remove the `TODO` and update the example user model.

```javascript
model User {
  id    Int    @id @default(autoincrement())
  email String @unique
  name  String
  role  String
}
```

Our updated `User` model has a few columns for information we want to store about a user. The `email` field has a constraint that it always has to be a unique value. The same email address can't be in the database multiple times. Next, we'll add a new model for our `Item` and `Location` tables. These need to be created at the same time because they have a relationship with each other.

```javascript
model Item {
  id         Int      @id @default(autoincrement())
  sku        String   @unique
  name       String
  location   Location @relation(fields: [locationId], references: [id])
  locationId Int
}

model Location {
  id       Int    @id @default(autoincrement())
  name     String
  district Int
  Item     Item[]
}
```

Then we'll update the `seed.js` file so that there are a few values present before we start our app. This is common on product dashboards for populating dropdown options or initializing tags that users can add to different settings. We'll be adding a user, an item, and one location to seed our database. Just a heads up, I've deleted a lot of comments from this file.

```javascript
/* eslint-disable no-console */
const { PrismaClient } = require('@prisma/client')
const dotenv = require('dotenv')

dotenv.config()
const db = new PrismaClient()

async function main() {
  console.warn('Please define your seed data.')

  await db.user.create({
    data: { name: 'Kaiya', email: 'best@test.com', role: 'inventory' },
  })

  await db.location.create({
    data: { name: 'Compound North', district: 1 },
  })

  await db.item.create({
    data: {
      name: 'Morning Glory',
      sku: 'hf8029ht8942ht8429pht8p942p',
      locationId: 1,
    },
  })
}

main()
  .catch((e) => console.error(e))
  .finally(async () => {
    await db.$disconnect()
  })
```

With the schema and seed data in place, we're almost ready to run our first migration. Before we do that, we need to be connected to the database. So in the `.env` file at the root of the project, update the `DATABASE_URL` to match your Postgres instance. It'll look similar to this.

`DATABASE_URL=postgres://postgres:admin@localhost:5432/dashboard`

If you don't have a local Postgres instance to work with, [you can download it here](https://www.postgresql.org/download/) pretty quickly.

Now we're going to run the migration with:

`yarn rw prisma migrate dev`

This will prompt you for a migration name, create the database and the tables, and add the seed data. If you look at your database through pgAdmin, you'll see something like this.

![seeded database in pgAdmin](https://res.cloudinary.com/jesse-thisdot/image/upload/v1624049253/e-603fc55d218a650069f5228b/gjelsegxua1mgaiy3l5t.png)

Now that you have your database ready to store and send the values we need, let's move on to our back-end and front-end.

## Adding the back-end and front-end

Redwood has one particular command that does a lot of work for us. It will create the entire CRUD for a table from the front-end components and routes to the back-end GraphQL queries, types, and mutations. We're going to take advantage of that to make most of our dashboard. Let's start with the following command.

`yarn rw g scaffold user`

This will generate several pages and components and GraphQL files to handle all of the CRUD actions for the user's portion of the dashboard. Take a look in `web > pages`. You should see a new directory called `User`. This has all of the pages we need to handle everything about users.

Now take a look in `web > components`. There will be another `User` folder. This one contains the components we use to interact with the GraphQL server. Most of the data is fetched in these components and requests are sent from these components.

The `web > layouts` folder will also have a new directory containing a layout file for your `User` pages. This is how you can create styles for a specific set of pages.

In the `Routes.js` file, you'll see there is a new set of routes for users wrapped in the `UserLayout`. All of the pages generate also have their routes automatically added to this file inside of their parent layout.

There were also some new files generated in the `api` directory. If you look inside `api > src > graphql`, you'll find the GraphQL schema for all the CRUD operations you need and the types have been defined by the model in the `schema.prisma` file.

Then inside `api > src > services`, you'll find all of the GraphQL resolvers for the queries and mutations defined in the schema.

All of these pages, components, types, and resolvers were generated by that one Redwood command. Let's run the app with `yarn rw dev` so you can see all of this working. If you go to the `users` page in the browser, you should see something similar to this.

**The home page will return a 404 right now because we haven't added a component for that route. We'll do that a bit later, but the `/users` page will still work.**

![users CRUD in browser](https://res.cloudinary.com/jesse-thisdot/image/upload/v1624049294/e-603fc55d218a650069f5228b/z0ywmlzj1sywxdmw1vjf.png)

You can add a new user, update the current user, or delete the current user with the buttons on the page.

We're going to run that `scaffold` command two more times to generate the CRUD for the items and locations.

```bash
yarn rw g scaffold location
yarn rw g scaffold item
```

Now we have all of the actions we need for our user dashboard. There are a couple of things we need to do to give this dashboard a more realistic feel. We'll add a home page and a layout to help us navigate between parts of the dashboard.

### Making the home page

Let's start by adding the `styled-components` package because we're going to use it to style a few things. First, go to the `web` directory in your terminal. That's where this package will need to be installed.

`yarn add styled-components`

Now let's run one of those magic Redwood commands to generate the home page and its routing.

`yarn rw g page home /`

This will create a new directory in `web > src > pages` for the `HomePage` component and it'll add a new route to `Routes.js`. You can re-run your app here to see the new home page. It'll look like this in the browser.

![new home page](https://res.cloudinary.com/jesse-thisdot/image/upload/v1624049318/e-603fc55d218a650069f5228b/dcsgwketcrhpwiuddwq8.png)

Now that we have a home page in place, let's make a layout that will apply to this and all the other pages we have so far. Of course, there's a Redwood command to handle that.

`yarn rw g layout home`

This will create a new folder in `web > src > layouts` for our home page. We're going to use a few styled components in the `HomeLayout` component to create a side navigation menu and give the dashboard a little definition. We'll also be using Redwood routing to add links to the other pages in the side navigation.

```javascript
import { Link, routes } from '@redwoodjs/router'
import styled from 'styled-components'

const HomeLayout = ({ children }) => {
  return (
    <FlexBox>
      <SideNav>
        <LinkContainer>
          <Link to={routes.users()}>Users</Link>
        </LinkContainer>
        <LinkContainer>
          <Link to={routes.locations()}>Locations</Link>
        </LinkContainer>
        <LinkContainer>
          <Link to={routes.items()}>Items</Link>
        </LinkContainer>
      </SideNav>
      <FullPage>{children}</FullPage>
    </FlexBox>
  )
}

const FlexBox = styled.div`
  display: flex;
`

const FullPage = styled.div`
  height: 100vh;
  width: 100%;
`

const LinkContainer = styled.div`
  padding: 12px;

  > * {
    color: #000;
    font-family: sans-serif;
    font-size: 18px;
    text-decoration: none;
  }

  > *:hover {
    color: #5b5b5b;
  }
`

const SideNav = styled.nav`
  border-right: 1px solid;
  width: 250px;
`

export default HomeLayout
```

Next, we need to apply this layout to all of the pages in our app. We'll do that in the `Routes.js` file. This means adding a `<Set>` component around all of the existing routes like below.

```javascript
...
<Router>
  <Set wrap={HomeLayout}>
    <Route path="/" page={HomePage} name="home" />
    <Set wrap={ItemsLayout}>
      <Route path="/items/new" page={ItemNewItemPage} name="newItem" />
      <Route path="/items/{id:Int}/edit" page={ItemEditItemPage} name="editItem" />
      <Route path="/items/{id:Int}" page={ItemItemPage} name="item" />
      <Route path="/items" page={ItemItemsPage} name="items" />
    </Set>
    <Set wrap={LocationsLayout}>
      <Route path="/locations/new" page={LocationNewLocationPage} name="newLocation" />
      <Route path="/locations/{id:Int}/edit" page={LocationEditLocationPage} name="editLocation" />
      <Route path="/locations/{id:Int}" page={LocationLocationPage} name="location" />
      <Route path="/locations" page={LocationLocationsPage} name="locations" />
    </Set>
    <Set wrap={UsersLayout}>
      <Route path="/users/new" page={UserNewUserPage} name="newUser" />
      <Route path="/users/{id:Int}/edit" page={UserEditUserPage} name="editUser" />
      <Route path="/users/{id:Int}" page={UserUserPage} name="user" />
      <Route path="/users" page={UserUsersPage} name="users" />
    </Set>
    <Route notfound page={NotFoundPage} />
  </Set>
</Router>
...
```

Now if you run `yarn rw dev`, you'll see something like this.

![dashboard with global layout applied](https://res.cloudinary.com/jesse-thisdot/image/upload/v1624049368/e-603fc55d218a650069f5228b/cmymcv9omp6le8jud6oc.png)

If you go any of the links in the side navigation, you'll see something like this.

![users in the dashboard with layout](https://res.cloudinary.com/jesse-thisdot/image/upload/v1624049383/e-603fc55d218a650069f5228b/dqgaomfednzrvetjgk0l.png)

I'll leave the extra styling to you. Styling this could be a completely separate tutorial!

This might feel like a cliffhanger since we didn't actually write much code, but that's what I like about Redwood. You can get big apps up and running quickly and things just work.

## Finished code

You can check out the front-end without the connection to the back-end here on this [Code Sandbox](https://codesandbox.io/s/runtime-water-1fvey?file=/src/App.js).

If you want all of the code for the front-end and back-end, check out the [`redwood-user-dashboard` in this Git repo](https://github.com/flippedcoder/blog-examples/tree/main/redwood-user-dashboard)!

## Conclusion

Dashboards are a major part of many apps so it's helpful to know how to create one quickly. Redwood is a framework that will help you get started with advanced projects like these and it makes it fast to generate complex structures.