Skip to content

Commit

Permalink
feat: 🚧 crated patient adding form
Browse files Browse the repository at this point in the history
  • Loading branch information
yeasin2002 committed Sep 23, 2024
1 parent b7c4956 commit 1e2d34a
Show file tree
Hide file tree
Showing 16 changed files with 492 additions and 170 deletions.
Binary file modified bun.lockb
Binary file not shown.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-progress": "^1.0.3",
"@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-tabs": "^1.0.4",
Expand All @@ -41,6 +42,7 @@
"@tanstack/react-query": "^5.52.0",
"@tanstack/react-query-devtools": "^5.52.0",
"bcryptjs": "^2.4.3",
"caniuse-lite": "",
"chart.js": "^4.4.3",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
Expand Down
34 changes: 33 additions & 1 deletion src/actions/auth.actions/patient-auth.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import bcrypt from "bcryptjs";

import connectDB from "@/lib/connectDB";
import { authSession } from "@/lib/auth";
import { loginFormData, registerFormData } from "@/types";
import { loginFormData, patientSchemaForDoctorToAddData, registerFormData } from "@/types";
import { db } from "@/model";

export const loginPatient_server = async ({ email, password }: loginFormData) => {
Expand Down Expand Up @@ -66,3 +66,35 @@ export const registerPatient_server = async (data: registerFormData) => {
};
}
};

export const registerPatientByDoctor_server = async (data: patientSchemaForDoctorToAddData) => {
try {
// check If Email Already Exists
const checkDoctorExist = await mongoose.models.Doctor.findOne({
email: data.email,
});
if (checkDoctorExist) throw new Error("Doctor with this email already exists");

// Hash Password
const hashedPassword = await bcrypt.hash(data.password, 10);

await mongoose.models.Doctor.create({
name: data.name,
email: data.email,
password: hashedPassword,
gender: data.gender,
// phone: data.phone,
// profileImage: data.profileImage,
});

return {
success: true,
message: "success",
};
} catch (error: any) {
return {
success: false,
message: error?.message as string,
};
}
};
2 changes: 1 addition & 1 deletion src/app/[lang]/admin/(dashboard)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ const Admin = async () => {
<DashboardAnalytics />
</section>
);
};
};

export default Admin;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const genderOption = ["male", "female"];
export const sweetEatingLevelOption = ["low", "medium", "high", "no"];
export const regularBrushingTimeOption = [1, 2];
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
"use client";

import { zodResolver } from "@hookform/resolvers/zod";
import { useForm, Controller } from "react-hook-form";
import Swal from "sweetalert2";
import toast from "react-hot-toast";

import { registerPatientByDoctor_server } from "@/actions/auth.actions";
import { DatePicker, Email, InputCombo, InputComboForPassword, InputComboSelect, LinkTo, Lock } from "@/components";
import { loginFormSchema } from "@/schema";
import { patientSchemaForDoctorToAddData } from "@/types";
import { genderOption, regularBrushingTimeOption, sweetEatingLevelOption } from "./PatientRegisterByDoctor.options";

interface Props {
dictionary: any;
}

export const PatientRegisterByDoctor = ({ dictionary }: Props) => {
const {
register,
handleSubmit,
formState: { errors },
control,
setError,
} = useForm<patientSchemaForDoctorToAddData>({
resolver: zodResolver(loginFormSchema),
});

const onSubmit = async (data: patientSchemaForDoctorToAddData) => {
try {
const res = await registerPatientByDoctor_server(data);
console.log("🚀 ~ onSubmit ~ res:", res);

if (!res.success) throw new Error(res.message);
toast.success(res?.message);
} catch (error: any) {
// toast.error(error?.message || "Invalid email or password", {id: toastId,});
return Swal.fire({
icon: "error",
title: "authentication failed.",
text: error?.message || "Something went wrong!",
});
}
};

return (
<form onSubmit={handleSubmit(onSubmit)} className="p-10">
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
<InputCombo
register={register("name")}
error={errors.name?.message}
icon={<Email />}
placeholder={"Enter your name"}
labelName="Full Name"
/>

<InputComboSelect
register={register("gender")}
error={errors.name?.message}
name={"Enter your name"}
labelName="Full Name"
item={genderOption?.map((item) => ({ label: item, value: item }))}
/>

<InputComboSelect
register={register("gender")}
error={errors.name?.message}
name={"Enter your name"}
labelName="Full Name"
item={regularBrushingTimeOption?.map((item) => ({ label: item.toString(), value: item.toString() }))}
/>

<InputComboSelect
register={register("sweetEatingLevel")}
error={errors.sweetEatingLevel?.message}
name={"How much do you eat sweets"}
labelName="Sweet Eating Level"
item={sweetEatingLevelOption?.map((item) => ({ label: item, value: item }))}
/>

<InputCombo
register={register("phone1")}
error={errors.phone1?.message}
icon={<Email />}
placeholder={"Enter your primary phone number"}
labelName="Primary Contact Number"
/>

<InputCombo
register={register("phone2")}
error={errors.phone2?.message}
icon={<Email />}
placeholder={"Enter your secondary phone number- optional"}
labelName="Secondary Contact Number"
/>

<InputCombo
register={register("reference")}
error={errors.reference?.message}
icon={<Email />}
placeholder={"Reference by any other doctors"}
labelName="Reference"
/>
<InputCombo
register={register("address")}
error={errors.address?.message}
icon={<Email />}
placeholder={"Enter your address"}
labelName="address"
/>

<InputCombo
register={register("email")}
error={errors.email?.message}
icon={<Email />}
placeholder={"Enter your email"}
labelName="Email"
/>

<InputCombo
register={register("occupation")}
error={errors.occupation?.message}
icon={<Email />}
placeholder={"What is your occupation"}
labelName="Occupation"
/>

<InputComboForPassword
register={register("password")}
error={errors.password?.message}
icon={<Lock />}
placeholder="*******"
labelName="Password"
/>

<InputCombo
register={register("occupation")}
error={errors.occupation?.message}
icon={<Email />}
placeholder={"What is your occupation"}
labelName="Occupation"
/>

<Controller
control={control}
name={"dateOfBirth"}
render={(files) => (
<div>
<DatePicker
date={files.field.value}
setDate={files.field.onChange}
className="w-full 2xl:px-4 2xl:py-6 2xl:text-lg"
defaultPlaceholder={"Birth Date"}
/>
{errors?.dateOfBirth && <p className="text-red-500"> Date is required! </p>}
</div>
)}
/>
</div>

<button
className="mt-6 w-full transform rounded-lg bg-blue-500 px-6 py-3 text-sm font-medium capitalize tracking-wide text-white transition-colors duration-300 hover:bg-blue-400 focus:outline-none focus:ring focus:ring-blue-300 focus:ring-opacity-50 2xl:py-4 2xl:text-2xl"
type="submit"
>
Sing UP
</button>
</form>
);
};
140 changes: 9 additions & 131 deletions src/app/[lang]/admin/(dashboard)/patient/add/page.tsx
Original file line number Diff line number Diff line change
@@ -1,137 +1,15 @@
"use client";
import { getDictionary } from "@/Internationalization";
import { PatientRegisterByDoctor } from "./PatientRegisterByDoctor";

