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

308 lines
8.3 KiB
TypeScript

import { useEffect, useState } from 'react';
import { useCreateUserMutation, useInviteUserMutation } from './queries';
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';
export const useAddUserDialogHandler = ({ onUserAdded, onOpenChange }: {
onUserAdded: () => void;
onOpenChange: (open: boolean) => void;
}) => {
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: () => {
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 { 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: () => {
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 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
})
}