diff --git a/public/images/addTeamMemberButton.png b/public/images/addTeamMemberButton.png
new file mode 100644
index 0000000..2cd248c
Binary files /dev/null and b/public/images/addTeamMemberButton.png differ
diff --git a/public/images/addTeamMemberPlaceholder.png b/public/images/addTeamMemberPlaceholder.png
new file mode 100644
index 0000000..f189836
Binary files /dev/null and b/public/images/addTeamMemberPlaceholder.png differ
diff --git a/src/app/(pages)/admin/addTeamMember/page.tsx b/src/app/(pages)/admin/addTeamMember/page.tsx
new file mode 100644
index 0000000..9866f61
--- /dev/null
+++ b/src/app/(pages)/admin/addTeamMember/page.tsx
@@ -0,0 +1,280 @@
+"use client";
+
+import { useRouter } from "next/navigation";
+import Image from "next/image";
+import { X } from "lucide-react";
+import { useState } from "react";
+import ErrorText from "../../../components/errorText";
+import SuccessText from "../../../components/successText";
+import ErrorFormFieldText from "../../../components/errorFormFieldText";
+import { teamMemberSchema } from "../../../../schemas/teamMember";
+import LoadingSpinner from "../../../components/loadingSpinner";
+
+const AddTeamMember = () => {
+ const router = useRouter();
+ const [loading, setLoading] = useState(false); // State to manage loading state of API Call
+ const [uploadedImageUrl, setUploadedImageUrl] = useState(null);
+ const [error, setError] = useState("");
+ const [success, setSuccess] = useState("");
+ const [validationErrors, setValidationErrors] = useState<{
+ [key: string]: string;
+ }>({}); // State to manage validation errors for form fields
+ const [formData, setFormData] = useState({
+ name: "",
+ designation: "",
+ email: "",
+ linkedin: "",
+ });
+
+ const handleImageUpload = async (event) => {
+ setError("");
+ const file = event.target.files[0];
+ if (file && file.type.startsWith("image/")) {
+ const imageFormData = new FormData();
+ imageFormData.append("file", file);
+ imageFormData.append(
+ "upload_preset",
+ process.env.NEXT_PUBLIC_CLOUDINARY_UPLOAD_PRESET,
+ ); // Replace with your preset
+
+ try {
+ const response = await fetch(
+ `https://api.cloudinary.com/v1_1/${process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME}/image/upload`,
+ {
+ method: "POST",
+ body: imageFormData,
+ },
+ );
+ const data = await response.json();
+ setUploadedImageUrl(data.secure_url);
+ } catch (err) {
+ console.error("Upload failed:", err);
+ setError("Failed to upload image. Please try again.");
+ }
+ } else {
+ alert("Please upload a valid image file.");
+ }
+ };
+
+ const handleChange = (e) => {
+ e.preventDefault();
+ setError("");
+ const { name, value } = e.target;
+ setFormData((prevData) => ({ ...prevData, [name]: value }));
+ setValidationErrors((prevErrors) => ({ ...prevErrors, [name]: "" }));
+ };
+
+ const addMember = async (e) => {
+ e.preventDefault();
+ try {
+ teamMemberSchema.parse(formData);
+ try {
+ const response = await fetch("/api/v1/addTeamMember", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ name: formData.name,
+ designation: formData.designation,
+ email: formData.email,
+ ...(formData.linkedin && { linkedin: formData.linkedin }),
+ ...(uploadedImageUrl && { image: uploadedImageUrl }),
+ }),
+ });
+
+ const data = await response.json();
+ if (!response.ok) {
+ setLoading(false);
+ setError(data.message || "Something went wrong.");
+ throw new Error(data.message || "Something went wrong.");
+ }
+ setSuccess("Team member added successfully. You can add more members.");
+ setError("");
+ setTimeout(() => {
+ setLoading(false);
+ router.push("/admin");
+ }, 3000);
+ } catch (err) {
+ setLoading(false);
+ setError(`${err}`);
+ }
+ } catch (err) {
+ if (err.errors) {
+ // Mapping the error messages to the respective fields
+ const newValidationErrors = {};
+ err.errors.forEach((errr) => {
+ newValidationErrors[errr.path[0]] = errr.message;
+ });
+ setValidationErrors(newValidationErrors);
+ }
+ }
+ };
+
+ return (
+
+
+
+
+
+ Add Member
+
+
router.push("/admin")}
+ className="absolute right-[10vw] md:w-11 md:h-9 w-10 h-9 hover:scale-[1.1] cursor-pointer"
+ />
+
+
+
+
+ {uploadedImageUrl && (
+
+ )}
+
+
+
+
+
+
+
+ );
+};
+
+export default AddTeamMember;
diff --git a/src/app/(pages)/admin/page.tsx b/src/app/(pages)/admin/page.tsx
new file mode 100644
index 0000000..50bf376
--- /dev/null
+++ b/src/app/(pages)/admin/page.tsx
@@ -0,0 +1,102 @@
+"use client";
+
+import { useRouter } from "next/navigation";
+import { useEffect, useState } from "react";
+import Image from "next/image";
+import "../../globals.scss";
+import NavbarAdmin from "../../components/NavbarAdmin";
+
+const Admin = () => {
+ const router = useRouter();
+ const [teamMembers, setTeamMembers] = useState([]);
+ const [error, setError] = useState("");
+
+ useEffect(() => {
+ const fetchTeamMembers = async () => {
+ try {
+ const res = await fetch("/api/v1/getAllTeamMembers", {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+ const data = await res.json();
+
+ if (!res.ok) {
+ throw new Error(data.message || "Failed to fetch team members");
+ }
+ setTeamMembers(data.teamMembers);
+ } catch (err) {
+ console.error(err.message);
+ setError("Failed to load team members.");
+ }
+ };
+ fetchTeamMembers();
+ }, []);
+
+ if (error) {
+ return {error}
;
+ }
+
+ return (
+
+
+
+
+ Present Team:
+
+
+ {teamMembers.map((member) => {
+ return (
+
+
+
+
+
+ {member.name}
+
+
+ {member.designation}
+
+
+ );
+ })}
+
+
+
+
+ );
+};
+export default Admin;
diff --git a/src/app/(pages)/layout.tsx b/src/app/(pages)/layout.tsx
index 358fb71..4069d57 100644
--- a/src/app/(pages)/layout.tsx
+++ b/src/app/(pages)/layout.tsx
@@ -1,12 +1,27 @@
+"use client";
+
+import { usePathname } from "next/navigation";
import Navbar from "../components/Navbar";
import Footer from "../components/Footer";
export default function RootLayout({ children }) {
+ const pathname = usePathname();
+
+ // Paths where Navbar and Footer should be hidden
+ const noLayoutPaths = [
+ "/login",
+ "/signup",
+ "/resetPassword",
+ "/admin/addTeamMember",
+ "/admin",
+ ];
+
+ const shouldHideLayout = noLayoutPaths.includes(pathname);
return (
-
+ {!shouldHideLayout && }
{children}
-
+ {!shouldHideLayout && }
);
}
diff --git a/src/app/api/v1/signIn/route.ts b/src/app/api/v1/signIn/route.ts
index 5e9fbac..77e6322 100644
--- a/src/app/api/v1/signIn/route.ts
+++ b/src/app/api/v1/signIn/route.ts
@@ -42,6 +42,9 @@ export async function POST(req: NextRequest) {
{ status: 200 },
);
+ if (user.role === "admin") {
+ response.cookies.set("adminToken", token, { httpOnly: true, path: "/" });
+ }
response.cookies.set("signInToken", token, { httpOnly: true, path: "/" });
return response;
diff --git a/src/app/api/v1/signOut/route.ts b/src/app/api/v1/signOut/route.ts
index f5fe407..9388bc7 100644
--- a/src/app/api/v1/signOut/route.ts
+++ b/src/app/api/v1/signOut/route.ts
@@ -12,6 +12,11 @@ export async function GET() {
expires: new Date(0),
});
+ response.cookies.set("adminToken", "", {
+ httpOnly: true,
+ expires: new Date(0),
+ });
+
return response;
} catch (error: any) {
return NextResponse.json({ message: "Sign out failed", success: false });
diff --git a/src/app/components/NavbarAdmin.tsx b/src/app/components/NavbarAdmin.tsx
new file mode 100644
index 0000000..0e761d5
--- /dev/null
+++ b/src/app/components/NavbarAdmin.tsx
@@ -0,0 +1,210 @@
+"use client";
+
+import Image from "next/image";
+import { useEffect, useState } from "react";
+import Link from "next/link";
+import { Icon } from "@iconify/react";
+import { usePathname, useRouter } from "next/navigation";
+
+export default function NavbarAdmin() {
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
+ const [isAuthenticated, setIsAuthenticated] = useState(false);
+ const router = useRouter();
+ const path = usePathname();
+ useEffect(() => {}, [path]);
+
+ useEffect(() => {
+ const fetchUserData = async () => {
+ try {
+ const response = await fetch("/api/v1/getUser");
+ if (response.ok) {
+ const data = await response.json();
+ if (data) {
+ setIsAuthenticated(true);
+ } else {
+ setIsAuthenticated(false);
+ }
+ }
+ } catch (error) {
+ console.error("Error fetching data:", error);
+ }
+ };
+ fetchUserData();
+ }, [path]);
+
+ const handleLogOut = async () => {
+ await fetch("/api/v1/signOut", {
+ method: "GET",
+ })
+ .then((res) => res.json())
+ .then((data) => {
+ router.push("/signIn");
+ console.log(data.message);
+ })
+ .catch((err) => console.error(err));
+ };
+
+ const toggleMenu = () => setIsMenuOpen(!isMenuOpen);
+
+ const menuLinks = [
+ { name: "Home", href: "/" },
+ { name: "Team Management", href: "/admin" },
+ { name: "Testimonial Management", href: "/admin" },
+ ];
+ return (
+
+ );
+}
diff --git a/src/app/components/successText.tsx b/src/app/components/successText.tsx
index 720f320..ea91a7b 100644
--- a/src/app/components/successText.tsx
+++ b/src/app/components/successText.tsx
@@ -1,7 +1,7 @@
const SuccessText = ({ message }) => {
return (