Skip to content

Commit

Permalink
Minor bug fixes and improvments
Browse files Browse the repository at this point in the history
  • Loading branch information
ad956 committed May 7, 2024
1 parent f5934f6 commit 92d742b
Show file tree
Hide file tree
Showing 16 changed files with 464 additions and 157 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## [![My Skills](https://skillicons.dev/icons?i=nextjs,tailwindcss,githubactions,mongodb,vercel,ts,docker&theme=dark)](https://skillicons.dev)

[![Build](https://img.shields.io/github/actions/workflow/status/ad956/patient-fitness-tracker/tests.yml?branch=main)](https://img.shields.io)
[![Build](https://img.shields.io/github/actions/workflow/status/ad956/patient-fitness-tracker/playwright.yml?branch=main)](https://img.shields.io)

The Patient Fitness Tracker is a modern healthcare platform designed to streamline patient management and monitoring across multiple hospitals. It empowers patients to take control of their health journey while enabling healthcare providers to deliver personalized care efficiently.

Expand Down Expand Up @@ -47,6 +47,7 @@ The Patient Fitness Tracker is a modern healthcare platform designed to streamli
| SMTP_PASSWORD | Password for SMTP authentication. |
| SMTP_FROM_EMAIL | Email address for sending emails. |
| JWT_SECRET | Secret key for JWT token generation and verification. |
| BCRYPT_SALT_ROUNDS | Number of salt rounds for bcrypt password hashing |

5. **Start the development server:**
```bash
Expand Down
28 changes: 12 additions & 16 deletions app/(pages)/(auth)/signup/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,13 @@ export default function Signup() {
const formData = new FormData(e.currentTarget as HTMLFormElement);

try {
toast.loading("Please wait ...", {
position: "bottom-center",
});

const signUpSuccess = await signupAction(formData);
toast.dismiss();

if (signUpSuccess.failure) {
toast.error(signUpSuccess.msg);
} else {
Expand All @@ -179,28 +185,18 @@ export default function Signup() {
role: userRole?.toString() || "",
});

const sendingOtpPromise = new Promise((resolve) => {
setTimeout(() => {
resolve(true);
setShowOtp(true);
}, 2000);
});

toast.promise(
sendingOtpPromise,
toast.success(
"Almost done! Please use the OTP you received to complete signup. Thank you!",
{
loading: "Please wait...",
success:
"Almost done! Please use the OTP you received to complete signup. Thank you!",
error: "Error while sending OTP",
},
{ position: "bottom-center", duration: 5000 }
position: "bottom-center",
}
);
setShowOtp(true);
}
}
} catch (error) {
toast.error("Error signing up. Please try again!");
console.error("Error signing up");
toast.error("Error signing up. Please try again!");
}
}

Expand Down
46 changes: 31 additions & 15 deletions app/api/auth/login/route.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,35 @@
import dbConfig from "@lib/db";
import WelcomeTemplate from "@/emails/otpmail";
import OtpTemplate from "@lib/emails/templates";
import { sendEmail } from "@lib/email";
import { render } from "@react-email/render";
import { generateSecureOTP } from "@utils/generateOtp";
import bcrypt from "bcrypt";

type LoginBody = {
email: string;
password: string;
role: string;
};

const allowedRoles = ["patient", "hospital", "doctor", "receptionist"];

export async function POST(req: Request) {
try {
const body: LoginBody = await req.json();
switch (body.role) {
case "patient":
return setOTP(body);
case "hospital":
return setOTP(body);
case "doctor":
return setOTP(body);
case "receptionist":
return setOTP(body);

default:
return Response.json({ error: "Invalid user" });
if (!body || !body.email || !body.password || !body.role) {
return Response.json({
error:
"Invalid request body. Please provide email, password, and role.",
});
}

if (!allowedRoles.includes(body.role)) {
return Response.json({ error: "User role isn't valid." });
}

const result = await setOTP(body);
return result;
} catch (error) {
console.error("Error during login:", error);
return Response.json({ error: "Internal Server Error" });
Expand All @@ -37,9 +41,21 @@ async function setOTP(loginBody: LoginBody) {

const collection = db.collection(loginBody.role);
const email = loginBody.email;
const user = await collection.findOne({ email });
const projection = {
_id: 0,
email: 1,
firstname: 1,
lastname: 1,
password: 1,
};
const user = await collection.findOne(
{ email },
{
projection,
}
);

if (!user || user.password !== loginBody.password) {
if (!user || !(await bcrypt.compare(loginBody.password, user.password))) {
return Response.json(
{ error: "Invalid email or password" },
{ status: 401 }
Expand All @@ -59,7 +75,7 @@ async function setOTP(loginBody: LoginBody) {
const mailsent = await sendEmail({
to: send.to,
subject: send.subject,
html: render(WelcomeTemplate(send.name, send.otp)),
html: render(OtpTemplate(send.name, send.otp)),
from: {
name: "Patient Fitness Tracker",
address: "[email protected]",
Expand Down
38 changes: 0 additions & 38 deletions app/api/auth/session/route.ts

This file was deleted.

41 changes: 24 additions & 17 deletions app/api/auth/signup/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import dbConfig from "@lib/db";
import WelcomeTemplate from "@/emails/otpmail";
import OtpTemplate from "@/lib/emails/templates";
import { sendEmail } from "@lib/email";
import { render } from "@react-email/render";
import { generateSecureOTP } from "@utils/generateOtp";
Expand All @@ -9,6 +9,7 @@ import {
patientadditionalDetails,
receptionistadditionalDetails,
} from "@constants/index";
import bcrypt from "bcrypt";

type SignupBody = {
firstname: string;
Expand All @@ -19,6 +20,8 @@ type SignupBody = {
role: string;
};

const allowedRoles = ["patient", "hospital", "doctor", "receptionist"];

export async function POST(req: Request) {
try {
const body: SignupBody = await req.json();
Expand All @@ -30,21 +33,12 @@ export async function POST(req: Request) {
);
}

switch (body.role) {
case "patient":
return createAccount(body);
case "hospital":
return createAccount(body);
case "doctor":
return createAccount(body);
case "receptionist":
return createAccount(body);

default:
return Response.json({
error: "Error creating account. Invalid user role!",
});
if (!allowedRoles.includes(body.role)) {
return Response.json({ error: "User role isn't valid." });
}

const result = await createAccount(body);
return result;
} catch (error) {
console.error("Error during signup:", error);
return Response.json({ error: "Internal Server Error" });
Expand All @@ -57,6 +51,7 @@ async function createAccount(signupBody: SignupBody) {
const collection = db.collection(signupBody.role);
const email = signupBody.email;
const username = signupBody.username;

const existingUser = await collection.findOne({
$or: [{ email }, { username }],
});
Expand Down Expand Up @@ -91,7 +86,13 @@ async function createAccount(signupBody: SignupBody) {
break;
}

const user = { ...signupBody, ...additionalDetails };
const hashedPassword = await hashPassword(signupBody.password);

const user = {
...signupBody,
...additionalDetails,
password: hashedPassword,
};

await collection.insertOne(user);

Expand All @@ -108,7 +109,7 @@ async function createAccount(signupBody: SignupBody) {
const mailsent = await sendEmail({
to: send.to,
subject: send.subject,
html: render(WelcomeTemplate(send.name, send.otp)),
html: render(OtpTemplate(send.name, send.otp)),
from: {
name: "Patient Fitness Tracker",
address: "[email protected]",
Expand Down Expand Up @@ -138,3 +139,9 @@ function checkMissingElements(body: SignupBody) {
}
return false;
}

async function hashPassword(password: string) {
const saltRounds = parseInt(process.env.BCRYPT_SALT_ROUNDS || "10"); // Read salt rounds from environment variable or default to "10"
const hashedPassword = await bcrypt.hash(password, saltRounds);
return hashedPassword;
}
31 changes: 16 additions & 15 deletions app/api/auth/verifyotp/route.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { setSession } from "@sessions/sessionUtils";
import dbConfig from "@lib/db";

type bodyType = {
Expand All @@ -6,27 +7,24 @@ type bodyType = {
role: string;
};

const allowedRoles = ["patient", "hospital", "doctor", "receptionist"];

export async function POST(req: Request) {
try {
const body = await req.json();
const body: bodyType = await req.json();

if (!body.otp) {
return Response.json({ error: "OTP Not Provided" });
if (!body || !body.email || !body.role || !body.otp) {
return Response.json({
error: "Email, OTP, and role are required fields in the request body.",
});
}

switch (body.role) {
case "patient":
return checkOTP(body);
case "hospital":
return checkOTP(body);
case "doctor":
return checkOTP(body);
case "receptionist":
return checkOTP(body);

default:
return Response.json({ error: "Invalid user" });
if (!allowedRoles.includes(body.role)) {
return Response.json({ error: "User role isn't valid." });
}

const result = await checkOTP(body);
return result;
} catch (error) {
console.error("Error during otp verification:", error);
return Response.json({ error: "Internal Server Error" });
Expand All @@ -50,5 +48,8 @@ async function checkOTP(body: bodyType) {

await collection.updateOne({ email }, { $set: { otp: "" } });

// setting session for user (stores jwt token in cookies named session)
await setSession(email, body.role);

return Response.json({ message: "ok" }, { status: 200 });
}
3 changes: 0 additions & 3 deletions app/components/otp/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"use client";

import setSessionReq from "@sessions/setSessionReq";
import {
Modal,
ModalBody,
Expand Down Expand Up @@ -66,8 +65,6 @@ export default function OtpSection({ userData }: userDataType) {
const sendingOtpPromise = new Promise((resolve) => {
setTimeout(async () => {
resolve(true);
// as the setSession method uses next/headers it can't be called from a client componnet
await setSessionReq(userData.email, userData.role);
router.push(`/${userData.role}`);
}, 1000);
});
Expand Down
2 changes: 1 addition & 1 deletion app/layouts/LandingPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {

export default function LandingPage() {
return (
<main className="scroll-smooth h-[100vh]">
<main className="scroll-smooth h-screen">
<NavBar />
<Intro />
<Lifeline />
Expand Down
2 changes: 1 addition & 1 deletion app/utils/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const commonadditionalDetails = {
gender: "",
contact: "",
profile:
"https://cdn.pixabay.com/photo/2021/07/02/04/48/user-6380868_1280.png",
"https://res.cloudinary.com/dtkfvp2ic/image/upload/v1715082439/110505291-heart-shape-illustration-health-medicine-concept-people-running-for-exercise-awareness-or-sport_eyu2iw.jpg",
address: {
address_line_1: "",
address_line_2: "",
Expand Down
2 changes: 0 additions & 2 deletions lib/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ export async function signupAction(formData: FormData) {

const user = { firstname, lastname, username, email, password, role };

console.log(user);

try {
const serverUrl = getBaseUrl();
const response = await fetch(`${serverUrl}/api/auth/signup`, {
Expand Down
4 changes: 2 additions & 2 deletions emails/otpmail.tsx → lib/emails/templates.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Html } from "@react-email/html";
import { getCurrentDateFormatted } from "@/app/utils/getDate";
import { getCurrentDateFormatted } from "@utils/getDate";

export default function WelcomeEmail(name: string, otp: string) {
export default function OtpTemplate(name: string, otp: string) {
return (
<Html>
<div
Expand Down
Loading

0 comments on commit 92d742b

Please sign in to comment.