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;
};
, 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>>>();
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>
);
};
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
}
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
Top comments (0)