import { InputComboMax } from "@/components";
import { NewPatientSchema } from "@/schema";
import { zodResolver } from "@hookform/resolvers/zod";
import React, { useState } from "react";
import { useForm } from "react-hook-form";
import toast from "react-hot-toast";
import * as z from "zod";

type PatientData = z.infer<typeof NewPatientSchema>;
const AddPatients = () => {
const [isLoading, setIsLoading] = useState(false);
const {
register,
handleSubmit,
formState: { errors },
setError,
setValue,
watch,
control,
getValues,
} = useForm<PatientData>({
resolver: zodResolver(NewPatientSchema),
});

const onSubmit = (data: PatientData) => {};
interface Props {
params: { lang: string };
}

const AddPatients = async ({ params }: Props) => {
const dictionary = await getDictionary(params.lang, "useAuth");
return (
<section className="min-h-screen bg-white dark:bg-gray-900">
<div className="mx-auto flex w-full items-center p-8 lg:px-12">
<div className="w-full">
<h1 className="text-2xl font-semibold capitalize tracking-wider text-gray-800 dark:text-white">
Get your free account now.
</h1>
<p className="mt-4 text-gray-500 dark:text-gray-400">
Let’s get you all set up so you can verify your personal account and begin setting up your profile.
</p>
<div className="mt-6">
<h1 className="text-gray-500 dark:text-gray-300">Select type of account</h1>
<div className="mt-3 md:-mx-2 md:flex md:items-center">
<button
type="button"
className="flex w-full justify-center rounded-lg bg-blue-500 px-6 py-3 text-white focus:outline-none md:mx-2 md:w-auto"
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M21 13.255A23.931 23.931 0 0112 15c-3.183 0-6.22-.62-9-1.745M16 6V4a2 2 0 00-2-2h-4a2 2 0 00-2 2v2m4 6h.01M5 20h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
/>
</svg>
<span className="mx-2">client</span>
</button>
<button
type="button"
className="mt-4 flex w-full justify-center rounded-lg border border-blue-500 px-6 py-3 text-blue-500 focus:outline-none dark:border-blue-400 dark:text-blue-400 md:mx-2 md:mt-0 md:w-auto"
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
/>
</svg>
<span className="mx-2">worker</span>
</button>
</div>
</div>
<form className="mt-8 grid grid-cols-1 gap-6 md:grid-cols-2" onSubmit={handleSubmit(onSubmit)}>
<InputComboMax
register={register("name")}
error={errors.name?.message}
labelName="First Name"
placeholder="mr."
/>

<InputComboMax
register={register("email")}
error={errors.email?.message}
labelName="Email"
placeholder="[email protected]"
/>
<InputComboMax
register={register("phone1")}
error={errors.phone1?.message}
labelName="Contact Number"
placeholder="XXX-XX-XXXX-XXX"
/>
<InputComboMax
register={register("address")}
error={errors.address?.message}
labelName="address"
placeholder="example: Dhaka"
/>
<InputComboMax
register={register("password")}
error={errors.password?.message}
labelName="password"
placeholder="Enter your password"
/>

<button className="flex w-full transform items-center justify-between rounded-lg bg-blue-500 px-6 py-3 text-sm capitalize tracking-wide text-white transition-colors duration-300 hover:bg-blue-400 focus:outline-none focus:ring focus:ring-blue-300 focus:ring-opacity-50">
<span>Sign Up </span>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5 rtl:-scale-x-100"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fillRule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clipRule="evenodd"
/>
</svg>
</button>
</form>
</div>
</div>
<section>
<PatientRegisterByDoctor dictionary={dictionary} />
</section>
);
};
Expand Down
2 changes: 1 addition & 1 deletion src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
await connectDB();
const user = await db.Doctors.findOne({ email: email });
if (!user) throw new Error("User not found");
if (user?.isPending) throw new Error("Wait for admin approval");
if (user?.isPending) throw new Error("Wait for admin approval");

const isMatch = await bcrypt.compare(password, user?.password);
if (!isMatch) throw new Error("Email or Password is not correct");
Expand Down
Loading

0 comments on commit 1e2d34a

Please sign in to comment.