style: 統一所有表格標題樣式為一般粗細並修正排序功能
This commit is contained in:
@@ -13,7 +13,7 @@ import {
|
||||
import { Badge } from "@/Components/ui/badge";
|
||||
import Pagination from '@/Components/shared/Pagination';
|
||||
import { SearchableSelect } from "@/Components/ui/searchable-select";
|
||||
import { FileText, Eye } from 'lucide-react';
|
||||
import { FileText, Eye, ArrowUpDown, ArrowUp, ArrowDown } from 'lucide-react';
|
||||
import { format } from 'date-fns';
|
||||
import { Button } from '@/Components/ui/button';
|
||||
import ActivityDetailDialog from './ActivityDetailDialog';
|
||||
@@ -45,6 +45,8 @@ interface Props extends PageProps {
|
||||
};
|
||||
filters: {
|
||||
per_page?: string;
|
||||
sort_by?: string;
|
||||
sort_order?: 'asc' | 'desc';
|
||||
};
|
||||
}
|
||||
|
||||
@@ -82,11 +84,41 @@ export default function ActivityLogIndex({ activities, filters }: Props) {
|
||||
setPerPage(value);
|
||||
router.get(
|
||||
route('activity-logs.index'),
|
||||
{ per_page: value },
|
||||
{ ...filters, per_page: value },
|
||||
{ preserveState: false, replace: true, preserveScroll: true }
|
||||
);
|
||||
};
|
||||
|
||||
const handleSort = (field: string) => {
|
||||
let newSortBy: string | undefined = field;
|
||||
let newSortOrder: 'asc' | 'desc' | undefined = 'asc';
|
||||
|
||||
if (filters.sort_by === field) {
|
||||
if (filters.sort_order === 'asc') {
|
||||
newSortOrder = 'desc';
|
||||
} else {
|
||||
newSortBy = undefined;
|
||||
newSortOrder = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
router.get(
|
||||
route('activity-logs.index'),
|
||||
{ ...filters, sort_by: newSortBy, sort_order: newSortOrder },
|
||||
{ preserveState: true, replace: true }
|
||||
);
|
||||
};
|
||||
|
||||
const SortIcon = ({ field }: { field: string }) => {
|
||||
if (filters.sort_by !== field) {
|
||||
return <ArrowUpDown className="h-4 w-4 text-muted-foreground ml-1" />;
|
||||
}
|
||||
if (filters.sort_order === "asc") {
|
||||
return <ArrowUp className="h-4 w-4 text-primary-main ml-1" />;
|
||||
}
|
||||
return <ArrowDown className="h-4 w-4 text-primary-main ml-1" />;
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthenticatedLayout
|
||||
breadcrumbs={[
|
||||
@@ -113,7 +145,15 @@ export default function ActivityLogIndex({ activities, filters }: Props) {
|
||||
<Table>
|
||||
<TableHeader className="bg-gray-50">
|
||||
<TableRow>
|
||||
<TableHead className="w-[180px]">時間</TableHead>
|
||||
<TableHead className="w-[50px] text-center">#</TableHead>
|
||||
<TableHead className="w-[180px]">
|
||||
<button
|
||||
onClick={() => handleSort('created_at')}
|
||||
className="flex items-center gap-1 hover:text-gray-900 transition-colors"
|
||||
>
|
||||
時間 <SortIcon field="created_at" />
|
||||
</button>
|
||||
</TableHead>
|
||||
<TableHead className="w-[150px]">操作人員</TableHead>
|
||||
<TableHead className="w-[100px] text-center">動作</TableHead>
|
||||
<TableHead className="w-[150px]">對象</TableHead>
|
||||
@@ -123,8 +163,11 @@ export default function ActivityLogIndex({ activities, filters }: Props) {
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{activities.data.length > 0 ? (
|
||||
activities.data.map((activity) => (
|
||||
activities.data.map((activity, index) => (
|
||||
<TableRow key={activity.id}>
|
||||
<TableCell className="text-gray-500 font-medium text-center">
|
||||
{activities.from + index}
|
||||
</TableCell>
|
||||
<TableCell className="text-gray-500 font-medium whitespace-nowrap">
|
||||
{activity.created_at}
|
||||
</TableCell>
|
||||
@@ -154,7 +197,8 @@ export default function ActivityLogIndex({ activities, filters }: Props) {
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handleViewDetail(activity)}
|
||||
className="button-outlined-info"
|
||||
className="button-outlined-primary"
|
||||
title="檢視詳情"
|
||||
>
|
||||
<Eye className="h-4 w-4" />
|
||||
</Button>
|
||||
@@ -163,7 +207,7 @@ export default function ActivityLogIndex({ activities, filters }: Props) {
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={5} className="text-center py-8 text-gray-500">
|
||||
<TableCell colSpan={7} className="text-center py-8 text-gray-500">
|
||||
尚無操作紀錄
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
|
||||
import { Head, Link, router } from '@inertiajs/react';
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Shield, Plus, Pencil, Trash2, Users } from 'lucide-react';
|
||||
import { Shield, Plus, Pencil, Trash2, Users, ArrowUpDown, ArrowUp, ArrowDown } from 'lucide-react';
|
||||
import { Button } from '@/Components/ui/button';
|
||||
import { Badge } from '@/Components/ui/badge';
|
||||
import {
|
||||
@@ -42,9 +42,13 @@ interface Role {
|
||||
|
||||
interface Props {
|
||||
roles: Role[];
|
||||
filters: {
|
||||
sort_by?: string;
|
||||
sort_order?: 'asc' | 'desc';
|
||||
};
|
||||
}
|
||||
|
||||
export default function RoleIndex({ roles }: Props) {
|
||||
export default function RoleIndex({ roles, filters = {} }: Props) {
|
||||
const [selectedRole, setSelectedRole] = useState<Role | null>(null);
|
||||
|
||||
const handleDelete = (id: number, name: string) => {
|
||||
@@ -55,6 +59,36 @@ export default function RoleIndex({ roles }: Props) {
|
||||
}
|
||||
};
|
||||
|
||||
const handleSort = (field: string) => {
|
||||
let newSortBy: string | undefined = field;
|
||||
let newSortOrder: 'asc' | 'desc' | undefined = 'asc';
|
||||
|
||||
if (filters.sort_by === field) {
|
||||
if (filters.sort_order === 'asc') {
|
||||
newSortOrder = 'desc';
|
||||
} else {
|
||||
newSortBy = undefined;
|
||||
newSortOrder = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
router.get(
|
||||
route('roles.index'),
|
||||
{ ...filters, sort_by: newSortBy, sort_order: newSortOrder },
|
||||
{ preserveState: true, replace: true }
|
||||
);
|
||||
};
|
||||
|
||||
const SortIcon = ({ field }: { field: string }) => {
|
||||
if (filters.sort_by !== field) {
|
||||
return <ArrowUpDown className="h-4 w-4 text-muted-foreground ml-1" />;
|
||||
}
|
||||
if (filters.sort_order === "asc") {
|
||||
return <ArrowUp className="h-4 w-4 text-primary-main ml-1" />;
|
||||
}
|
||||
return <ArrowDown className="h-4 w-4 text-primary-main ml-1" />;
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthenticatedLayout
|
||||
breadcrumbs={[
|
||||
@@ -92,9 +126,30 @@ export default function RoleIndex({ roles }: Props) {
|
||||
<TableHead className="w-[50px] text-center">#</TableHead>
|
||||
<TableHead>名稱</TableHead>
|
||||
<TableHead>代號</TableHead>
|
||||
<TableHead className="text-center">權限數量</TableHead>
|
||||
<TableHead className="text-center">使用者人數</TableHead>
|
||||
<TableHead className="text-left">建立時間</TableHead>
|
||||
<TableHead className="text-center">
|
||||
<button
|
||||
onClick={() => handleSort('permissions_count')}
|
||||
className="flex items-center justify-center gap-1 w-full hover:text-gray-900 transition-colors"
|
||||
>
|
||||
權限數量 <SortIcon field="permissions_count" />
|
||||
</button>
|
||||
</TableHead>
|
||||
<TableHead className="text-center">
|
||||
<button
|
||||
onClick={() => handleSort('users_count')}
|
||||
className="flex items-center justify-center gap-1 w-full hover:text-gray-900 transition-colors"
|
||||
>
|
||||
使用者人數 <SortIcon field="users_count" />
|
||||
</button>
|
||||
</TableHead>
|
||||
<TableHead className="text-left">
|
||||
<button
|
||||
onClick={() => handleSort('created_at')}
|
||||
className="flex items-center gap-1 hover:text-gray-900 transition-colors"
|
||||
>
|
||||
建立時間 <SortIcon field="created_at" />
|
||||
</button>
|
||||
</TableHead>
|
||||
<TableHead className="text-center">操作</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState } from 'react';
|
||||
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
|
||||
import { Head, Link, router } from '@inertiajs/react';
|
||||
import { Users, Plus, Pencil, Trash2, Mail, Shield } from 'lucide-react';
|
||||
import { Users, Plus, Pencil, Trash2, Mail, Shield, ArrowUpDown, ArrowUp, ArrowDown } from 'lucide-react';
|
||||
import { Button } from '@/Components/ui/button';
|
||||
import {
|
||||
Table,
|
||||
@@ -47,6 +47,8 @@ interface Props {
|
||||
};
|
||||
filters: {
|
||||
per_page?: string;
|
||||
sort_by?: string;
|
||||
sort_order?: 'asc' | 'desc';
|
||||
};
|
||||
}
|
||||
|
||||
@@ -66,11 +68,41 @@ export default function UserIndex({ users, filters }: Props) {
|
||||
setPerPage(value);
|
||||
router.get(
|
||||
route('users.index'),
|
||||
{ per_page: value },
|
||||
{ ...filters, per_page: value },
|
||||
{ preserveState: false, replace: true, preserveScroll: true }
|
||||
);
|
||||
};
|
||||
|
||||
const handleSort = (field: string) => {
|
||||
let newSortBy: string | undefined = field;
|
||||
let newSortOrder: 'asc' | 'desc' | undefined = 'asc';
|
||||
|
||||
if (filters.sort_by === field) {
|
||||
if (filters.sort_order === 'asc') {
|
||||
newSortOrder = 'desc';
|
||||
} else {
|
||||
newSortBy = undefined;
|
||||
newSortOrder = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
router.get(
|
||||
route('users.index'),
|
||||
{ ...filters, sort_by: newSortBy, sort_order: newSortOrder },
|
||||
{ preserveState: true, replace: true }
|
||||
);
|
||||
};
|
||||
|
||||
const SortIcon = ({ field }: { field: string }) => {
|
||||
if (filters.sort_by !== field) {
|
||||
return <ArrowUpDown className="h-4 w-4 text-muted-foreground ml-1" />;
|
||||
}
|
||||
if (filters.sort_order === "asc") {
|
||||
return <ArrowUp className="h-4 w-4 text-primary-main ml-1" />;
|
||||
}
|
||||
return <ArrowDown className="h-4 w-4 text-primary-main ml-1" />;
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
@@ -108,9 +140,23 @@ export default function UserIndex({ users, filters }: Props) {
|
||||
<TableHeader className="bg-gray-50">
|
||||
<TableRow>
|
||||
<TableHead className="w-[50px] text-center">#</TableHead>
|
||||
<TableHead className="w-[250px]">使用者</TableHead>
|
||||
<TableHead>
|
||||
<button
|
||||
onClick={() => handleSort('name')}
|
||||
className="flex items-center gap-1 hover:text-gray-900 transition-colors"
|
||||
>
|
||||
使用者 <SortIcon field="name" />
|
||||
</button>
|
||||
</TableHead>
|
||||
<TableHead>角色</TableHead>
|
||||
<TableHead className="w-[200px]">加入時間</TableHead>
|
||||
<TableHead>
|
||||
<button
|
||||
onClick={() => handleSort('created_at')}
|
||||
className="flex items-center gap-1 hover:text-gray-900 transition-colors"
|
||||
>
|
||||
加入時間 <SortIcon field="created_at" />
|
||||
</button>
|
||||
</TableHead>
|
||||
<TableHead className="text-center">操作</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
|
||||
Reference in New Issue
Block a user