Skip to content

Commit

Permalink
Merge pull request #88 from TEDx-SJEC/admin-page
Browse files Browse the repository at this point in the history
Admin page
  • Loading branch information
Vyshnav001 authored Dec 2, 2024
2 parents 55cf72f + 005838a commit 847005f
Show file tree
Hide file tree
Showing 27 changed files with 939 additions and 568 deletions.
43 changes: 37 additions & 6 deletions src/app/actions/change-role.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,56 @@
"use server";

import prisma from "@/server/db";
import { getServerSideSession } from "@/lib/get-server-session";
import { revalidatePath } from "next/cache";
import getErrorMessage from "@/utils/getErrorMessage";
import { UserRole,ADMIN_USERS_PATH } from "@/constants";

async function updateUserRole(id: string, role: string) {
async function updateUserRole(id: string, role: UserRole) {
const VALID_ROLES = Object.values(UserRole);
if (!VALID_ROLES.includes(role)) {
throw new Error(`Invalid role: ${role}`);
}
const session = await getServerSideSession();
if (!session || session.user.role !== UserRole.ADMIN) {
throw new Error(`Unauthorized Access...`);
}
try {
const updatedUser = await prisma.user.update({
where: { id },
data: { role },
});
revalidatePath("/admin/users");
revalidatePath(ADMIN_USERS_PATH);
return updatedUser;
} catch (error) {
console.error("Error updating user role:", error);
return null;
console.error("Error updating user role:", getErrorMessage(error));
throw new Error("Failed to update user role. Please try again later.");
}
}

export const makeAdmin = async (userId: string) => {
return await updateUserRole(userId, "ADMIN");
try {
return await updateUserRole(userId, UserRole.ADMIN);
} catch (error) {
console.error("Failed to make user admin:", getErrorMessage(error));
return null;
}
};

export const makeParticipant = async (userId: string) => {
return await updateUserRole(userId, "PARTICIPANT");
try {
return await updateUserRole(userId, UserRole.PARTICIPANT);
} catch (error) {
console.error("Failed to make user participant:", getErrorMessage(error));
return null;
}
};

export const makeCoordinator = async (userId: string) => {
try {
return await updateUserRole(userId, UserRole.COORDINATOR);
} catch (error) {
console.error("Failed to make user coordinator:", getErrorMessage(error));
return null;
}
};
51 changes: 40 additions & 11 deletions src/app/actions/create-coupon-code.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,46 @@
"use server";
import { generateCouponCode } from "@/lib/helper";
import prisma from "@/server/db";
import { couponSchema } from "@/utils/zod-schemas";

export const saveCoupon = async (
coupon: string,
id: string,
discount: string = "20",
coupon: string,
createdById: string,
discount: number = 20,
numberOfCoupons: number = 1
) => {
const resp = await prisma.referral.create({
data: {
code: coupon,
isUsed: false,
createdById: id,
discountPercentage: discount,
},
});
try {
const validatCoupon = couponSchema.parse({ coupon, createdById, discount });

// Check if the coupon already exists
const couponExists = await prisma.referral.findFirst({
where: { code: validatCoupon.coupon },
});
if (couponExists) {
throw new Error("Coupon code already exists");
}

// Create coupons
const createCoupon = async (code: string) => {
return prisma.referral.create({
data: {
code,
isUsed: false,
createdById: validatCoupon.createdById,
discountPercentage: validatCoupon.discount.toString(),
},
});
};

const couponCodes =
numberOfCoupons === 1
? [validatCoupon.coupon]
: Array.from({ length: numberOfCoupons }, () => generateCouponCode(10));

const responses = await Promise.all(couponCodes.map(createCoupon));
return responses;
} catch (error) {
console.error("Error creating coupon:", error);
throw new Error("Failed to create coupon. Please try again later.");
}
};
15 changes: 15 additions & 0 deletions src/app/actions/get-payment-count.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"use server";

import { getServerSideSession } from "@/lib/get-server-session";
import prisma from "@/server/db";

export default async function getPaymentCount() {
const session = await getServerSideSession();
if (!session) {
return null;
}

const paymentCount = await prisma.payment.count();

return paymentCount;
}
25 changes: 16 additions & 9 deletions src/app/actions/get-user-by-id.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import prisma from "@/server/db"; // Adjust the import based on your structure

export async function getUserById(userId: string) {
const user = await prisma.user.findUnique({
where: { id: userId },
select: {
id: true,
role: true, // Include any other fields you need
},
});

return user;
try {
const user = await prisma.user.findUnique({
where: { id: userId },
select: {
id: true,
role: true,
},
});
if (!user) {
throw new Error(`User with ID ${userId} not found`);
}
return user;
} catch (error) {
console.error("Error getting user by id:", error);
throw new Error("Failed to get user. Please try again later.");
}
}
14 changes: 14 additions & 0 deletions src/app/actions/get-user-count.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"use server";
import prisma from "@/server/db";
import { getServerSideSession } from "@/lib/get-server-session";

export default async function getUserCount() {
const session = await getServerSideSession();
if (!session) {
return null;
}

const userCount = await prisma.user.count();

return userCount;
}
12 changes: 9 additions & 3 deletions src/app/actions/is-allowed-to-access.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,16 @@
import prisma from "@/server/db";

export const isAllowedToAccess = async (email: string): Promise<boolean> => {
if (!email) return false;
try {
const user = await prisma.sjecUser.findFirst({
where: {
email: email,
},
where: {
email: email,
},
});
return user !== null;
} catch (error) {
console.error("Error getting user by email:", error);
return false;
}
};
43 changes: 27 additions & 16 deletions src/app/actions/submit-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,37 @@
import { getServerSideSession } from "@/lib/get-server-session";
import prisma from "@/server/db";
import { FormDataInterface } from "@/types";
import { z } from "zod";

const amountSchema = z.number().positive("Amount must be a positive number.");

export async function submitForm(data: FormDataInterface, amount: number) {
const session = await getServerSideSession();
if (!session) {
return;
throw new Error("User is not authenticated");
}
const validatedAmount = amountSchema.parse(amount);

const totalAmount = Math.round(validatedAmount + validatedAmount * 0.02);

return await prisma.form.create({
data: {
name: data.name,
usn: data.usn,
email: data.email,
foodPreference: data.foodPreference,
contact: data.phone,
designation: data.designation,
paidAmount: amount,
photo: data.photo,
collegeIdCard: data.idCard,
createdById: session.user.id,
entityName: data.entityName,
},
});
try {
return await prisma.form.create({
data: {
name: data.name,
usn: data.usn,
email: data.email,
foodPreference: data.foodPreference,
contact: data.phone,
designation: data.designation,
paidAmount: totalAmount,
photo: data.photo,
collegeIdCard: data.idCard,
createdById: session.user.id,
entityName: data.entityName,
},
});
} catch (error) {
console.error("Error creating form:", error);
throw new Error("Failed to submit the form. Please try again later.");
}
}
58 changes: 50 additions & 8 deletions src/app/admin/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { Inter } from "next/font/google";
import "./globals.css";
import Providers from "@/components/layout/Provider";
import { AdminNavbar } from "@/components/admin/Navbar/navbar";
import { useSession } from "next-auth/react";
import { signIn, useSession } from "next-auth/react";
import { useEffect, useState } from "react";
import { tailChase } from "ldrs";

const inter = Inter({ subsets: ["latin"] });

Expand All @@ -14,22 +16,62 @@ export default function RootLayout({
}: Readonly<{
children: React.ReactNode;
}>) {
const { data: session } = useSession({
const { data: session, status } = useSession({
required: true,
onUnauthenticated: async () => {
await signIn("google");
},
});

if (typeof window !== "undefined") {
tailChase.register();
}

const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
if (status === "loading") {
setIsLoading(true);
} else {
setIsLoading(false);
}
}, [status]);

if (isLoading || status !== "authenticated" || !session) {
// Show the loading spinner if session is loading or not authenticated
return (
<div className="flex flex-col items-center justify-center min-h-screen">
<l-tail-chase
size={"88"}
speed={"1.75"}
color={"#FF0000"}
></l-tail-chase>
</div>
);
}

if (!session) {
return (
<div className="w-screen h-screen flex justify-center items-center">
Unauthorized
<div className="w-screen h-screen flex justify-center items-center bg-black text-gray-200">
<div className="text-center">
<h1 className="text-3xl font-bold mb-2 text-red-500">Unauthorized</h1>
<p className="text-gray-400">
You need to log in to access this page.
</p>
</div>
</div>
);
}

if (session.user.role !== "ADMIN") {
if (session.user.role !== "ADMIN" && session.user.role !== "COORDINATOR") {
return (
<div className="w-screen h-screen flex justify-center items-center">
Forbidden
<div className="w-screen h-screen flex justify-center items-center bg-black text-gray-200">
<div className="text-center">
<h1 className="text-3xl font-bold mb-2 text-red-500">Forbidden</h1>
<p className="text-gray-400">
You do not have the required permissions to view this page.
</p>
</div>
</div>
);
}
Expand All @@ -40,7 +82,7 @@ export default function RootLayout({
<Providers>
<div className="flex h-screen overflow-hidden">
<AdminNavbar />
<main className="flex-1 overflow-auto bg-indigo-50">
<main className="ml-16 md:ml-0 flex-1 overflow-y-auto bg-gray-800">
{children}
</main>
</div>
Expand Down
31 changes: 31 additions & 0 deletions src/app/admin/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"use client"
import React from "react";

const Loading: React.FC = () => {
return (
<div className="flex items-center justify-center h-screen text-red-500">
<div className="loader"></div>
<style jsx>{`
.loader {
border: 8px solid #f3f3f3; /* Light grey */
border-top: 8px solid #3498db; /* Blue */
border-radius: 50%;
width: 60px;
height: 60px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
`}</style>
</div>
);
};

export default Loading;
Loading

0 comments on commit 847005f

Please sign in to comment.