1
0
Code Issues Pull Requests Actions Packages Projects Releases Wiki Activity Security Code Quality

Add sort for user table and refactor permission page

This commit is contained in:
2025-07-07 16:08:06 +07:00
parent bd70451320
commit b35066835b
8 changed files with 96 additions and 46 deletions

View File

@@ -5,10 +5,11 @@ import { join } from "path";
export async function GET(
request: Request,
{ params }: { params: { id: string } }
{ params }: { params: Promise<{ id: string }> }
) {
try {
const userId = parseInt(params.id);
const { id } = await params;
const userId = parseInt(id);
if (isNaN(userId)) {
return NextResponse.json(
@@ -45,10 +46,11 @@ export async function GET(
export async function PUT(
request: Request,
{ params }: { params: { id: string } }
{ params }: { params: Promise<{ id: string }> }
) {
try {
const userId = parseInt(params.id);
const { id } = await params;
const userId = parseInt(id);
if (isNaN(userId)) {
return NextResponse.json(
@@ -129,10 +131,11 @@ export async function PUT(
export async function DELETE(
request: Request,
{ params }: { params: { id: string } }
{ params }: { params: Promise<{ id: string }> }
) {
try {
const userId = parseInt(params.id);
const { id } = await params;
const userId = parseInt(id);
if (isNaN(userId)) {
return NextResponse.json(

View File

@@ -39,7 +39,7 @@ export default function UserEditPage() {
try {
setIsLoading(true);
const response = await axios.get(`/api/user/${userId}`);
const userData = response.data;
const userData = response.data.data; // Access the nested data property
form.reset({
username: userData.username,

View File

@@ -40,7 +40,6 @@ export default function UserPermissionPage() {
const [user, setUser] = useState<User | null>(null);
const [permissions, setPermissions] = useState<Permission[]>([]);
const [userPermissions, setUserPermissions] = useState<UserPermission[]>([]);
const [selectedPermissions, setSelectedPermissions] = useState<Set<number>>(new Set());
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
@@ -55,7 +54,7 @@ export default function UserPermissionPage() {
if (!userResponse.ok) {
throw new Error("Failed to fetch user data");
}
const userData = await userResponse.json();
const userData: User = await userResponse.json();
setUser(userData);
// Fetch all permissions
@@ -63,7 +62,7 @@ export default function UserPermissionPage() {
if (!permissionsResponse.ok) {
throw new Error("Failed to fetch permissions");
}
const permissionsData = await permissionsResponse.json();
const permissionsData: Permission[] = await permissionsResponse.json();
setPermissions(permissionsData);
// Fetch user permissions
@@ -71,13 +70,12 @@ export default function UserPermissionPage() {
if (!userPermissionsResponse.ok) {
throw new Error("Failed to fetch user permissions");
}
const userPermissionsData = await userPermissionsResponse.json();
setUserPermissions(userPermissionsData);
const userPermissionsData: UserPermission[] = await userPermissionsResponse.json();
// Set selected permissions
const selectedIds: Set<number> = new Set<number>(userPermissionsData.map((up: UserPermission) => up.permissionId as number));
setSelectedPermissions(selectedIds);
} catch (error) {
} catch (error: unknown) {
console.error("Error fetching data:", error);
toast.error("Failed to load user permissions");
} finally {
@@ -104,14 +102,12 @@ export default function UserPermissionPage() {
const axios = (await import("axios")).default;
const response = await axios.put(`/api/user/${userId}/permissions`, {
await axios.put<UserPermission[]>(`/api/user/${userId}/permissions`, {
permissionIds: Array.from(selectedPermissions),
});
const updatedPermissions = response.data;
setUserPermissions(updatedPermissions);
toast.success("User permissions updated successfully!");
} catch (error: any) {
} catch (error: unknown) {
console.error("Error updating permissions:", error);
toast.error("Failed to update permissions");
} finally {

View File

@@ -10,6 +10,7 @@ import axios from "axios";
import { ServerDataTable } from "@/components/ui/server-data-table";
import { User, createUserColumns } from "@/components/users/user-columns";
import { Header } from "@/components/common/header";
import { SortingState } from "@tanstack/react-table";
interface PaginationInfo {
page: number;
@@ -49,7 +50,7 @@ export default function UserPage() {
});
// Initialize search and sorting state to match query
const [searchValue, setSearchValue] = useState(query.search);
const [sorting, setSorting] = useState<{ id: string; desc: boolean }[]>(
const [sorting, setSorting] = useState<SortingState>(
query.sortBy ? [{ id: query.sortBy, desc: query.sortOrder === "desc" }] : []
);

View File

@@ -71,7 +71,7 @@ export const createCustomerColumns = ({
const customer = row.original;
return (
<div
className="font-medium cursor-pointer hover:text-blue-600 hover:underline"
className="text-blue-600 hover:text-blue-800 hover:underline cursor-pointer font-medium"
onClick={() => onEdit(customer.id)}
>
{customer.firstNameEn} {customer.lastNameEn}

View File

@@ -5,6 +5,7 @@ import {
flexRender,
getCoreRowModel,
getSortedRowModel,
SortingState,
useReactTable,
} from "@tanstack/react-table";
import { useEffect, useRef, useCallback } from "react";
@@ -34,9 +35,9 @@ interface ServerDataTableProps<TData, TValue> {
onSearchChange: (search: string) => void;
onSortingChange: (sortBy: string, sortOrder: "asc" | "desc") => void;
searchValue: string;
sorting: { id: string; desc: boolean }[];
sorting: SortingState;
setSearchValue: (value: string) => void;
setSorting: (value: { id: string; desc: boolean }[]) => void;
setSorting: (value: SortingState) => void;
}
export function ServerDataTable<TData, TValue>({

View File

@@ -1,5 +1,6 @@
import { Button } from "@/components/ui/button";
import { Key } from "lucide-react";
import { Key, ArrowUpDown } from "lucide-react";
import { Column } from "@tanstack/react-table";
export interface User {
id: number;
@@ -19,11 +20,34 @@ export function createUserColumns({ onEdit, onDelete, onPermissions }: {
return [
{
accessorKey: "id",
header: "ID",
header: ({ column }: { column: Column<User, unknown> }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
className="h-8 p-0 font-medium"
>
ID
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
},
enableSorting: true,
},
{
accessorKey: "fullName",
header: "Name",
header: ({ column }: { column: Column<User, unknown> }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
className="h-8 p-0 font-medium"
>
Name
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
},
cell: (row: { row: { original: User } }) => (
<button
onClick={() => onEdit?.(row.row.original.id)}
@@ -32,14 +56,39 @@ export function createUserColumns({ onEdit, onDelete, onPermissions }: {
{`${row.row.original.firstName} ${row.row.original.lastName}`}
</button>
),
enableSorting: true,
},
{
accessorKey: "email",
header: "Email",
header: ({ column }: { column: Column<User, unknown> }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
className="h-8 p-0 font-medium"
>
Email
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
},
enableSorting: true,
},
{
accessorKey: "username",
header: "Username",
header: ({ column }: { column: Column<User, unknown> }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
className="h-8 p-0 font-medium"
>
Username
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
},
enableSorting: true,
},
{
id: "actions",

View File

@@ -223,26 +223,6 @@
"userId": 1,
"permissionId": 15
},
{
"id": 16,
"userId": 2,
"permissionId": 4
},
{
"id": 17,
"userId": 2,
"permissionId": 5
},
{
"id": 18,
"userId": 2,
"permissionId": 8
},
{
"id": 19,
"userId": 2,
"permissionId": 11
},
{
"id": 20,
"userId": 3,
@@ -437,6 +417,26 @@
"id": 58,
"userId": 11,
"permissionId": 12
},
{
"id": 59,
"userId": 2,
"permissionId": 4
},
{
"id": 60,
"userId": 2,
"permissionId": 5
},
{
"id": 61,
"userId": 2,
"permissionId": 8
},
{
"id": 62,
"userId": 2,
"permissionId": 11
}
],
"permissions": [