mirror of
https://github.com/ahmadk953/tasko.git
synced 2025-01-31 00:53:37 +00:00
Added Pagination to Audit Log Page
This commit is contained in:
parent
10c54df886
commit
c9313ff5f0
4 changed files with 198 additions and 44 deletions
|
@ -19,7 +19,7 @@ Currently, there is no documentation or wiki available but there will be one add
|
|||
This will be published on the site some time soon but for now, the roadmap will be listed here.
|
||||
|
||||
- [ ] Finish adding started at date feature
|
||||
- [ ] Pagination for Audit Logs page
|
||||
- [x] Pagination for Audit Logs page
|
||||
- [ ] Board sorting options (Boards Page)
|
||||
- [ ] Add real-time collaboration
|
||||
- [ ] Add end-to-end Database encryption (for customer data such as card titles and descriptions, and subscription information)
|
||||
|
|
|
@ -1,45 +1,144 @@
|
|||
import { auth } from '@clerk/nextjs/server';
|
||||
import { redirect } from 'next/navigation';
|
||||
'use client';
|
||||
|
||||
import { db } from '@/lib/db';
|
||||
import {
|
||||
Pagination,
|
||||
PaginationContent,
|
||||
PaginationEllipsis,
|
||||
PaginationItem,
|
||||
PaginationLink,
|
||||
PaginationNext,
|
||||
PaginationPrevious,
|
||||
} from '@/components/ui/pagination';
|
||||
import { ActivityItem } from '@/components/activity-item';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
|
||||
export const ActivityList = async () => {
|
||||
const { orgId } = await auth();
|
||||
|
||||
if (!orgId) redirect('/select-org');
|
||||
|
||||
const auditLogs = await db.auditLog.findMany({
|
||||
where: {
|
||||
orgId,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
cacheStrategy: { ttl: 30, swr: 60 },
|
||||
});
|
||||
import { AuditLog } from '@prisma/client';
|
||||
|
||||
export const ActivityList = ({
|
||||
totalPages,
|
||||
currentPage,
|
||||
auditLogs,
|
||||
}: {
|
||||
totalPages: number;
|
||||
currentPage: number;
|
||||
auditLogs: AuditLog[];
|
||||
}) => {
|
||||
return (
|
||||
<ol className='mt-4 space-y-4'>
|
||||
<p className='hidden text-center text-xs text-muted-foreground last:block'>
|
||||
No activity found inside this organization
|
||||
</p>
|
||||
{auditLogs.map((log) => (
|
||||
<ActivityItem key={log.id} data={log} />
|
||||
))}
|
||||
</ol>
|
||||
<div>
|
||||
<ol className='mt-4 space-y-4'>
|
||||
<p className='hidden text-center text-xs text-muted-foreground last:block'>
|
||||
No activity found inside this organization
|
||||
</p>
|
||||
{auditLogs.map((log) => (
|
||||
<ActivityItem key={log.id} data={log} />
|
||||
))}
|
||||
</ol>
|
||||
<ActivityListPagination
|
||||
totalPages={totalPages}
|
||||
currentPage={currentPage}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
ActivityList.Skeleton = function ActivityListSkeleton() {
|
||||
const ActivityListPagination = ({
|
||||
totalPages,
|
||||
currentPage,
|
||||
}: {
|
||||
totalPages: number;
|
||||
currentPage: number;
|
||||
}) => {
|
||||
const renderPageNumbers = () => {
|
||||
const pageNumbers = [];
|
||||
const maxVisiblePages = 5;
|
||||
|
||||
if (totalPages <= maxVisiblePages) {
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
pageNumbers.push(
|
||||
<PaginationItem key={i}>
|
||||
<PaginationLink
|
||||
href={`activity?page=${i}`}
|
||||
isActive={currentPage === i}
|
||||
>
|
||||
{i}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Always show first page
|
||||
pageNumbers.push(
|
||||
<PaginationItem key={1}>
|
||||
<PaginationLink href='activity?page=1' isActive={currentPage === 1}>
|
||||
1
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
);
|
||||
|
||||
// Show ellipsis if current page is more than 3
|
||||
if (currentPage > 3) {
|
||||
pageNumbers.push(
|
||||
<PaginationItem key='ellipsis-start'>
|
||||
<PaginationEllipsis />
|
||||
</PaginationItem>
|
||||
);
|
||||
}
|
||||
|
||||
// Show current page and surrounding pages
|
||||
const startPage = Math.max(2, currentPage - 1);
|
||||
const endPage = Math.min(totalPages - 1, currentPage + 1);
|
||||
|
||||
for (let i = startPage; i <= endPage; i++) {
|
||||
pageNumbers.push(
|
||||
<PaginationItem key={i}>
|
||||
<PaginationLink
|
||||
href={`activity?page=${i}`}
|
||||
isActive={currentPage === i}
|
||||
>
|
||||
{i}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
);
|
||||
}
|
||||
|
||||
// Show ellipsis if current page is less than totalPages - 2
|
||||
if (currentPage < totalPages - 2) {
|
||||
pageNumbers.push(
|
||||
<PaginationItem key='ellipsis-end'>
|
||||
<PaginationEllipsis />
|
||||
</PaginationItem>
|
||||
);
|
||||
}
|
||||
|
||||
// Always show last page
|
||||
pageNumbers.push(
|
||||
<PaginationItem key={totalPages}>
|
||||
<PaginationLink
|
||||
href={`activity?page=${totalPages}`}
|
||||
isActive={currentPage === totalPages}
|
||||
>
|
||||
{totalPages}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
);
|
||||
}
|
||||
|
||||
return pageNumbers;
|
||||
};
|
||||
|
||||
return (
|
||||
<ol className='mt-4 space-y-4'>
|
||||
<Skeleton className='h-14 w-[80%]' />
|
||||
<Skeleton className='h-14 w-[50%]' />
|
||||
<Skeleton className='h-14 w-[70%]' />
|
||||
<Skeleton className='h-14 w-[80%]' />
|
||||
<Skeleton className='h-14 w-[75%]' />
|
||||
</ol>
|
||||
<Pagination>
|
||||
<PaginationContent>
|
||||
{currentPage > 1 && (
|
||||
<PaginationItem>
|
||||
<PaginationPrevious href={`activity?page=${currentPage - 1}`} />
|
||||
</PaginationItem>
|
||||
)}
|
||||
{renderPageNumbers()}
|
||||
{currentPage < totalPages && (
|
||||
<PaginationItem>
|
||||
<PaginationNext href={`activity?page=${currentPage + 1}`} />
|
||||
</PaginationItem>
|
||||
)}
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,23 +1,77 @@
|
|||
import { auth } from '@clerk/nextjs/server';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { Suspense } from 'react';
|
||||
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
|
||||
import { Info } from '../_components/info';
|
||||
import { ActivityList } from './_components/activity-list';
|
||||
import { checkSubscription } from '@/lib/subscription';
|
||||
|
||||
const ActivityPage = async () => {
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { checkSubscription } from '@/lib/subscription';
|
||||
import { db } from '@/lib/db';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
|
||||
const ActivityPage = async ({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: { [key: string]: string | undefined };
|
||||
}) => {
|
||||
const isPro = await checkSubscription();
|
||||
const { orgId } = await auth();
|
||||
const params = await searchParams;
|
||||
const currentPage = Number(params.page);
|
||||
const limit = 10;
|
||||
|
||||
if (!orgId) redirect('/select-org');
|
||||
if (!currentPage || currentPage < 1) redirect('activity?page=1');
|
||||
|
||||
const skip = (currentPage - 1) * limit;
|
||||
|
||||
const auditLogs = await db.auditLog.findMany({
|
||||
where: {
|
||||
orgId,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
skip,
|
||||
take: limit,
|
||||
cacheStrategy: { ttl: 30, swr: 60 },
|
||||
});
|
||||
|
||||
const totalCount = await db.auditLog.count({
|
||||
where: {
|
||||
orgId,
|
||||
},
|
||||
cacheStrategy: { ttl: 30, swr: 60 },
|
||||
});
|
||||
|
||||
const totalPages = Math.ceil(totalCount / limit);
|
||||
|
||||
return (
|
||||
<div className='w-full'>
|
||||
<Info isPro={isPro} />
|
||||
<Separator className='my-2' />
|
||||
<Suspense fallback={<ActivityList.Skeleton />}>
|
||||
<ActivityList />
|
||||
<Suspense fallback={<ActivityListSkeleton />}>
|
||||
<ActivityList
|
||||
totalPages={totalPages}
|
||||
currentPage={currentPage}
|
||||
auditLogs={auditLogs}
|
||||
/>
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ActivityPage;
|
||||
|
||||
const ActivityListSkeleton = () => {
|
||||
return (
|
||||
<ol className='mt-4 space-y-4'>
|
||||
<Skeleton className='h-14 w-[80%]' />
|
||||
<Skeleton className='h-14 w-[50%]' />
|
||||
<Skeleton className='h-14 w-[70%]' />
|
||||
<Skeleton className='h-14 w-[80%]' />
|
||||
<Skeleton className='h-14 w-[75%]' />
|
||||
</ol>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import * as React from 'react';
|
||||
import { ChevronLeft, ChevronRight, MoreHorizontal } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
import { ButtonProps, buttonVariants } from '@/components/ui/button';
|
||||
|
@ -37,7 +38,7 @@ PaginationItem.displayName = 'PaginationItem';
|
|||
type PaginationLinkProps = {
|
||||
isActive?: boolean;
|
||||
} & Pick<ButtonProps, 'size'> &
|
||||
React.ComponentProps<'a'>;
|
||||
React.ComponentProps<typeof Link>;
|
||||
|
||||
const PaginationLink = ({
|
||||
className,
|
||||
|
@ -45,7 +46,7 @@ const PaginationLink = ({
|
|||
size = 'icon',
|
||||
...props
|
||||
}: PaginationLinkProps) => (
|
||||
<a
|
||||
<Link
|
||||
aria-current={isActive ? 'page' : undefined}
|
||||
className={cn(
|
||||
buttonVariants({
|
||||
|
|
Loading…
Reference in a new issue