update popup notify. update new pagination
This commit is contained in:
11
package-lock.json
generated
11
package-lock.json
generated
@@ -32,6 +32,7 @@
|
|||||||
"react-dropzone": "^14.3.8",
|
"react-dropzone": "^14.3.8",
|
||||||
"react-redux": "^9.2.0",
|
"react-redux": "^9.2.0",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
|
"sweetalert2": "^11.26.24",
|
||||||
"swiper": "^11.2.10",
|
"swiper": "^11.2.10",
|
||||||
"tailwind-merge": "^2.6.0",
|
"tailwind-merge": "^2.6.0",
|
||||||
"yet-another-react-lightbox": "^3.30.1"
|
"yet-another-react-lightbox": "^3.30.1"
|
||||||
@@ -8908,6 +8909,16 @@
|
|||||||
"url": "https://opencollective.com/svgo"
|
"url": "https://opencollective.com/svgo"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sweetalert2": {
|
||||||
|
"version": "11.26.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.26.24.tgz",
|
||||||
|
"integrity": "sha512-SLgukW4wicewpW5VOukSXY5Z6DL/z7HCOK2ODSjmQPiSphCN8gJAmh9npoceXOtBRNoDN0xIz+zHYthtfiHmjg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/limonte"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/swiper": {
|
"node_modules/swiper": {
|
||||||
"version": "11.2.10",
|
"version": "11.2.10",
|
||||||
"resolved": "https://registry.npmjs.org/swiper/-/swiper-11.2.10.tgz",
|
"resolved": "https://registry.npmjs.org/swiper/-/swiper-11.2.10.tgz",
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
"react-dropzone": "^14.3.8",
|
"react-dropzone": "^14.3.8",
|
||||||
"react-redux": "^9.2.0",
|
"react-redux": "^9.2.0",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
|
"sweetalert2": "^11.26.24",
|
||||||
"swiper": "^11.2.10",
|
"swiper": "^11.2.10",
|
||||||
"tailwind-merge": "^2.6.0",
|
"tailwind-merge": "^2.6.0",
|
||||||
"yet-another-react-lightbox": "^3.30.1"
|
"yet-another-react-lightbox": "^3.30.1"
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import ComponentCard from "@/components/common/ComponentCard";
|
import ComponentCard from "@/components/common/ComponentCard";
|
||||||
|
import Swal from "sweetalert2";
|
||||||
import PageBreadcrumb from "@/components/common/PageBreadCrumb";
|
import PageBreadcrumb from "@/components/common/PageBreadCrumb";
|
||||||
import BasicTableOne from "@/components/tables/BasicTableOne";
|
import BasicTableOne from "@/components/tables/BasicTableOne";
|
||||||
import ChangeRoleModal from "@/components/tables/ChangeRoleModal";
|
import ChangeRoleModal from "@/components/tables/ChangeRoleModal";
|
||||||
import UserDetailModal from "@/components/tables/UserDetailModal";
|
import UserDetailModal from "@/components/tables/UserDetailModal";
|
||||||
import { Modal } from "@/components/ui/modal";
|
|
||||||
import { responseUserTable, getUserDto, fullDataUser } from "@/interface/admin";
|
import { responseUserTable, getUserDto, fullDataUser } from "@/interface/admin";
|
||||||
import {
|
import {
|
||||||
apiDeleteUser,
|
apiDeleteUser,
|
||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
apiRestoreUser,
|
apiRestoreUser,
|
||||||
} from "@/service/adminService";
|
} from "@/service/adminService";
|
||||||
import { useEffect, useState, useCallback } from "react";
|
import { useEffect, useState, useCallback } from "react";
|
||||||
import { toast } from "sonner";
|
import Pagination from "@/components/tables/Pagination";
|
||||||
|
|
||||||
export type SortColumn = "created_at" | "updated_at" | "display_name" | "email";
|
export type SortColumn = "created_at" | "updated_at" | "display_name" | "email";
|
||||||
|
|
||||||
@@ -48,7 +48,6 @@ export default function UserTable() {
|
|||||||
const fetchRoles = async () => {
|
const fetchRoles = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await apiGetAllRole();
|
const res = await apiGetAllRole();
|
||||||
console.log("Danh sách role:", res);
|
|
||||||
if (res?.status) {
|
if (res?.status) {
|
||||||
setRoles(res.data);
|
setRoles(res.data);
|
||||||
}
|
}
|
||||||
@@ -58,7 +57,7 @@ export default function UserTable() {
|
|||||||
};
|
};
|
||||||
fetchRoles();
|
fetchRoles();
|
||||||
}, []);
|
}, []);
|
||||||
console.log("Roles đã fetch:", roles);
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handler = setTimeout(() => {
|
const handler = setTimeout(() => {
|
||||||
setDebouncedParams({
|
setDebouncedParams({
|
||||||
@@ -124,36 +123,57 @@ export default function UserTable() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = async (user: fullDataUser) => {
|
const handleDelete = async (user: fullDataUser) => {
|
||||||
if (
|
const result = await Swal.fire({
|
||||||
window.confirm(
|
title: "Xác nhận khóa?",
|
||||||
`Khóa người dùng ${user.profile?.display_name || user.email}?`,
|
text: `Bạn có chắc muốn khóa người dùng ${user.profile?.display_name || user.email}?`,
|
||||||
)
|
icon: "warning",
|
||||||
) {
|
showCancelButton: true,
|
||||||
|
showCloseButton: true,
|
||||||
|
confirmButtonColor: "#d33",
|
||||||
|
cancelButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "Đồng ý, khóa!",
|
||||||
|
cancelButtonText: "Hủy",
|
||||||
|
reverseButtons: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.isConfirmed) {
|
||||||
try {
|
try {
|
||||||
await apiDeleteUser(user.id);
|
await apiDeleteUser(user.id);
|
||||||
toast.success("Đã khóa người dùng thành công!");
|
Swal.fire(
|
||||||
|
"Đã khóa!",
|
||||||
|
"Người dùng đã bị tạm dừng hoạt động.",
|
||||||
|
"success",
|
||||||
|
);
|
||||||
fetchUsers();
|
fetchUsers();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toast.error("Đã xảy ra lỗi khi xóa!");
|
Swal.fire("Lỗi!", "Không thể thực hiện thao tác này.", "error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRestore = async (user: fullDataUser) => {
|
const handleRestore = async (user: fullDataUser) => {
|
||||||
if (
|
const result = await Swal.fire({
|
||||||
window.confirm(
|
title: "Khôi phục tài khoản?",
|
||||||
`Khôi phục người dùng ${user.profile?.display_name || user.email}?`,
|
text: `Khôi phục quyền truy cập cho ${user.profile?.display_name || user.email}?`,
|
||||||
)
|
icon: "question",
|
||||||
) {
|
showCancelButton: true,
|
||||||
|
showCloseButton: true,
|
||||||
|
confirmButtonColor: "#28a745",
|
||||||
|
confirmButtonText: "Xác nhận",
|
||||||
|
cancelButtonText: "Hủy",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.isConfirmed) {
|
||||||
try {
|
try {
|
||||||
await apiRestoreUser(user.id);
|
await apiRestoreUser(user.id);
|
||||||
toast.success("Khôi phục người dùng thành công!");
|
Swal.fire("Thành công", "Tài khoản đã được khôi phục.", "success");
|
||||||
fetchUsers();
|
fetchUsers();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toast.error("Đã xảy ra lỗi khi khôi phục!");
|
Swal.fire("Thất bại", "Vui lòng thử lại sau.", "error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<PageBreadcrumb pageTitle="Quản lý người dùng" />
|
<PageBreadcrumb pageTitle="Quản lý người dùng" />
|
||||||
@@ -171,7 +191,6 @@ export default function UserTable() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Auth Provider */}
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block mb-2 text-sm font-medium">
|
<label className="block mb-2 text-sm font-medium">
|
||||||
Auth Provider
|
Auth Provider
|
||||||
@@ -243,26 +262,19 @@ export default function UserTable() {
|
|||||||
|
|
||||||
<div className="flex items-center justify-between mt-6">
|
<div className="flex items-center justify-between mt-6">
|
||||||
<p className="text-sm text-gray-500">
|
<p className="text-sm text-gray-500">
|
||||||
Hiển thị trang {pagination?.current_page} /{" "}
|
Hiển thị trang {pagination?.current_page || 1} /{" "}
|
||||||
{pagination?.total_pages} ({pagination?.total_records} kết quả)
|
{pagination?.total_pages || 1} ({pagination?.total_records || 0} kết quả)
|
||||||
</p>
|
</p>
|
||||||
<div className="flex gap-2">
|
|
||||||
<button
|
{pagination && pagination.total_pages > 1 && (
|
||||||
disabled={page <= 1 || loading}
|
<Pagination
|
||||||
onClick={() => setPage((p) => p - 1)}
|
currentPage={pagination.current_page}
|
||||||
className="px-4 py-2 border rounded-md disabled:opacity-50 hover:bg-gray-100 dark:hover:bg-gray-700"
|
totalPages={pagination.total_pages}
|
||||||
>
|
onPageChange={(newPage) => setPage(newPage)}
|
||||||
Trước
|
/>
|
||||||
</button>
|
)}
|
||||||
<button
|
|
||||||
disabled={page >= (pagination?.total_pages || 1) || loading}
|
|
||||||
onClick={() => setPage((p) => p + 1)}
|
|
||||||
className="px-4 py-2 border rounded-md disabled:opacity-50 hover:bg-gray-100 dark:hover:bg-gray-700"
|
|
||||||
>
|
|
||||||
Sau
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UserDetailModal
|
<UserDetailModal
|
||||||
isOpen={isModalOpen}
|
isOpen={isModalOpen}
|
||||||
onClose={() => setIsModalOpen(false)}
|
onClose={() => setIsModalOpen(false)}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export default function BasicTableOne({
|
|||||||
return (
|
return (
|
||||||
<div className="flex flex-col ml-2 opacity-50 cursor-pointer hover:opacity-100">
|
<div className="flex flex-col ml-2 opacity-50 cursor-pointer hover:opacity-100">
|
||||||
<svg
|
<svg
|
||||||
className={`w-3 h-3 ${isActive && sortOrder === "asc" ? "text-brand-500 opacity-100" : "text-gray-400"}`}
|
className={`w-3 h-3 ${isActive && sortOrder === "asc" ? "text-blue-700 opacity-100" : "text-gray-400"}`}
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@@ -67,7 +67,7 @@ export default function BasicTableOne({
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<svg
|
<svg
|
||||||
className={`w-3 h-3 -mt-1 ${isActive && sortOrder === "desc" ? "text-brand-500 opacity-100" : "text-gray-400"}`}
|
className={`w-3 h-3 -mt-1 ${isActive && sortOrder === "desc" ? "text-blue-700 opacity-100" : "text-gray-400"}`}
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ const AppHeader: React.FC = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="sticky top-0 flex w-full bg-white border-gray-200 z-99999 dark:border-gray-800 dark:bg-gray-900 lg:border-b">
|
<header className="sticky top-0 flex w-full bg-white border-gray-200 z-99 dark:border-gray-800 dark:bg-gray-900 lg:border-b">
|
||||||
<div className="flex flex-col items-center justify-between grow lg:flex-row lg:px-6">
|
<div className="flex flex-col items-center justify-between grow lg:flex-row lg:px-6">
|
||||||
<div className="flex items-center justify-between w-full gap-2 px-3 py-3 border-b border-gray-200 dark:border-gray-800 sm:gap-4 lg:justify-normal lg:border-b-0 lg:px-0 lg:py-4">
|
<div className="flex items-center justify-between w-full gap-2 px-3 py-3 border-b border-gray-200 dark:border-gray-800 sm:gap-4 lg:justify-normal lg:border-b-0 lg:px-0 lg:py-4">
|
||||||
<button
|
<button
|
||||||
|
|||||||
Reference in New Issue
Block a user