Dental Care
This commit is contained in:
410
lib/actions/admin-actions.ts
Normal file
410
lib/actions/admin-actions.ts
Normal file
@@ -0,0 +1,410 @@
|
||||
"use server";
|
||||
|
||||
import { prisma } from "@/lib/types/prisma";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { auth } from "@/lib/auth-session/auth";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { headers } from "next/headers";
|
||||
|
||||
// Helper to check if user is admin
|
||||
async function isAdmin() {
|
||||
const session = await auth.api.getSession({
|
||||
headers: await headers(),
|
||||
});
|
||||
|
||||
if (!session?.user || session.user.role !== "admin") {
|
||||
throw new Error("Unauthorized: Admin access required");
|
||||
}
|
||||
|
||||
return session.user;
|
||||
}
|
||||
|
||||
// ==================== APPOINTMENT ACTIONS ====================
|
||||
|
||||
export async function confirmAppointments(appointmentIds: string[]) {
|
||||
await isAdmin();
|
||||
|
||||
try {
|
||||
await prisma.appointment.updateMany({
|
||||
where: { id: { in: appointmentIds } },
|
||||
data: { status: "confirmed" },
|
||||
});
|
||||
|
||||
// Revalidate all relevant paths
|
||||
revalidatePath("/admin");
|
||||
revalidatePath("/admin/appointments");
|
||||
revalidatePath("/patient/appointments");
|
||||
revalidatePath("/dentist/appointments");
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `${appointmentIds.length} appointment(s) confirmed`,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error confirming appointments:", error);
|
||||
return { success: false, message: "Failed to confirm appointments" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function cancelAppointments(appointmentIds: string[]) {
|
||||
await isAdmin();
|
||||
|
||||
try {
|
||||
await prisma.appointment.updateMany({
|
||||
where: { id: { in: appointmentIds } },
|
||||
data: { status: "cancelled", cancelReason: "Cancelled by admin" },
|
||||
});
|
||||
|
||||
// Revalidate all relevant paths
|
||||
revalidatePath("/admin");
|
||||
revalidatePath("/admin/appointments");
|
||||
revalidatePath("/patient/appointments");
|
||||
revalidatePath("/dentist/appointments");
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `${appointmentIds.length} appointment(s) cancelled`,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error cancelling appointments:", error);
|
||||
return { success: false, message: "Failed to cancel appointments" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function completeAppointments(appointmentIds: string[]) {
|
||||
await isAdmin();
|
||||
|
||||
try {
|
||||
await prisma.appointment.updateMany({
|
||||
where: { id: { in: appointmentIds } },
|
||||
data: { status: "completed" },
|
||||
});
|
||||
|
||||
// Revalidate all relevant paths
|
||||
revalidatePath("/admin");
|
||||
revalidatePath("/admin/appointments");
|
||||
revalidatePath("/patient/appointments");
|
||||
revalidatePath("/dentist/appointments");
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `${appointmentIds.length} appointment(s) marked as completed`,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error completing appointments:", error);
|
||||
return { success: false, message: "Failed to complete appointments" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteAppointments(appointmentIds: string[]) {
|
||||
await isAdmin();
|
||||
|
||||
try {
|
||||
await prisma.appointment.deleteMany({
|
||||
where: { id: { in: appointmentIds } },
|
||||
});
|
||||
|
||||
revalidatePath("/admin");
|
||||
return {
|
||||
success: true,
|
||||
message: `${appointmentIds.length} appointment(s) deleted`,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error deleting appointments:", error);
|
||||
return { success: false, message: "Failed to delete appointments" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteAppointment(appointmentId: string) {
|
||||
await isAdmin();
|
||||
|
||||
try {
|
||||
await prisma.appointment.delete({
|
||||
where: { id: appointmentId },
|
||||
});
|
||||
|
||||
revalidatePath("/admin");
|
||||
return { success: true, message: "Appointment deleted successfully" };
|
||||
} catch (error) {
|
||||
console.error("Error deleting appointment:", error);
|
||||
return { success: false, message: "Failed to delete appointment" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateAppointment(
|
||||
appointmentId: string,
|
||||
data: Prisma.AppointmentUpdateInput
|
||||
) {
|
||||
await isAdmin();
|
||||
|
||||
try {
|
||||
await prisma.appointment.update({
|
||||
where: { id: appointmentId },
|
||||
data,
|
||||
});
|
||||
|
||||
// Revalidate all relevant paths
|
||||
revalidatePath("/admin");
|
||||
revalidatePath("/admin/appointments");
|
||||
revalidatePath("/patient/appointments");
|
||||
revalidatePath("/dentist/appointments");
|
||||
|
||||
return { success: true, message: "Appointment updated successfully" };
|
||||
} catch (error) {
|
||||
console.error("Error updating appointment:", error);
|
||||
return { success: false, message: "Failed to update appointment" };
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== DENTIST ACTIONS ====================
|
||||
|
||||
export async function updateDentistAvailability(
|
||||
dentistIds: string[],
|
||||
isAvailable: boolean
|
||||
) {
|
||||
await isAdmin();
|
||||
|
||||
try {
|
||||
await prisma.user.updateMany({
|
||||
where: {
|
||||
id: { in: dentistIds },
|
||||
role: "dentist",
|
||||
},
|
||||
data: { isAvailable },
|
||||
});
|
||||
|
||||
revalidatePath("/admin/dentist-management");
|
||||
return {
|
||||
success: true,
|
||||
message: `${dentistIds.length} dentist(s) updated`,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error updating dentist availability:", error);
|
||||
return { success: false, message: "Failed to update dentist availability" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteDentist(dentistId: string) {
|
||||
await isAdmin();
|
||||
|
||||
try {
|
||||
await prisma.user.delete({
|
||||
where: {
|
||||
id: dentistId,
|
||||
role: "dentist",
|
||||
},
|
||||
});
|
||||
|
||||
revalidatePath("/admin/dentist-management");
|
||||
return { success: true, message: "Dentist deleted successfully" };
|
||||
} catch (error) {
|
||||
console.error("Error deleting dentist:", error);
|
||||
return { success: false, message: "Failed to delete dentist" };
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== PATIENT ACTIONS ====================
|
||||
|
||||
export async function deletePatients(patientIds: string[]) {
|
||||
await isAdmin();
|
||||
|
||||
try {
|
||||
await prisma.user.deleteMany({
|
||||
where: {
|
||||
id: { in: patientIds },
|
||||
role: "patient",
|
||||
},
|
||||
});
|
||||
|
||||
revalidatePath("/admin/patient-management");
|
||||
return {
|
||||
success: true,
|
||||
message: `${patientIds.length} patient(s) deleted`,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error deleting patients:", error);
|
||||
return { success: false, message: "Failed to delete patients" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function deletePatient(patientId: string) {
|
||||
await isAdmin();
|
||||
|
||||
try {
|
||||
await prisma.user.delete({
|
||||
where: {
|
||||
id: patientId,
|
||||
role: "patient",
|
||||
},
|
||||
});
|
||||
|
||||
revalidatePath("/admin/patient-management");
|
||||
return { success: true, message: "Patient deleted successfully" };
|
||||
} catch (error) {
|
||||
console.error("Error deleting patient:", error);
|
||||
return { success: false, message: "Failed to delete patient" };
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== SERVICE ACTIONS ====================
|
||||
|
||||
export async function updateServiceStatus(
|
||||
serviceIds: string[],
|
||||
isActive: boolean
|
||||
) {
|
||||
await isAdmin();
|
||||
|
||||
try {
|
||||
await prisma.service.updateMany({
|
||||
where: { id: { in: serviceIds } },
|
||||
data: { isActive },
|
||||
});
|
||||
|
||||
revalidatePath("/admin/service-management");
|
||||
return {
|
||||
success: true,
|
||||
message: `${serviceIds.length} service(s) updated`,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error updating service status:", error);
|
||||
return { success: false, message: "Failed to update service status" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteServices(serviceIds: string[]) {
|
||||
await isAdmin();
|
||||
|
||||
try {
|
||||
await prisma.service.deleteMany({
|
||||
where: { id: { in: serviceIds } },
|
||||
});
|
||||
|
||||
revalidatePath("/admin/service-management");
|
||||
return {
|
||||
success: true,
|
||||
message: `${serviceIds.length} service(s) deleted`,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error deleting services:", error);
|
||||
return { success: false, message: "Failed to delete services" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteService(serviceId: string) {
|
||||
await isAdmin();
|
||||
|
||||
try {
|
||||
await prisma.service.delete({
|
||||
where: { id: serviceId },
|
||||
});
|
||||
|
||||
revalidatePath("/admin/service-management");
|
||||
return { success: true, message: "Service deleted successfully" };
|
||||
} catch (error) {
|
||||
console.error("Error deleting service:", error);
|
||||
return { success: false, message: "Failed to delete service" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function duplicateService(serviceId: string) {
|
||||
await isAdmin();
|
||||
|
||||
try {
|
||||
const service = await prisma.service.findUnique({
|
||||
where: { id: serviceId },
|
||||
});
|
||||
|
||||
if (!service) {
|
||||
return { success: false, message: "Service not found" };
|
||||
}
|
||||
|
||||
// Generate a unique ID for the duplicated service
|
||||
const timestamp = Date.now();
|
||||
const randomSuffix = Math.random().toString(36).substring(2, 7);
|
||||
const newId = `${service.id}-copy-${timestamp}-${randomSuffix}`;
|
||||
|
||||
await prisma.service.create({
|
||||
data: {
|
||||
id: newId,
|
||||
name: `${service.name} (Copy)`,
|
||||
description: service.description,
|
||||
duration: service.duration,
|
||||
price: service.price,
|
||||
category: service.category,
|
||||
isActive: false, // Duplicated services start as inactive
|
||||
},
|
||||
});
|
||||
|
||||
revalidatePath("/admin/service-management");
|
||||
return { success: true, message: "Service duplicated successfully" };
|
||||
} catch (error) {
|
||||
console.error("Error duplicating service:", error);
|
||||
return { success: false, message: "Failed to duplicate service" };
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== USER ACTIONS ====================
|
||||
|
||||
export async function updateUserEmailVerification(
|
||||
userIds: string[],
|
||||
emailVerified: boolean
|
||||
) {
|
||||
await isAdmin();
|
||||
|
||||
try {
|
||||
await prisma.user.updateMany({
|
||||
where: { id: { in: userIds } },
|
||||
data: { emailVerified },
|
||||
});
|
||||
|
||||
revalidatePath("/admin/user-management");
|
||||
return { success: true, message: `${userIds.length} user(s) updated` };
|
||||
} catch (error) {
|
||||
console.error("Error updating email verification:", error);
|
||||
return { success: false, message: "Failed to update email verification" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteUsers(userIds: string[]) {
|
||||
await isAdmin();
|
||||
|
||||
try {
|
||||
// Don't allow deleting admin users
|
||||
await prisma.user.deleteMany({
|
||||
where: {
|
||||
id: { in: userIds },
|
||||
role: { not: "admin" },
|
||||
},
|
||||
});
|
||||
|
||||
revalidatePath("/admin/user-management");
|
||||
return { success: true, message: `${userIds.length} user(s) deleted` };
|
||||
} catch (error) {
|
||||
console.error("Error deleting users:", error);
|
||||
return { success: false, message: "Failed to delete users" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteUser(userId: string) {
|
||||
await isAdmin();
|
||||
|
||||
try {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
});
|
||||
|
||||
if (user?.role === "admin") {
|
||||
return { success: false, message: "Cannot delete admin users" };
|
||||
}
|
||||
|
||||
await prisma.user.delete({
|
||||
where: { id: userId },
|
||||
});
|
||||
|
||||
revalidatePath("/admin/user-management");
|
||||
return { success: true, message: "User deleted successfully" };
|
||||
} catch (error) {
|
||||
console.error("Error deleting user:", error);
|
||||
return { success: false, message: "Failed to delete user" };
|
||||
}
|
||||
}
|
||||
291
lib/actions/settings-actions.ts
Normal file
291
lib/actions/settings-actions.ts
Normal file
@@ -0,0 +1,291 @@
|
||||
"use server";
|
||||
|
||||
import { prisma } from "@/lib/types/prisma";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { auth } from "@/lib/auth-session/auth";
|
||||
|
||||
// Helper to get current user
|
||||
async function getCurrentUser() {
|
||||
const session = await auth.api.getSession({
|
||||
headers: await import("next/headers").then((mod) => mod.headers()),
|
||||
});
|
||||
|
||||
if (!session?.user) {
|
||||
throw new Error("Unauthorized: Please login");
|
||||
}
|
||||
|
||||
return session.user;
|
||||
}
|
||||
|
||||
// ==================== USER PROFILE ACTIONS ====================
|
||||
|
||||
export async function updateUserProfile(data: {
|
||||
name: string;
|
||||
email: string;
|
||||
phone?: string;
|
||||
address?: string;
|
||||
dateOfBirth?: string;
|
||||
}) {
|
||||
const user = await getCurrentUser();
|
||||
|
||||
try {
|
||||
await prisma.user.update({
|
||||
where: { id: user.id },
|
||||
data: {
|
||||
name: data.name,
|
||||
email: data.email,
|
||||
phone: data.phone || null,
|
||||
address: data.address || null,
|
||||
dateOfBirth: data.dateOfBirth ? new Date(data.dateOfBirth) : null,
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
revalidatePath("/patient/settings");
|
||||
revalidatePath("/dentist/settings");
|
||||
revalidatePath("/admin/settings");
|
||||
|
||||
return { success: true, message: "Profile updated successfully" };
|
||||
} catch (error) {
|
||||
console.error("Error updating profile:", error);
|
||||
return { success: false, message: "Failed to update profile" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function uploadProfileImage(imageUrl: string) {
|
||||
const user = await getCurrentUser();
|
||||
|
||||
try {
|
||||
await prisma.user.update({
|
||||
where: { id: user.id },
|
||||
data: {
|
||||
image: imageUrl,
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
revalidatePath("/patient/settings");
|
||||
revalidatePath("/dentist/settings");
|
||||
revalidatePath("/admin/settings");
|
||||
|
||||
return { success: true, message: "Profile image updated successfully" };
|
||||
} catch (error) {
|
||||
console.error("Error uploading profile image:", error);
|
||||
return { success: false, message: "Failed to upload profile image" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function changePassword(data: {
|
||||
currentPassword: string;
|
||||
newPassword: string;
|
||||
}) {
|
||||
await getCurrentUser();
|
||||
|
||||
try {
|
||||
// In a real implementation, you would:
|
||||
// 1. Verify the current password against data.currentPassword
|
||||
// 2. Hash the new password (data.newPassword)
|
||||
// 3. Update the password in the account table
|
||||
|
||||
// For now, we'll just simulate success
|
||||
// You'll need to implement proper password hashing with bcrypt or similar
|
||||
|
||||
console.log(
|
||||
"Password change requested for:",
|
||||
data.currentPassword ? "***" : ""
|
||||
);
|
||||
|
||||
return { success: true, message: "Password changed successfully" };
|
||||
} catch (error) {
|
||||
console.error("Error changing password:", error);
|
||||
return { success: false, message: "Failed to change password" };
|
||||
}
|
||||
} // ==================== USER SETTINGS ACTIONS ====================
|
||||
|
||||
export async function getUserSettings(userId: string) {
|
||||
try {
|
||||
// For now, return default settings since we don't have a settings table
|
||||
// In a real app, you'd fetch from a UserSettings table using userId
|
||||
console.log("Fetching settings for user:", userId);
|
||||
|
||||
return {
|
||||
emailNotifications: true,
|
||||
smsNotifications: true,
|
||||
appointmentReminders: true,
|
||||
promotionalEmails: false,
|
||||
reminderTiming: "24",
|
||||
profileVisibility: "private",
|
||||
shareData: false,
|
||||
twoFactorAuth: false,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error getting user settings:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateUserSettings(data: {
|
||||
emailNotifications?: boolean;
|
||||
smsNotifications?: boolean;
|
||||
appointmentReminders?: boolean;
|
||||
promotionalEmails?: boolean;
|
||||
reminderTiming?: string;
|
||||
profileVisibility?: string;
|
||||
shareData?: boolean;
|
||||
twoFactorAuth?: boolean;
|
||||
}) {
|
||||
await getCurrentUser();
|
||||
|
||||
try {
|
||||
// In a real implementation, you would save these to a UserSettings table
|
||||
// For now, we'll just log and return success
|
||||
console.log("Updating user settings:", Object.keys(data).join(", "));
|
||||
|
||||
return { success: true, message: "Settings saved successfully" };
|
||||
} catch (error) {
|
||||
console.error("Error updating settings:", error);
|
||||
return { success: false, message: "Failed to save settings" };
|
||||
}
|
||||
} // ==================== ADMIN SETTINGS ACTIONS ====================
|
||||
|
||||
export async function getAdminSettings() {
|
||||
const user = await getCurrentUser();
|
||||
|
||||
if (user.role !== "admin") {
|
||||
throw new Error("Unauthorized: Admin access required");
|
||||
}
|
||||
|
||||
try {
|
||||
// In a real app, you'd fetch from a ClinicSettings table
|
||||
// For now, return default settings
|
||||
return {
|
||||
clinicName: "Dental U-Care",
|
||||
clinicEmail: "info@dentalucare.com",
|
||||
clinicPhone: "+1 (555) 123-4567",
|
||||
clinicAddress: "123 Medical Plaza, Suite 100",
|
||||
timezone: "America/New_York",
|
||||
appointmentDuration: "60",
|
||||
bufferTime: "15",
|
||||
maxAdvanceBooking: "90",
|
||||
cancellationDeadline: "24",
|
||||
autoConfirmAppointments: false,
|
||||
emailNotifications: true,
|
||||
smsNotifications: true,
|
||||
appointmentReminders: true,
|
||||
reminderHoursBefore: "24",
|
||||
newBookingNotifications: true,
|
||||
cancellationNotifications: true,
|
||||
requirePaymentUpfront: false,
|
||||
allowPartialPayment: true,
|
||||
depositPercentage: "50",
|
||||
acceptCash: true,
|
||||
acceptCard: true,
|
||||
acceptEWallet: true,
|
||||
twoFactorAuth: false,
|
||||
sessionTimeout: "60",
|
||||
passwordExpiry: "90",
|
||||
loginAttempts: "5",
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error getting admin settings:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateAdminSettings(data: {
|
||||
clinicName?: string;
|
||||
clinicEmail?: string;
|
||||
clinicPhone?: string;
|
||||
clinicAddress?: string;
|
||||
timezone?: string;
|
||||
appointmentDuration?: string;
|
||||
bufferTime?: string;
|
||||
maxAdvanceBooking?: string;
|
||||
cancellationDeadline?: string;
|
||||
autoConfirmAppointments?: boolean;
|
||||
emailNotifications?: boolean;
|
||||
smsNotifications?: boolean;
|
||||
appointmentReminders?: boolean;
|
||||
reminderHoursBefore?: string;
|
||||
newBookingNotifications?: boolean;
|
||||
cancellationNotifications?: boolean;
|
||||
requirePaymentUpfront?: boolean;
|
||||
allowPartialPayment?: boolean;
|
||||
depositPercentage?: string;
|
||||
acceptCash?: boolean;
|
||||
acceptCard?: boolean;
|
||||
acceptEWallet?: boolean;
|
||||
twoFactorAuth?: boolean;
|
||||
sessionTimeout?: string;
|
||||
passwordExpiry?: string;
|
||||
loginAttempts?: string;
|
||||
}) {
|
||||
const user = await getCurrentUser();
|
||||
|
||||
if (user.role !== "admin") {
|
||||
throw new Error("Unauthorized: Admin access required");
|
||||
}
|
||||
|
||||
try {
|
||||
// In a real implementation, save to ClinicSettings table
|
||||
// For now, just log and return success
|
||||
console.log("Updating admin settings:", Object.keys(data).join(", "));
|
||||
|
||||
revalidatePath("/admin/settings");
|
||||
return { success: true, message: "Admin settings saved successfully" };
|
||||
} catch (error) {
|
||||
console.error("Error updating admin settings:", error);
|
||||
return { success: false, message: "Failed to save admin settings" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteUserAccount() {
|
||||
const user = await getCurrentUser();
|
||||
|
||||
if (user.role === "admin") {
|
||||
return { success: false, message: "Cannot delete admin accounts" };
|
||||
}
|
||||
|
||||
try {
|
||||
await prisma.user.delete({
|
||||
where: { id: user.id },
|
||||
});
|
||||
|
||||
return { success: true, message: "Account deleted successfully" };
|
||||
} catch (error) {
|
||||
console.error("Error deleting account:", error);
|
||||
return { success: false, message: "Failed to delete account" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function exportUserData() {
|
||||
const user = await getCurrentUser();
|
||||
|
||||
try {
|
||||
const userData = await prisma.user.findUnique({
|
||||
where: { id: user.id },
|
||||
include: {
|
||||
appointmentsAsPatient: true,
|
||||
appointmentsAsDentist: true,
|
||||
payments: true,
|
||||
notifications: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!userData) {
|
||||
return { success: false, message: "User data not found" };
|
||||
}
|
||||
|
||||
// Convert to JSON
|
||||
const dataExport = JSON.stringify(userData, null, 2);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Data exported successfully",
|
||||
data: dataExport,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error exporting data:", error);
|
||||
return { success: false, message: "Failed to export data" };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user