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

Compare commits

..

10 Commits

Author SHA1 Message Date
b35066835b Add sort for user table and refactor permission page 2025-07-07 16:08:06 +07:00
bd70451320 Add sample data 2025-07-07 10:49:01 +07:00
1865cf4efd refactor source code 2025-07-07 10:39:54 +07:00
ae1a45fe57 Add user page 2025-07-06 21:28:45 +07:00
76ca36ca1b Add mail template layout 2025-07-06 16:27:12 +07:00
815de2932b Add custom page 2025-07-06 16:24:34 +07:00
e8f76f395a Add logon page 2025-07-06 10:18:19 +07:00
99607fb75b add lowdb 2025-07-06 10:18:10 +07:00
1b00f472a2 Add shadcn ui component 2025-07-06 09:59:21 +07:00
ddc122c0a5 Init shadcn ui 2025-07-06 09:43:38 +07:00
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": [