MIF_E31221222/sigap-website/app/(pages)/(admin)/dashboard/user-management/handler.tsx

698 lines
20 KiB
TypeScript

// import { useEffect, useState } from 'react';
// import { IUserSchema, IUserFilterOptionsSchema } from '@/src/entities/models/users/users.model';
// import { toast } from 'sonner';
// import { set } from 'date-fns';
// import { CreateUserSchema, defaulICreateUserSchemaValues, ICreateUserSchema } from '@/src/entities/models/users/create-user.model';
// import { useForm } from 'react-hook-form';
// import { zodResolver } from '@hookform/resolvers/zod';
// import { defaulIInviteUserSchemaValues, IInviteUserSchema, InviteUserSchema } from '@/src/entities/models/users/invite-user.model';
// import { useQueryClient } from '@tanstack/react-query';
// import { useSendMagicLinkMutation, useSendPasswordRecoveryMutation } from '@/app/(pages)/(auth)/queries';
// import { ValidBanDuration } from '@/app/_lib/types/ban-duration';
// import { IUpdateUserSchema, UpdateUserSchema } from '@/src/entities/models/users/update-user.model';
// import { useUsersAction } from './queries';
// import { getUsersQuery, useGetUsersQuery, useUsersQuery } from './_queries/queries';
// export const useUsersHandlers = () => {
// const queryClient = useQueryClient();
// // Core mutations
// // const { updateUser, isPending: isUpdatePending, errors: isUpdateError } = useUpdateUserMutation();
// // const { deleteUser, isPending: isDeletePending } = useDeleteUserMutation();
// // const { sendPasswordRecovery, isPending: isSendPasswordRecoveryPending } = useSendPasswordRecoveryMutation();
// // const { sendMagicLink, isPending: isSendMagicLinkPending } = useSendMagicLinkMutation();
// // const { banUser, isPending: isBanPending } = useBanUserMutation();
// // const { unbanUser, isPending: isUnbanPending } = useUnbanUserMutation();
// const {
// getCurrentUser,
// getUserById,
// getUserByEmail,
// getUserByUsername,
// createUser,
// inviteUser,
// updateUser,
// deleteUser,
// banUser,
// unbanUser
// } = useUsersAction();
// /**
// * update a user by ID
// */
// const handleUpdateUser = async (userId: string, data: IUpdateUserSchema, options?: {
// onSuccess?: () => void,
// onError?: (error: unknown) => void
// }) => {
// await updateUser({ id: userId, data }, {
// onSuccess: () => {
// queryClient.invalidateQueries({ queryKey: ["users"] });
// toast.success("User updated successfully");
// options?.onSuccess?.();
// },
// onError: (error) => {
// toast.error("Failed to update user");
// options?.onError?.(error);
// },
// });
// }
// /**
// * Deletes a user by ID
// */
// const handleDeleteUser = async (userId: string, options?: {
// onSuccess?: () => void,
// onError?: (error: unknown) => void
// }) => {
// await deleteUser(userId, {
// onSuccess: () => {
// queryClient.invalidateQueries({ queryKey: ["users"] });
// toast.success("User deleted successfully");
// options?.onSuccess?.();
// },
// onError: (error) => {
// toast.error("Failed to delete user");
// options?.onError?.(error);
// },
// });
// };
// /**
// * Sends a password recovery email to the user
// */
// const handleSendPasswordRecovery = async (email: string, options?: {
// onSuccess?: () => void,
// onError?: (error: unknown) => void
// }) => {
// if (!email) {
// toast.error("No email address provided");
// options?.onError?.(new Error("No email address provided"));
// return;
// }
// await sendPasswordRecovery(email, {
// onSuccess: () => {
// toast.success("Recovery email sent");
// options?.onSuccess?.();
// },
// onError: (error) => {
// toast.error("Failed to send recovery email");
// options?.onError?.(error);
// },
// });
// };
// /**
// * Sends a magic link to the user's email
// */
// const handleSendMagicLink = async (email: string, options?: {
// onSuccess?: () => void,
// onError?: (error: unknown) => void
// }) => {
// if (!email) {
// toast.error("No email address provided");
// options?.onError?.(new Error("No email address provided"));
// return;
// }
// await sendMagicLink(email, {
// onSuccess: () => {
// toast.success("Magic link sent");
// options?.onSuccess?.();
// },
// onError: (error) => {
// toast.error("Failed to send magic link");
// options?.onError?.(error);
// },
// });
// };
// /**
// * Bans a user for the specified duration
// */
// const handleBanUser = async (userId: string, banDuration: ValidBanDuration = "24h", options?: {
// onSuccess?: () => void,
// onError?: (error: unknown) => void
// }) => {
// await banUser({ credential: { id: userId }, data: { ban_duration: banDuration } }, {
// onSuccess: () => {
// queryClient.invalidateQueries({ queryKey: ["users"] });
// toast.success("User banned successfully");
// options?.onSuccess?.();
// },
// onError: (error) => {
// toast.error("Failed to ban user");
// options?.onError?.(error);
// },
// });
// };
// /**
// * Unbans a user
// */
// const handleUnbanUser = async (userId: string, options?: {
// onSuccess?: () => void,
// onError?: (error: unknown) => void
// }) => {
// await unbanUser({ id: userId }, {
// onSuccess: () => {
// queryClient.invalidateQueries({ queryKey: ["users"] });
// toast.success("User unbanned successfully");
// options?.onSuccess?.();
// },
// onError: (error) => {
// toast.error("Failed to unban user");
// options?.onError?.(error);
// },
// });
// };
// /**
// * Toggles a user's ban status
// */
// const handleToggleBan = async (user: { id: string, banned_until?: ValidBanDuration }, options?: {
// onSuccess?: () => void,
// onError?: (error: unknown) => void,
// banDuration?: ValidBanDuration
// }) => {
// if (user.banned_until) {
// await handleUnbanUser(user.id, options);
// } else {
// await handleBanUser(user.id, options?.banDuration, options);
// }
// };
// /**
// * Copies text to clipboard
// */
// const handleCopyItem = (item: string, options?: {
// onSuccess?: () => void,
// onError?: (error: unknown) => void
// }) => {
// if (!navigator.clipboard) {
// const error = new Error("Clipboard not supported");
// toast.error("Clipboard not supported");
// options?.onError?.(error);
// return;
// }
// if (!item) {
// const error = new Error("Nothing to copy");
// toast.error("Nothing to copy");
// options?.onError?.(error);
// return;
// }
// navigator.clipboard.writeText(item)
// .then(() => {
// toast.success("Copied to clipboard");
// options?.onSuccess?.();
// })
// .catch((error) => {
// toast.error("Failed to copy to clipboard");
// options?.onError?.(error);
// });
// };
// return {
// // Action handlers
// updateUser: handleUpdateUser,
// deleteUser: handleDeleteUser,
// sendPasswordRecovery: handleSendPasswordRecovery,
// sendMagicLink: handleSendMagicLink,
// banUser: handleBanUser,
// unbanUser: handleUnbanUser,
// toggleBan: handleToggleBan,
// copyToClipboard: handleCopyItem,
// // Loading states
// isUpdatePending,
// isDeletePending,
// isSendPasswordRecoveryPending,
// isSendMagicLinkPending,
// isBanPending,
// isUnbanPending,
// // Errors
// isUpdateError,
// };
// };
// // Specific handler for the component
// export const useAddUserDialogHandler = ({ onUserAdded, onOpenChange }: {
// onUserAdded: () => void;
// onOpenChange: (open: boolean) => void;
// }) => {
// const queryClient = useQueryClient();
// const { createUser, isPending } = useCreateUserMutation();
// const {
// register,
// handleSubmit,
// reset,
// formState: { errors: errors },
// setError,
// getValues,
// clearErrors,
// watch,
// } = useForm<ICreateUserSchema>({
// resolver: zodResolver(CreateUserSchema),
// defaultValues: {
// email: "",
// password: "",
// email_confirm: true,
// }
// });
// const emailConfirm = watch("email_confirm");
// const onSubmit = handleSubmit(async (data) => {
// await createUser(data, {
// onSuccess: () => {
// queryClient.invalidateQueries({ queryKey: ["users"] });
// toast.success("User created successfully.");
// onUserAdded();
// onOpenChange(false);
// reset();
// },
// onError: (error) => {
// reset();
// toast.error(error.message);
// },
// });
// });
// const handleOpenChange = (open: boolean) => {
// if (!open) {
// reset();
// }
// onOpenChange(open);
// };
// return {
// register,
// handleSubmit: onSubmit,
// reset,
// errors,
// isPending,
// getValues,
// clearErrors,
// emailConfirm,
// handleOpenChange,
// };
// }
// export const useInviteUserHandler = ({ onUserInvited, onOpenChange }: {
// onUserInvited: () => void;
// onOpenChange: (open: boolean) => void;
// }) => {
// const queryClient = useQueryClient();
// const { inviteUser, isPending } = useInviteUserMutation();
// const {
// register,
// handleSubmit,
// reset,
// formState: { errors: errors },
// setError,
// getValues,
// clearErrors,
// watch,
// } = useForm<IInviteUserSchema>({
// resolver: zodResolver(InviteUserSchema),
// defaultValues: defaulIInviteUserSchemaValues
// })
// const onSubmit = handleSubmit(async (data) => {
// await inviteUser(data, {
// onSuccess: () => {
// queryClient.invalidateQueries({ queryKey: ["users"] });
// toast.success("Invitation sent");
// onUserInvited();
// onOpenChange(false);
// reset();
// },
// onError: () => {
// reset();
// toast.error("Failed to send invitation");
// },
// });
// });
// const handleOpenChange = (open: boolean) => {
// if (!open) {
// reset();
// }
// onOpenChange(open);
// };
// return {
// register,
// handleSubmit: onSubmit,
// handleOpenChange,
// reset,
// getValues,
// clearErrors,
// watch,
// errors,
// isPending,
// };
// }
// export const useUserDetailSheetHandlers = ({ open, user, onUserUpdated, onOpenChange }: {
// open: boolean;
// user: IUserSchema;
// onUserUpdated: () => void;
// onOpenChange: (open: boolean) => void;
// }) => {
// const {
// deleteUser,
// sendPasswordRecovery,
// sendMagicLink,
// banUser,
// unbanUser,
// toggleBan,
// copyToClipboard,
// isDeletePending,
// isSendPasswordRecoveryPending,
// isSendMagicLinkPending,
// isBanPending,
// isUnbanPending,
// } = useUsersHandlers();
// const handleDeleteUser = async () => {
// await deleteUser(user.id, {
// onSuccess: () => {
// onOpenChange(false);
// }
// });
// };
// const handleSendPasswordRecovery = async () => {
// if (!user.email) {
// toast.error("User has no email address");
// return;
// }
// await sendPasswordRecovery(user.email);
// };
// const handleSendMagicLink = async () => {
// if (!user.email) {
// toast.error("User has no email address");
// return;
// }
// await sendMagicLink(user.email);
// };
// const handleBanUser = async () => {
// await banUser(user.id, "24h", {
// onSuccess: onUserUpdated
// });
// };
// const handleUnbanUser = async () => {
// await unbanUser(user.id, {
// onSuccess: onUserUpdated
// });
// };
// const handleToggleBan = async () => {
// await toggleBan({ id: user.id }, {
// onSuccess: onUserUpdated
// });
// };
// return {
// handleDeleteUser,
// handleSendPasswordRecovery,
// handleSendMagicLink,
// handleBanUser,
// handleUnbanUser,
// handleToggleBan,
// handleCopyItem: copyToClipboard,
// isDeletePending,
// isSendPasswordRecoveryPending,
// isSendMagicLinkPending,
// isBanPending,
// isUnbanPending,
// };
// };
// export const useUserProfileSheetHandlers = ({ open, onOpenChange, userData, onUserUpdated }: {
// open: boolean;
// userData: IUserSchema;
// onOpenChange: (open: boolean) => void;
// onUserUpdated: () => void;
// }) => {
// const { updateUser, isUpdatePending, isUpdateError } = useUsersHandlers();
// // Initialize form with user data
// const form = useForm<IUpdateUserSchema>({
// resolver: zodResolver(UpdateUserSchema),
// defaultValues: {
// email: userData?.email || undefined,
// encrypted_password: userData?.encrypted_password || undefined,
// role: (userData?.role as "user" | "staff" | "admin") || "user",
// phone: userData?.phone || undefined,
// invited_at: userData?.invited_at || undefined,
// confirmed_at: userData?.confirmed_at || undefined,
// // recovery_sent_at: userData?.recovery_sent_at || undefined,
// last_sign_in_at: userData?.last_sign_in_at || undefined,
// created_at: userData?.created_at || undefined,
// updated_at: userData?.updated_at || undefined,
// is_anonymous: userData?.is_anonymous || false,
// profile: {
// // id: userData?.profile?.id || undefined,
// // user_id: userData?.profile?.user_id || undefined,
// avatar: userData?.profile?.avatar || undefined,
// username: userData?.profile?.username || undefined,
// first_name: userData?.profile?.first_name || undefined,
// last_name: userData?.profile?.last_name || undefined,
// bio: userData?.profile?.bio || undefined,
// address: userData?.profile?.address || {
// street: "",
// city: "",
// state: "",
// country: "",
// postal_code: "",
// },
// birth_date: userData?.profile?.birth_date ? new Date(userData.profile.birth_date) : undefined,
// },
// },
// })
// const handleUpdateUser = async () => {
// await updateUser(userData.id, form.getValues(), {
// onSuccess: () => {
// onUserUpdated();
// onOpenChange(false);
// },
// onError: () => {
// onOpenChange(false);
// },
// });
// }
// return {
// handleUpdateUser,
// form,
// isUpdatePending,
// };
// }
// export const useUserManagementHandlers = (refetch: () => void) => {
// const [searchQuery, setSearchQuery] = useState("")
// const [detailUser, setDetailUser] = useState<IUserSchema | null>(null)
// const [updateUser, setUpdateUser] = useState<IUserSchema | null>(null)
// const [isSheetOpen, setIsSheetOpen] = useState(false)
// const [isUpdateOpen, setIsUpdateOpen] = useState(false)
// const [isAddUserOpen, setIsAddUserOpen] = useState(false)
// const [isInviteUserOpen, setIsInviteUserOpen] = useState(false)
// // Filter states
// const [filters, setFilters] = useState<IUserFilterOptionsSchema>({
// email: "",
// phone: "",
// lastSignIn: "",
// createdAt: "",
// status: [],
// })
// // Handle opening the detail sheet
// const handleUserClick = (user: IUserSchema) => {
// setDetailUser(user)
// setIsSheetOpen(true)
// }
// // Handle opening the update sheet
// const handleUserUpdate = (user: IUserSchema) => {
// setUpdateUser(user)
// setIsUpdateOpen(true)
// }
// // Close detail sheet when update sheet opens
// useEffect(() => {
// if (isUpdateOpen) {
// setIsSheetOpen(false)
// }
// }, [isUpdateOpen])
// // Reset detail user when sheet closes
// useEffect(() => {
// if (!isSheetOpen) {
// // Use a small delay to prevent flickering if another sheet is opening
// const timer = setTimeout(() => {
// if (!isSheetOpen && !isUpdateOpen) {
// setDetailUser(null)
// }
// }, 300)
// return () => clearTimeout(timer)
// }
// }, [isSheetOpen, isUpdateOpen])
// // Reset update user when update sheet closes
// useEffect(() => {
// if (!isUpdateOpen) {
// // Use a small delay to prevent flickering if another sheet is opening
// const timer = setTimeout(() => {
// if (!isUpdateOpen) {
// setUpdateUser(null)
// }
// }, 300)
// return () => clearTimeout(timer)
// }
// }, [isUpdateOpen])
// const clearFilters = () => {
// setFilters({
// email: "",
// phone: "",
// lastSignIn: "",
// createdAt: "",
// status: [],
// })
// }
// const getActiveFilterCount = () => {
// return Object.values(filters).filter(
// (value) => (typeof value === "string" && value !== "") || (Array.isArray(value) && value.length > 0),
// ).length
// }
// return {
// searchQuery,
// setSearchQuery,
// detailUser,
// updateUser,
// isSheetOpen,
// setIsSheetOpen,
// isUpdateOpen,
// setIsUpdateOpen,
// isAddUserOpen,
// setIsAddUserOpen,
// isInviteUserOpen,
// setIsInviteUserOpen,
// filters,
// setFilters,
// handleUserClick,
// handleUserUpdate,
// clearFilters,
// getActiveFilterCount,
// }
// }
// export const filterUsers = (users: IUserSchema[], searchQuery: string, filters: IUserFilterOptionsSchema): IUserSchema[] => {
// return users.filter((user) => {
// // Global search
// if (searchQuery) {
// const query = searchQuery.toLowerCase()
// const matchesSearch =
// user.email?.toLowerCase().includes(query) ||
// user.phone?.toLowerCase().includes(query) ||
// user.id.toLowerCase().includes(query)
// if (!matchesSearch) return false
// }
// // Email filter
// if (filters.email && !user.email?.toLowerCase().includes(filters.email.toLowerCase())) {
// return false
// }
// // Phone filter
// if (filters.phone && !user.phone?.toLowerCase().includes(filters.phone.toLowerCase())) {
// return false
// }
// // Last sign in filter
// if (filters.lastSignIn) {
// if (filters.lastSignIn === "never" && user.last_sign_in_at) {
// return false
// } else if (filters.lastSignIn === "today") {
// const today = new Date()
// today.setHours(0, 0, 0, 0)
// const signInDate = user.last_sign_in_at ? new Date(user.last_sign_in_at) : null
// if (!signInDate || signInDate < today) return false
// } else if (filters.lastSignIn === "week") {
// const weekAgo = new Date()
// weekAgo.setDate(weekAgo.getDate() - 7)
// const signInDate = user.last_sign_in_at ? new Date(user.last_sign_in_at) : null
// if (!signInDate || signInDate < weekAgo) return false
// } else if (filters.lastSignIn === "month") {
// const monthAgo = new Date()
// monthAgo.setMonth(monthAgo.getMonth() - 1)
// const signInDate = user.last_sign_in_at ? new Date(user.last_sign_in_at) : null
// if (!signInDate || signInDate < monthAgo) return false
// }
// }
// // Created at filter
// if (filters.createdAt) {
// if (filters.createdAt === "today") {
// const today = new Date()
// today.setHours(0, 0, 0, 0)
// const createdAt = user.created_at ? (user.created_at ? new Date(user.created_at) : new Date()) : new Date()
// if (createdAt < today) return false
// } else if (filters.createdAt === "week") {
// const weekAgo = new Date()
// weekAgo.setDate(weekAgo.getDate() - 7)
// const createdAt = user.created_at ? new Date(user.created_at) : new Date()
// if (createdAt < weekAgo) return false
// } else if (filters.createdAt === "month") {
// const monthAgo = new Date()
// monthAgo.setMonth(monthAgo.getMonth() - 1)
// const createdAt = user.created_at ? new Date(user.created_at) : new Date()
// if (createdAt < monthAgo) return false
// }
// }
// // Status filter
// if (filters.status.length > 0) {
// const userStatus = user.banned_until ? "banned" : !user.email_confirmed_at ? "unconfirmed" : "active"
// if (!filters.status.includes(userStatus)) {
// return false
// }
// }
// return true
// })
// }