DEV Community

Andrej Tlčina
Andrej Tlčina

Posted on

2 1

Building Training Plan builder: Introduction

Hello! So, I started a new mini side project. The app is supposed to help you create a training plan and export it in PDF format. In this article, I'm going to talk about setting up the project and building authentication and DB schema.

Setting the project up

I did the setup with the help of this tutorial. Sabin Adams will help you set up the development environment with Remix, Tailwind, MongoDB, and Prisma. I don't want to take credit for that, so if you want to set up a simple project, take a look at that article/tutorial.

Authentication

This part will be pretty short. Mainly because, I did a lot of the same stuff as I did in the previous project and again there's already a great article talking about the auth in Remix with MongoDB, that I followed. You can find it here

User info in the app

When it came to keeping user information on the frontend I immediately went to creating context. But I felt a little lazy and I didn't need user data in a lot of places, so because I have the info saved in the request header I decided to create a simple function

export const authenticateUser = async (req: Request) => {
  const user = await getUser(req);

  if (!user) throw redirect("auth/login");

  return user;
};
Enter fullscreen mode Exit fullscreen mode

, which I call in loader and get the result in the component.

export const loader: LoaderFunction = async ({ request }) => {
  const user = await authenticateUser(request);

  return user;
};
...
// in Route component
const user = useLoaderData<Awaited<ReturnType<typeof authenticateUser>>>();
Enter fullscreen mode Exit fullscreen mode

The result is then passed to Layout component, which displays the name of the user in the navbar

const Navbar = (props: { user: { name: string; id: string } }) => {
  const { user } = props;

  return (
    <div className="navbar bg-base-100">
      <Link to="/" className="btn btn-ghost normal-case text-xl">
        Strongion
      </Link>
      <div className="ml-auto font-semibold text-lg">{user.name}</div>
      <form action="/auth/logout" method="POST">
        <button name="_action" value="delete" className="btn btn-nav-sm ml-2">
          Log out
        </button>
      </form>
    </div>
  );
};

const Layout: React.FC<{ user: { name: string; id: string } }> = (props) => {
  const { user, children } = props;

  return (
    <div className="h-screen bg-base-100">
      <Navbar user={user} />
      <div className="w-2/3 mx-auto">{children}</div>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

DB Schema

I created the schema with four main models: User, Plan, Phase, Exercise. Each user can create multiple Plans, so we get one-to-many relations. Each plan has multiple Phases, and each Phase has multiple exercises attached to it. All relations are of type one-to-many. Here's the whole schema with attributes

model User {
  id            String   @id @default(auto()) @map("_id") @db.ObjectId
  createdAt     DateTime @default(now())
  updatedAt     DateTime @updatedAt
  name          String   @unique
  password      String
  trainingPlans Plan[]
}

model Plan {
  id          String   @id @default(auto()) @map("_id") @db.ObjectId
  title       String   @unique
  description String?
  trainee     User     @relation(references: [id], fields: [traineeId], onDelete: Cascade)
  traineeId   String   @db.ObjectId
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
  phases      Phase[]
}

model Phase {
  id          String     @id @default(auto()) @map("_id") @db.ObjectId
  title       String
  description String?
  exercises   Exercise[]
  plan        Plan       @relation(references: [id], fields: [planId], onDelete: Cascade)
  planId      String     @db.ObjectId

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Exercise {
  id           String       @id @default(auto()) @map("_id") @db.ObjectId
  name         String
  description  String?
  exerciseData ExerciseData
  phase        Phase        @relation(references: [id], fields: [phaseId], onDelete: Cascade)
  phaseId      String       @db.ObjectId

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

type ExerciseData {
  reps Int
  sets Int
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

In this section, we went through setting up a simple project, authentication, and creating a DB schema. If you ask me, pretty uninteresting things, but they have to be done 😅, so, to make the project more exciting, in the next section I'm going to explain building the Drag and Drop component with autocomplete search. See you there 😉

Github -> Strongion

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay