Files
fast-next-template/frontend/src/components/admin/organizations/OrganizationListTable.tsx
Felipe Cardoso 96df7edf88 Refactor useAuth hook, settings components, and docs for formatting and readability improvements
- Consolidated multi-line arguments into single lines where appropriate in `useAuth`.
- Improved spacing and readability in data processing across components (`ProfileSettingsForm`, `PasswordChangeForm`, `SessionCard`).
- Applied consistent table and markdown formatting in design system docs (e.g., `README.md`, `08-ai-guidelines.md`, `00-quick-start.md`).
- Updated code snippets to ensure adherence to Prettier rules and streamlined JSX structures.
2025-11-10 11:03:45 +01:00

187 lines
6.5 KiB
TypeScript

/**
* OrganizationListTable Component
* Displays paginated list of organizations with basic info and actions
*/
'use client';
import { format } from 'date-fns';
import { Users } from 'lucide-react';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import { Badge } from '@/components/ui/badge';
import { Skeleton } from '@/components/ui/skeleton';
import { Button } from '@/components/ui/button';
import { OrganizationActionMenu } from './OrganizationActionMenu';
import type { Organization, PaginationMeta } from '@/lib/api/hooks/useAdmin';
interface OrganizationListTableProps {
organizations: Organization[];
pagination: PaginationMeta;
isLoading: boolean;
onPageChange: (page: number) => void;
onEditOrganization?: (organization: Organization) => void;
onViewMembers?: (organizationId: string) => void;
}
export function OrganizationListTable({
organizations,
pagination,
isLoading,
onPageChange,
onEditOrganization,
onViewMembers,
}: OrganizationListTableProps) {
return (
<div className="space-y-4">
{/* Table */}
<div className="rounded-md border">
<Table>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Description</TableHead>
<TableHead className="text-center">Members</TableHead>
<TableHead className="text-center">Status</TableHead>
<TableHead>Created</TableHead>
<TableHead className="w-[70px]">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{isLoading ? (
// Loading skeleton
Array.from({ length: 5 }).map((_, i) => (
<TableRow key={i}>
<TableCell>
<Skeleton className="h-4 w-[150px]" />
</TableCell>
<TableCell>
<Skeleton className="h-4 w-[250px]" />
</TableCell>
<TableCell>
<Skeleton className="h-4 w-[40px] mx-auto" />
</TableCell>
<TableCell>
<Skeleton className="h-5 w-[60px] mx-auto" />
</TableCell>
<TableCell>
<Skeleton className="h-4 w-[100px]" />
</TableCell>
<TableCell>
<Skeleton className="h-8 w-8" />
</TableCell>
</TableRow>
))
) : organizations.length === 0 ? (
// Empty state
<TableRow>
<TableCell colSpan={6} className="h-24 text-center">
No organizations found.
</TableCell>
</TableRow>
) : (
// Organization rows
organizations.map((org) => {
return (
<TableRow key={org.id}>
<TableCell className="font-medium">{org.name}</TableCell>
<TableCell className="max-w-md truncate">
{org.description || (
<span className="text-muted-foreground italic">No description</span>
)}
</TableCell>
<TableCell className="text-center">
<button
onClick={() => onViewMembers?.(org.id)}
className="inline-flex items-center gap-1 hover:text-primary"
>
<Users className="h-4 w-4" />
<span>{org.member_count}</span>
</button>
</TableCell>
<TableCell className="text-center">
<Badge variant={org.is_active ? 'default' : 'secondary'}>
{org.is_active ? 'Active' : 'Inactive'}
</Badge>
</TableCell>
<TableCell>{format(new Date(org.created_at), 'MMM d, yyyy')}</TableCell>
<TableCell>
<OrganizationActionMenu
organization={org}
onEdit={onEditOrganization}
onViewMembers={onViewMembers}
/>
</TableCell>
</TableRow>
);
})
)}
</TableBody>
</Table>
</div>
{/* Pagination */}
{!isLoading && organizations.length > 0 && (
<div className="flex items-center justify-between">
<div className="text-sm text-muted-foreground">
Showing {(pagination.page - 1) * pagination.page_size + 1} to{' '}
{Math.min(pagination.page * pagination.page_size, pagination.total)} of{' '}
{pagination.total} organizations
</div>
<div className="flex gap-2">
<Button
variant="outline"
size="sm"
onClick={() => onPageChange(pagination.page - 1)}
disabled={!pagination.has_prev}
>
Previous
</Button>
<div className="flex items-center gap-1">
{Array.from({ length: pagination.total_pages }, (_, i) => i + 1)
.filter(
(page) =>
page === 1 ||
page === pagination.total_pages ||
Math.abs(page - pagination.page) <= 1
)
.map((page, idx, arr) => {
const prevPage = arr[idx - 1];
const showEllipsis = prevPage && page - prevPage > 1;
return (
<div key={page} className="flex items-center">
{showEllipsis && <span className="px-2 text-muted-foreground">...</span>}
<Button
variant={page === pagination.page ? 'default' : 'outline'}
size="sm"
onClick={() => onPageChange(page)}
className="w-9"
>
{page}
</Button>
</div>
);
})}
</div>
<Button
variant="outline"
size="sm"
onClick={() => onPageChange(pagination.page + 1)}
disabled={!pagination.has_next}
>
Next
</Button>
</div>
</div>
)}
</div>
);
}