feat(reset-password): implement reset password functionality for admins and customers
- Added new ResetPasswordModalContent component for displaying generated passwords. - Integrated reset password functionality in the AdminsTable and CustomersTable components, allowing users to reset passwords for admins and customers. - Created useResetPasswordModalPresenter hook to manage password visibility and clipboard copying. - Updated API services and hooks to support reset password requests for both admins and customers. - Enhanced tooltip functionality for reset password actions in the tables.
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import { PencilSquareIcon, TrashIcon, UserSettingIcon } from "@/assets/icons";
|
||||
import {
|
||||
KeyIcon,
|
||||
PencilSquareIcon,
|
||||
TrashIcon,
|
||||
UserSettingIcon,
|
||||
} from "@/assets/icons";
|
||||
import EmptyListWrapper from "@/components/HOC/EmptyListWrapper";
|
||||
import ConfirmationModal from "@/components/ui/ConfirmationModal";
|
||||
import IsVisible from "@/components/ui/IsVisible";
|
||||
@@ -8,6 +13,7 @@ import ListHeader from "@/components/ui/ListHeader";
|
||||
import ListWrapper from "@/components/ui/ListWrapper";
|
||||
import Modal from "@/components/ui/modal";
|
||||
import Pagination from "@/components/ui/pagination";
|
||||
import ResetPasswordModalContent from "@/components/ui/ResetPasswordModalContent";
|
||||
import Status from "@/components/ui/Status";
|
||||
import {
|
||||
Table,
|
||||
@@ -56,6 +62,11 @@ const AdminsTable = () => {
|
||||
navigateToEditAdmin,
|
||||
openDeleteModalForUser,
|
||||
openChangeStatusModalForUser,
|
||||
isResetPasswordModalOpen,
|
||||
closeResetPasswordModal,
|
||||
openResetPasswordModalForUser,
|
||||
generatedPassword,
|
||||
isResettingPassword,
|
||||
} = useAdminsPresenter();
|
||||
|
||||
return (
|
||||
@@ -153,6 +164,23 @@ const AdminsTable = () => {
|
||||
<span className="sr-only">Change Admin Status</span>
|
||||
<UserSettingIcon />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-tooltip-id="reset-password"
|
||||
data-tooltip-content="Reset password"
|
||||
data-tooltip-place="top"
|
||||
className="hover:text-primary"
|
||||
data-cy={`admins-row-reset-password-${item?.id ?? index}`}
|
||||
onClick={() => openResetPasswordModalForUser(item)}
|
||||
>
|
||||
<Tooltip
|
||||
{..._TooltipDefaultParams({
|
||||
id: "reset-password",
|
||||
})}
|
||||
/>
|
||||
<span className="sr-only">Reset Admin Password</span>
|
||||
<KeyIcon />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-tooltip-id="edit"
|
||||
@@ -190,6 +218,20 @@ const AdminsTable = () => {
|
||||
/>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
isOpen={isResetPasswordModalOpen}
|
||||
onClose={closeResetPasswordModal}
|
||||
showButtons={false}
|
||||
>
|
||||
<ResetPasswordModalContent
|
||||
subjectName={currentUser?.full_name}
|
||||
subjectLabel="admin"
|
||||
password={generatedPassword}
|
||||
isLoading={isResettingPassword}
|
||||
isOpen={isResetPasswordModalOpen}
|
||||
/>
|
||||
</Modal>
|
||||
|
||||
{isModalOpen && (
|
||||
<ConfirmationModal
|
||||
isOpen={isModalOpen}
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
useChangeAdminStatus,
|
||||
useDeleteAdmin,
|
||||
useGetAdminListQuery,
|
||||
useResetAdminPassword,
|
||||
} from "@/hooks/queries";
|
||||
import { IUser, TChangeAdminStatusCredentials } from "@/lib/api";
|
||||
import { useHybridPagination } from "@/hooks/useHybridPagination";
|
||||
@@ -27,6 +28,11 @@ export const useAdminsPresenter = () => {
|
||||
const [isChangeStatusModalOpen, setIsChangeStatusModalOpen] = useState(false);
|
||||
const [currentUser, setCurrentUser] = useState<IUser | null>(null);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [isResetPasswordModalOpen, setIsResetPasswordModalOpen] =
|
||||
useState(false);
|
||||
const [generatedPassword, setGeneratedPassword] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
const [status, setStatus] = useState<string>(
|
||||
currentUser?.status ?? AdminStatus.ACTIVE,
|
||||
);
|
||||
@@ -115,6 +121,26 @@ export const useAdminsPresenter = () => {
|
||||
setIsChangeStatusModalOpen(true);
|
||||
}, []);
|
||||
|
||||
const { mutate: resetAdminPassword, isPending: isResettingPassword } =
|
||||
useResetAdminPassword({
|
||||
successCallback: (password) => setGeneratedPassword(password),
|
||||
});
|
||||
|
||||
const openResetPasswordModalForUser = useCallback(
|
||||
(user: IUser) => {
|
||||
setCurrentUser(user);
|
||||
setGeneratedPassword(null);
|
||||
setIsResetPasswordModalOpen(true);
|
||||
resetAdminPassword(user.id);
|
||||
},
|
||||
[resetAdminPassword],
|
||||
);
|
||||
|
||||
const closeResetPasswordModal = useCallback(() => {
|
||||
setIsResetPasswordModalOpen(false);
|
||||
setGeneratedPassword(null);
|
||||
}, []);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const reduced =
|
||||
typeof window !== "undefined" &&
|
||||
@@ -237,5 +263,10 @@ export const useAdminsPresenter = () => {
|
||||
navigateToEditAdmin,
|
||||
openDeleteModalForUser,
|
||||
openChangeStatusModalForUser,
|
||||
isResetPasswordModalOpen,
|
||||
closeResetPasswordModal,
|
||||
openResetPasswordModalForUser,
|
||||
generatedPassword,
|
||||
isResettingPassword,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
"use client";
|
||||
import { ExclamationIcon, PencilSquareIcon, TrashIcon } from "@/assets/icons";
|
||||
import {
|
||||
ExclamationIcon,
|
||||
KeyIcon,
|
||||
PencilSquareIcon,
|
||||
TrashIcon,
|
||||
} from "@/assets/icons";
|
||||
import { Building } from "@/components/Layouts/sidebar/icons";
|
||||
import EmptyListWrapper from "@/components/HOC/EmptyListWrapper";
|
||||
import ConfirmationModal from "@/components/ui/ConfirmationModal";
|
||||
@@ -8,6 +13,7 @@ import ListHeader from "@/components/ui/ListHeader";
|
||||
import ListWrapper from "@/components/ui/ListWrapper";
|
||||
import Modal from "@/components/ui/modal";
|
||||
import Pagination from "@/components/ui/pagination";
|
||||
import ResetPasswordModalContent from "@/components/ui/ResetPasswordModalContent";
|
||||
import Status from "@/components/ui/Status";
|
||||
import {
|
||||
Table,
|
||||
@@ -58,6 +64,11 @@ const CustomersTable = () => {
|
||||
dataTbodyRef,
|
||||
pagination,
|
||||
handlePaginationChange,
|
||||
isResetPasswordModalOpen,
|
||||
closeResetPasswordModal,
|
||||
openResetPasswordModalForCustomer,
|
||||
generatedPassword,
|
||||
isResettingPassword,
|
||||
} = useCustomerListPresenter();
|
||||
|
||||
return (
|
||||
@@ -215,6 +226,26 @@ const CustomersTable = () => {
|
||||
<span className="sr-only">Assign To Company </span>
|
||||
<Building />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-tooltip-id="reset-password"
|
||||
data-tooltip-content="Reset password"
|
||||
data-tooltip-place="top"
|
||||
className="hover:text-primary"
|
||||
onClick={() =>
|
||||
openResetPasswordModalForCustomer(customer)
|
||||
}
|
||||
>
|
||||
<Tooltip
|
||||
{..._TooltipDefaultParams({
|
||||
id: "reset-password",
|
||||
})}
|
||||
/>
|
||||
<span className="sr-only">
|
||||
Reset Customer Password
|
||||
</span>
|
||||
<KeyIcon />
|
||||
</button>
|
||||
<button
|
||||
data-tooltip-id="feedback"
|
||||
data-tooltip-content="Feedback"
|
||||
@@ -269,6 +300,19 @@ const CustomersTable = () => {
|
||||
/>
|
||||
</Modal>
|
||||
</div>
|
||||
<Modal
|
||||
isOpen={isResetPasswordModalOpen}
|
||||
onClose={closeResetPasswordModal}
|
||||
showButtons={false}
|
||||
>
|
||||
<ResetPasswordModalContent
|
||||
subjectName={currentCustomer?.full_name}
|
||||
subjectLabel="customer"
|
||||
password={generatedPassword}
|
||||
isLoading={isResettingPassword}
|
||||
isOpen={isResetPasswordModalOpen}
|
||||
/>
|
||||
</Modal>
|
||||
{isModalOpen && (
|
||||
<ConfirmationModal
|
||||
isOpen={isModalOpen}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
useCompanyFullList,
|
||||
useCustomersQuery,
|
||||
useDeleteCustomerQuery,
|
||||
useResetCustomerPassword,
|
||||
} from "@/hooks/queries";
|
||||
import { useHybridPagination } from "@/hooks/useHybridPagination";
|
||||
import { useTableSectionRowAnimations } from "@/hooks/useTableSectionRowAnimations";
|
||||
@@ -27,6 +28,11 @@ const useCustomerListPresenter = () => {
|
||||
);
|
||||
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [isResetPasswordModalOpen, setIsResetPasswordModalOpen] =
|
||||
useState(false);
|
||||
const [generatedPassword, setGeneratedPassword] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
const pathName = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
const [params, setParams] = useState<Record<string, any>>({
|
||||
@@ -90,6 +96,26 @@ const useCustomerListPresenter = () => {
|
||||
},
|
||||
);
|
||||
|
||||
const { mutate: resetCustomerPassword, isPending: isResettingPassword } =
|
||||
useResetCustomerPassword({
|
||||
successCallback: (password) => setGeneratedPassword(password),
|
||||
});
|
||||
|
||||
const openResetPasswordModalForCustomer = useCallback(
|
||||
(customer: TCustomer) => {
|
||||
setCurrentCustomer(customer);
|
||||
setGeneratedPassword(null);
|
||||
setIsResetPasswordModalOpen(true);
|
||||
resetCustomerPassword(customer.id);
|
||||
},
|
||||
[resetCustomerPassword],
|
||||
);
|
||||
|
||||
const closeResetPasswordModal = useCallback(() => {
|
||||
setIsResetPasswordModalOpen(false);
|
||||
setGeneratedPassword(null);
|
||||
}, []);
|
||||
|
||||
const search = (formData: any) => {
|
||||
const newParams = buildFirstPageParams({ ...params, ...formData });
|
||||
const cleanedParams = deleteEmptyKeys(newParams);
|
||||
@@ -154,6 +180,11 @@ const useCustomerListPresenter = () => {
|
||||
dataTbodyRef,
|
||||
pagination,
|
||||
handlePaginationChange,
|
||||
isResetPasswordModalOpen,
|
||||
closeResetPasswordModal,
|
||||
openResetPasswordModalForCustomer,
|
||||
generatedPassword,
|
||||
isResettingPassword,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -119,6 +119,8 @@ const parseCpvCodesFromSearchParams = (
|
||||
|
||||
const buildTenderFilterFormValues = (params: Record<string, any>) => ({
|
||||
...params,
|
||||
documents_scraped:
|
||||
params.documents_scraped === true || params.documents_scraped === "true",
|
||||
cpv_codes: formatCpvCodesForForm(params.cpv_codes),
|
||||
created_at_range: buildRangeFormValue(
|
||||
params,
|
||||
@@ -157,6 +159,7 @@ const TENDER_FILTER_FORM_DEFAULTS: Record<string, any> = {
|
||||
main_classification: "",
|
||||
classifications: "",
|
||||
cpv_codes: "",
|
||||
documents_scraped: false,
|
||||
created_at_range: [],
|
||||
tender_deadline_range: [],
|
||||
publication_date_range: [],
|
||||
|
||||
Reference in New Issue
Block a user