feat: implement user dropdown menu and reusable components for user data management and display
Build and Release / release (push) Successful in 48s

This commit is contained in:
2026-06-06 22:57:19 +07:00
parent 5bd4a4e402
commit b3e765b6f1
3 changed files with 40 additions and 31 deletions
+2 -2
View File
@@ -57,7 +57,7 @@ export default function UserDropdown() {
.replace(/=.*/, "=;expires=" + new Date().toUTCString() + ";path=/"); .replace(/=.*/, "=;expires=" + new Date().toUTCString() + ";path=/");
}); });
window.location.href = "/signin"; window.location.href = "/auth/signin";
} }
}; };
@@ -181,7 +181,7 @@ export default function UserDropdown() {
</ul> </ul>
<Link <Link
onClick={handleLogout} onClick={handleLogout}
href="/signin" href="/auth/signin"
className="flex items-center gap-3 px-3 py-2 mt-3 font-medium text-gray-700 rounded-lg group text-theme-sm hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-gray-300" className="flex items-center gap-3 px-3 py-2 mt-3 font-medium text-gray-700 rounded-lg group text-theme-sm hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-gray-300"
> >
<svg <svg
+9 -9
View File
@@ -147,12 +147,12 @@ export default function BasicTableOne({
</div> </div>
</TableCell> </TableCell>
<TableCell {/* <TableCell
isHeader isHeader
className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400" className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400"
> >
Trạng thái Trạng thái
</TableCell> </TableCell> */}
<TableCell <TableCell
isHeader isHeader
@@ -166,7 +166,7 @@ export default function BasicTableOne({
</div> </div>
</TableCell> </TableCell>
<TableCell {/* <TableCell
isHeader isHeader
className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400" className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400"
> >
@@ -176,7 +176,7 @@ export default function BasicTableOne({
> >
Cập nhật <SortIcon column="updated_at" /> Cập nhật <SortIcon column="updated_at" />
</div> </div>
</TableCell> </TableCell> */}
<TableCell <TableCell
isHeader isHeader
@@ -243,7 +243,7 @@ export default function BasicTableOne({
</div> </div>
</TableCell> </TableCell>
<TableCell className="px-5 py-4 text-start"> {/* <TableCell className="px-5 py-4 text-start">
<Badge <Badge
size="sm" size="sm"
variant="light" variant="light"
@@ -251,14 +251,14 @@ export default function BasicTableOne({
> >
{user.is_deleted ? "Bị khóa" : "Hoạt động"} {user.is_deleted ? "Bị khóa" : "Hoạt động"}
</Badge> </Badge>
</TableCell> </TableCell> */}
<TableCell className="px-5 py-4 text-gray-600 text-theme-sm dark:text-gray-400"> <TableCell className="px-5 py-4 text-gray-600 text-theme-sm dark:text-gray-400">
{formatDate(user.created_at)} {formatDate(user.created_at)}
</TableCell> </TableCell>
<TableCell className="px-5 py-4 text-gray-600 text-theme-sm dark:text-gray-400"> {/* <TableCell className="px-5 py-4 text-gray-600 text-theme-sm dark:text-gray-400">
{formatDate(user.updated_at)} {formatDate(user.updated_at)}
</TableCell> </TableCell> */}
<TableCell className="px-5 py-4 text-center"> <TableCell className="px-5 py-4 text-center">
<button <button
@@ -273,7 +273,7 @@ export default function BasicTableOne({
) : ( ) : (
<TableRow> <TableRow>
<TableCell <TableCell
colSpan={7} colSpan={5}
className="px-5 py-24 text-center text-gray-500 italic" className="px-5 py-24 text-center text-gray-500 italic"
> >
<div className="flex flex-col items-center justify-center gap-2"> <div className="flex flex-col items-center justify-center gap-2">
+10 -1
View File
@@ -4,6 +4,8 @@ import UserMetaCard from "@/components/user-profile/UserMetaCard";
import UserInfoCard from "@/components/user-profile/UserInfoCard"; import UserInfoCard from "@/components/user-profile/UserInfoCard";
import { fullDataUser } from "@/interface/admin"; import { fullDataUser } from "@/interface/admin";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useSelector } from "react-redux";
import { RootState } from "@/store/store";
import { MediaDto } from "@/interface/media"; import { MediaDto } from "@/interface/media";
import { apiGetUserMedia } from "@/service/adminService"; import { apiGetUserMedia } from "@/service/adminService";
import MediaCard from "@/components/user-profile/Media"; import MediaCard from "@/components/user-profile/Media";
@@ -30,6 +32,9 @@ export default function UserDetailModal({
const [mediaData, setMediaData] = useState<MediaDto | null>(null); const [mediaData, setMediaData] = useState<MediaDto | null>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const currentUserRoles = useSelector((state: RootState) => state.user.data?.roles) || [];
const isMod = currentUserRoles.some((role: any) => role.name === "MOD");
const formattedData = { data: user }; const formattedData = { data: user };
useEffect(() => { useEffect(() => {
@@ -98,12 +103,14 @@ export default function UserDetailModal({
Thao tác quản trị viên Thao tác quản trị viên
</div> </div>
<div className="flex gap-3"> <div className="flex gap-3">
{!isMod && (
<button <button
onClick={() => onResetPassword(user)} onClick={() => onResetPassword(user)}
className="px-4 py-2 text-sm font-medium text-blue-600 bg-blue-50 rounded-lg hover:bg-blue-100 dark:bg-blue-500/10 dark:text-blue-400 dark:hover:bg-blue-500/20 transition-colors" className="px-4 py-2 text-sm font-medium text-blue-600 bg-blue-50 rounded-lg hover:bg-blue-100 dark:bg-blue-500/10 dark:text-blue-400 dark:hover:bg-blue-500/20 transition-colors"
> >
Đt lại mật khẩu Đt lại mật khẩu
</button> </button>
)}
<button <button
onClick={() => onChangeRole(user)} onClick={() => onChangeRole(user)}
className="px-4 py-2 text-sm font-medium text-purple-600 bg-purple-50 rounded-lg hover:bg-purple-100 dark:bg-purple-500/10 dark:text-purple-400 dark:hover:bg-purple-500/20 transition-colors" className="px-4 py-2 text-sm font-medium text-purple-600 bg-purple-50 rounded-lg hover:bg-purple-100 dark:bg-purple-500/10 dark:text-purple-400 dark:hover:bg-purple-500/20 transition-colors"
@@ -111,7 +118,8 @@ export default function UserDetailModal({
Đi vai trò Đi vai trò
</button> </button>
{user.is_deleted ? ( {!isMod && (
user.is_deleted ? (
<button <button
onClick={() => onRestore(user)} onClick={() => onRestore(user)}
className="px-4 py-2 text-sm font-medium text-green-600 bg-green-50 rounded-lg hover:bg-green-100 dark:bg-green-500/10 dark:text-green-400 dark:hover:bg-green-500/20 transition-colors" className="px-4 py-2 text-sm font-medium text-green-600 bg-green-50 rounded-lg hover:bg-green-100 dark:bg-green-500/10 dark:text-green-400 dark:hover:bg-green-500/20 transition-colors"
@@ -125,6 +133,7 @@ export default function UserDetailModal({
> >
Khóa / Xóa Khóa / Xóa
</button> </button>
)
)} )}
</div> </div>
</div> </div>